Skip to content

Commit 1b84ab7

Browse files
committed
Move branch immediate values to the instructions array
This also drop the immediates array as no longer needed.
1 parent d2db6ce commit 1b84ab7

File tree

6 files changed

+111
-215
lines changed

6 files changed

+111
-215
lines changed

lib/fizzy/execute.cpp

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ namespace fizzy
1818
{
1919
namespace
2020
{
21-
// code_offset + imm_offset + stack_height
22-
constexpr auto BranchImmediateSize = 3 * sizeof(uint32_t);
21+
// code_offset + stack_drop
22+
constexpr auto BranchImmediateSize = 2 * sizeof(uint32_t);
2323

2424
constexpr uint32_t F32AbsMask = 0x7fffffff;
2525
constexpr uint32_t F32SignMask = ~F32AbsMask;
@@ -453,15 +453,12 @@ __attribute__((no_sanitize("float-cast-overflow"))) inline constexpr float demot
453453
return static_cast<float>(value);
454454
}
455455

456-
void branch(const Code& code, OperandStack& stack, const uint8_t*& pc, const uint8_t*& immediates,
457-
uint32_t arity) noexcept
456+
void branch(const Code& code, OperandStack& stack, const uint8_t*& pc, uint32_t arity) noexcept
458457
{
459-
const auto code_offset = read<uint32_t>(immediates);
460-
const auto imm_offset = read<uint32_t>(immediates);
461-
const auto stack_drop = read<uint32_t>(immediates);
458+
const auto code_offset = read<uint32_t>(pc);
459+
const auto stack_drop = read<uint32_t>(pc);
462460

463461
pc = code.instructions.data() + code_offset;
464-
immediates = code.immediates.data() + imm_offset;
465462

466463
// When branch is taken, additional stack items must be dropped.
467464
assert(static_cast<int>(stack_drop) >= 0);
@@ -523,7 +520,6 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
523520
static_cast<size_t>(code.max_stack_height));
524521

525522
const uint8_t* pc = code.instructions.data();
526-
const uint8_t* immediates = code.immediates.data();
527523

528524
while (true)
529525
{
@@ -539,27 +535,20 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
539535
case Instr::if_:
540536
{
541537
if (stack.pop().as<uint32_t>() != 0)
542-
immediates += 2 * sizeof(uint32_t); // Skip the immediates for else instruction.
538+
pc += sizeof(uint32_t); // Skip the immediate for else instruction.
543539
else
544540
{
545-
const auto target_pc = read<uint32_t>(immediates);
546-
const auto target_imm = read<uint32_t>(immediates);
547-
541+
const auto target_pc = read<uint32_t>(pc);
548542
pc = code.instructions.data() + target_pc;
549-
immediates = code.immediates.data() + target_imm;
550543
}
551544
break;
552545
}
553546
case Instr::else_:
554547
{
555548
// We reach else only after executing if block ("then" part),
556549
// so we need to skip else block now.
557-
const auto target_pc = read<uint32_t>(immediates);
558-
const auto target_imm = read<uint32_t>(immediates);
559-
550+
const auto target_pc = read<uint32_t>(pc);
560551
pc = code.instructions.data() + target_pc;
561-
immediates = code.immediates.data() + target_imm;
562-
563552
break;
564553
}
565554
case Instr::end:
@@ -573,16 +562,16 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
573562
case Instr::br_if:
574563
case Instr::return_:
575564
{
576-
const auto arity = read<uint32_t>(immediates);
565+
const auto arity = read<uint32_t>(pc);
577566

578567
// Check condition for br_if.
579568
if (instruction == Instr::br_if && stack.pop().as<uint32_t>() == 0)
580569
{
581-
immediates += BranchImmediateSize;
570+
pc += BranchImmediateSize;
582571
break;
583572
}
584573

585-
branch(code, stack, pc, immediates, arity);
574+
branch(code, stack, pc, arity);
586575
break;
587576
}
588577
case Instr::br_table:
@@ -595,9 +584,9 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
595584
const auto label_idx_offset = br_table_idx < br_table_size ?
596585
br_table_idx * BranchImmediateSize :
597586
br_table_size * BranchImmediateSize;
598-
immediates += label_idx_offset;
587+
pc += label_idx_offset;
599588

600-
branch(code, stack, pc, immediates, arity);
589+
branch(code, stack, pc, arity);
601590
break;
602591
}
603592
case Instr::call:

lib/fizzy/parser_expr.cpp

Lines changed: 39 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ inline void store(uint8_t* dst, T value) noexcept
1919
__builtin_memcpy(dst, &value, sizeof(value));
2020
}
2121

22-
template <typename T>
23-
inline void push(bytes& b, T value)
24-
{
25-
uint8_t storage[sizeof(T)];
26-
store(storage, value);
27-
b.append(storage, sizeof(storage));
28-
}
29-
3022
template <typename T>
3123
inline void push(std::vector<uint8_t>& b, T value)
3224
{
@@ -48,9 +40,6 @@ struct ControlFrame
4840
/// The target instruction code offset.
4941
const size_t code_offset{0};
5042

51-
/// The immediates offset for block instructions.
52-
const size_t immediates_offset{0};
53-
5443
/// The frame stack height of the parent frame.
5544
const int parent_stack_height{0};
5645

@@ -62,11 +51,10 @@ struct ControlFrame
6251
std::vector<size_t> br_immediate_offsets{};
6352

6453
ControlFrame(Instr _instruction, std::optional<ValType> _type, int _parent_stack_height,
65-
size_t _code_offset = 0, size_t _immediates_offset = 0) noexcept
54+
size_t _code_offset = 0) noexcept
6655
: instruction{_instruction},
6756
type{_type},
6857
code_offset{_code_offset},
69-
immediates_offset{_immediates_offset},
7058
parent_stack_height{_parent_stack_height}
7159
{}
7260
};
@@ -210,16 +198,16 @@ inline void update_branch_stack(const ControlFrame& current_frame, const Control
210198
drop_operand(current_frame, operand_stack, from_valtype(*branch_frame_type));
211199
}
212200

213-
void push_branch_immediates(const ControlFrame& branch_frame, int stack_height, bytes& immediates)
201+
void push_branch_immediates(
202+
const ControlFrame& branch_frame, int stack_height, std::vector<uint8_t>& instructions)
214203
{
215204
// How many stack items to drop when taking the branch.
216205
const auto stack_drop = stack_height - branch_frame.parent_stack_height;
217206

218207
// Push frame start location as br immediates - these are final if frame is loop,
219208
// but for block/if/else these are just placeholders, to be filled at end instruction.
220-
push(immediates, static_cast<uint32_t>(branch_frame.code_offset));
221-
push(immediates, static_cast<uint32_t>(branch_frame.immediates_offset));
222-
push(immediates, static_cast<uint32_t>(stack_drop));
209+
push(instructions, static_cast<uint32_t>(branch_frame.code_offset));
210+
push(instructions, static_cast<uint32_t>(stack_drop));
223211
}
224212

225213
inline void mark_frame_unreachable(
@@ -473,7 +461,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
473461

474462
// Push label with immediates offset after arity.
475463
control_stack.emplace(Instr::block, block_type, static_cast<int>(operand_stack.size()),
476-
code.instructions.size(), code.immediates.size());
464+
code.instructions.size());
477465
break;
478466
}
479467

@@ -483,7 +471,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
483471
std::tie(loop_type, pos) = parse_blocktype(pos, end);
484472

485473
control_stack.emplace(Instr::loop, loop_type, static_cast<int>(operand_stack.size()),
486-
code.instructions.size(), code.immediates.size());
474+
code.instructions.size());
487475
break;
488476
}
489477

@@ -493,12 +481,12 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
493481
std::tie(if_type, pos) = parse_blocktype(pos, end);
494482

495483
control_stack.emplace(Instr::if_, if_type, static_cast<int>(operand_stack.size()),
496-
code.instructions.size(), code.immediates.size());
484+
code.instructions.size());
497485

498486
// Placeholders for immediate values, filled at the matching end or else instructions.
499-
push(code.immediates, uint32_t{0}); // Diff to the else instruction
500-
push(code.immediates, uint32_t{0}); // Diff for the immediates.
501-
break;
487+
code.instructions.push_back(opcode);
488+
push(code.instructions, uint32_t{0}); // Diff to the else instruction
489+
continue;
502490
}
503491

504492
case Instr::else_:
@@ -508,30 +496,28 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
508496

509497
update_result_stack(frame, operand_stack); // else is the end of if.
510498

511-
const auto if_imm_offset = frame.immediates_offset;
499+
const auto if_imm_offset = frame.code_offset + 1;
512500
const auto frame_type = frame.type;
513501
auto frame_br_immediate_offsets = std::move(frame.br_immediate_offsets);
514502

515503
control_stack.pop();
516504
control_stack.emplace(Instr::else_, frame_type, static_cast<int>(operand_stack.size()),
517-
code.instructions.size(), code.immediates.size());
505+
code.instructions.size());
518506
// br immediates from `then` branch will need to be filled at the end of `else`
519507
control_stack.top().br_immediate_offsets = std::move(frame_br_immediate_offsets);
520508

521-
// Placeholders for immediate values, filled at the matching end instructions.
522-
push(code.immediates, uint32_t{0}); // Diff to the end instruction.
523-
push(code.immediates, uint32_t{0}); // Diff for the immediates
509+
code.instructions.push_back(opcode);
510+
511+
// Placeholder for the immediate value, filled at the matching end instructions.
512+
push(code.instructions, uint32_t{0}); // Diff to the end instruction.
524513

525-
// Fill in if's immediates with offsets of first instruction in else block.
526-
const auto target_pc = static_cast<uint32_t>(code.instructions.size() + 1);
527-
const auto target_imm = static_cast<uint32_t>(code.immediates.size());
514+
// Fill in if's immediate with the offset of first instruction in else block.
515+
const auto target_pc = static_cast<uint32_t>(code.instructions.size());
528516

529-
// Set the imm values for else instruction.
530-
auto* if_imm = code.immediates.data() + if_imm_offset;
517+
// Set the imm values for if instruction.
518+
auto* if_imm = code.instructions.data() + if_imm_offset;
531519
store(if_imm, target_pc);
532-
if_imm += sizeof(target_pc);
533-
store(if_imm, target_imm);
534-
break;
520+
continue;
535521
}
536522

537523
case Instr::end:
@@ -549,26 +535,21 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
549535
const auto target_pc = control_stack.size() == 1 ?
550536
static_cast<uint32_t>(code.instructions.size()) :
551537
static_cast<uint32_t>(code.instructions.size() + 1);
552-
const auto target_imm = static_cast<uint32_t>(code.immediates.size());
553538

554539
if (frame.instruction == Instr::if_ || frame.instruction == Instr::else_)
555540
{
556541
// We're at the end instruction of the if block without else or at the end of
557-
// else block. Fill in if/else's immediates with offsets of first instruction
542+
// else block. Fill in if/else's immediate with the offset of first instruction
558543
// after if/else block.
559-
auto* if_imm = code.immediates.data() + frame.immediates_offset;
544+
auto* if_imm = code.instructions.data() + frame.code_offset + 1;
560545
store(if_imm, target_pc);
561-
if_imm += sizeof(target_pc);
562-
store(if_imm, target_imm);
563546
}
564547

565548
// Fill in immediates all br/br_table instructions jumping out of this block.
566549
for (const auto br_imm_offset : frame.br_immediate_offsets)
567550
{
568-
auto* br_imm = code.immediates.data() + br_imm_offset;
551+
auto* br_imm = code.instructions.data() + br_imm_offset;
569552
store(br_imm, static_cast<uint32_t>(target_pc));
570-
br_imm += sizeof(uint32_t);
571-
store(br_imm, static_cast<uint32_t>(target_imm));
572553
// stack drop and arity were already stored in br handler
573554
}
574555
}
@@ -596,13 +577,14 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
596577

597578
update_branch_stack(frame, branch_frame, operand_stack);
598579

599-
push(code.immediates, get_branch_arity(branch_frame));
580+
code.instructions.push_back(opcode);
581+
push(code.instructions, get_branch_arity(branch_frame));
600582

601583
// Remember this br immediates offset to fill it at end instruction.
602-
branch_frame.br_immediate_offsets.push_back(code.immediates.size());
584+
branch_frame.br_immediate_offsets.push_back(code.instructions.size());
603585

604586
push_branch_immediates(
605-
branch_frame, static_cast<int>(operand_stack.size()), code.immediates);
587+
branch_frame, static_cast<int>(operand_stack.size()), code.instructions);
606588

607589
if (instr == Instr::br)
608590
mark_frame_unreachable(frame, operand_stack);
@@ -615,7 +597,7 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
615597
push_operand(operand_stack, *branch_frame.type);
616598
}
617599

618-
break;
600+
continue;
619601
}
620602

621603
case Instr::br_table:
@@ -653,13 +635,13 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
653635
if (get_branch_frame_type(branch_frame) != default_branch_type)
654636
throw validation_error{"br_table labels have inconsistent types"};
655637

656-
branch_frame.br_immediate_offsets.push_back(code.immediates.size());
638+
branch_frame.br_immediate_offsets.push_back(code.instructions.size());
657639
push_branch_immediates(
658-
branch_frame, static_cast<int>(operand_stack.size()), code.immediates);
640+
branch_frame, static_cast<int>(operand_stack.size()), code.instructions);
659641
}
660-
default_branch_frame.br_immediate_offsets.push_back(code.immediates.size());
642+
default_branch_frame.br_immediate_offsets.push_back(code.instructions.size());
661643
push_branch_immediates(
662-
default_branch_frame, static_cast<int>(operand_stack.size()), code.immediates);
644+
default_branch_frame, static_cast<int>(operand_stack.size()), code.instructions);
663645

664646
mark_frame_unreachable(frame, operand_stack);
665647

@@ -676,15 +658,16 @@ parser_result<Code> parse_expr(const uint8_t* pos, const uint8_t* end, FuncIdx f
676658

677659
update_branch_stack(frame, branch_frame, operand_stack);
678660

679-
push(code.immediates, get_branch_arity(branch_frame));
661+
code.instructions.push_back(opcode);
662+
push(code.instructions, get_branch_arity(branch_frame));
680663

681-
branch_frame.br_immediate_offsets.push_back(code.immediates.size());
664+
branch_frame.br_immediate_offsets.push_back(code.instructions.size());
682665

683666
push_branch_immediates(
684-
branch_frame, static_cast<int>(operand_stack.size()), code.immediates);
667+
branch_frame, static_cast<int>(operand_stack.size()), code.instructions);
685668

686669
mark_frame_unreachable(frame, operand_stack);
687-
break;
670+
continue;
688671
}
689672

690673
case Instr::call:

lib/fizzy/types.hpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,6 @@ struct Code
363363
/// The instructions bytecode interleaved with decoded immediate values.
364364
/// https://webassembly.github.io/spec/core/binary/instructions.html
365365
std::vector<uint8_t> instructions;
366-
367-
// The decoded instructions' immediate values.
368-
// These are instruction-type dependent fixed size value in the order of instructions.
369-
bytes immediates;
370366
};
371367

372368
/// The reference to the `code` in the wasm binary.

test/unittests/execute_numeric_test.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ ExecutionResult execute_unary_operation(Instr instr, uint64_t arg)
2222
module->funcsec.emplace_back(TypeIdx{0});
2323
module->codesec.emplace_back(Code{1, 0,
2424
{static_cast<uint8_t>(Instr::local_get), 0, 0, 0, 0, static_cast<uint8_t>(instr),
25-
static_cast<uint8_t>(Instr::end)},
26-
{}});
25+
static_cast<uint8_t>(Instr::end)}});
2726

2827
return execute(*instantiate(std::move(module)), 0, {arg});
2928
}
@@ -36,8 +35,7 @@ ExecutionResult execute_binary_operation(Instr instr, uint64_t lhs, uint64_t rhs
3635
module->funcsec.emplace_back(TypeIdx{0});
3736
module->codesec.emplace_back(Code{2, 0,
3837
{static_cast<uint8_t>(Instr::local_get), 0, 0, 0, 0, static_cast<uint8_t>(Instr::local_get),
39-
1, 0, 0, 0, static_cast<uint8_t>(instr), static_cast<uint8_t>(Instr::end)},
40-
{}});
38+
1, 0, 0, 0, static_cast<uint8_t>(instr), static_cast<uint8_t>(Instr::end)}});
4139

4240
return execute(*instantiate(std::move(module)), 0, {lhs, rhs});
4341
}

0 commit comments

Comments
 (0)