-
Notifications
You must be signed in to change notification settings - Fork 39
Drop immediates array #618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
80c9f1a to
c91d9d9
Compare
Codecov Report
@@ Coverage Diff @@
## master #618 +/- ##
==========================================
- Coverage 98.37% 98.36% -0.01%
==========================================
Files 69 69
Lines 9654 9622 -32
==========================================
- Hits 9497 9465 -32
Misses 157 157 |
b162348 to
656e46d
Compare
923bd69 to
ad8b7d3
Compare
|
|
||
| push(code.immediates, local_idx); | ||
| break; | ||
| code.instructions.push_back(opcode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use push_back for these and not push(code.instructions, opcode)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the copy of line after the switch.
But I can reverse the question: Why use push(code.instructions, opcode) for these when code.instructions.push_back(opcode) is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe push should be renamed to push_immediate, and optionally a new helper push_opcode added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe push should be renamed to push_immediate, and optionally a new helper push_opcode added
That would be nice for readability, in a separate PR.
| break; | ||
| code.instructions.push_back(opcode); | ||
| push(code.instructions, uint32_t{0}); // Diff to the else instruction | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are many of these changed to continue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The continue skips the code.instructions.push_back(opcode); after the switch because you do it here before pushing the immediates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As opcode is always the first to be pushed, would it be better to move code.instructions.push_back(opcode); to before the switch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible currently, but I'm not sure this will stand long term. In #622 I present a way to skip noop instructions in parsing. Also we can replace return with br, etc. So I think the control what opcode is pushed if any on the instruction granularity is useful.
Alternatively, we can push the opcode, and the "pop" it or replace it in some cases.
| break; | ||
| code.instructions.push_back(opcode); | ||
| push(code.instructions, uint32_t{0}); // Diff to the else instruction | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The continue skips the code.instructions.push_back(opcode); after the switch because you do it here before pushing the immediates.
|
|
||
| push(code.immediates, local_idx); | ||
| break; | ||
| code.instructions.push_back(opcode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the copy of line after the switch.
But I can reverse the question: Why use push(code.instructions, opcode) for these when code.instructions.push_back(opcode) is enough.
test/unittests/parser_expr_test.cpp
Outdated
| ElementsAre(Instr::i32_const, 0, 0, 0, 0, Instr::loop, Instr::br, /*arity:*/ 0, 0, 0, 0, | ||
| /*code_offset:*/ 5, 0, 0, 0, /*stack_drop:*/ 0, 0, 0, 0, Instr::end, Instr::drop, | ||
| Instr::end)); | ||
| // EXPECT_EQ(module_parent_stack->codesec[0].immediates, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be removed.
ad8b7d3 to
d3f0e65
Compare
lib/fizzy/parser_expr.cpp
Outdated
| // br immediates from `then` branch will need to be filled at the end of `else` | ||
| control_stack.top().br_immediate_offsets = std::move(frame_br_immediate_offsets); | ||
|
|
||
| // Placeholders for immediate values, filled at the matching end instructions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this comment one line down, to before pushing the immediate
| // Placeholders for immediate values, filled at the matching end instructions. | |
| // Placeholder for immediate value, filled at the matching end instructions. |
|
|
||
| push(code.immediates, local_idx); | ||
| break; | ||
| code.instructions.push_back(opcode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe push should be renamed to push_immediate, and optionally a new helper push_opcode added
lib/fizzy/parser_expr.cpp
Outdated
| code.instructions.push_back(opcode); | ||
| push(code.instructions, uint32_t{0}); // Diff to the end instruction. | ||
|
|
||
| // Fill in if's immediates with offsets of first instruction in else block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Fill in if's immediates with offsets of first instruction in else block. | |
| // Fill in if's immediate with offset of first instruction in else block. |
lib/fizzy/parser_expr.cpp
Outdated
| if (frame.instruction == Instr::if_ || frame.instruction == Instr::else_) | ||
| { | ||
| // We're at the end instruction of the if block without else or at the end of | ||
| // else block. Fill in if/else's immediates with offsets of first instruction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // else block. Fill in if/else's immediates with offsets of first instruction | |
| // else block. Fill in if/else's immediate with offset of first instruction |
|
My classic benchmarks, GCC10 LTO on Haswell 4GHz. The parsing got slower. There can be two reasons for that: we allocate differently - single array, and the parser loop is more complex. |
f365586 to
d3f0e65
Compare
|
|
||
| // The instructions bytecode without immediate values. | ||
| // https://webassembly.github.io/spec/core/binary/instructions.html | ||
| std::vector<uint8_t> instructions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also tried using bytes, but the performance is the same. So I'm happy to leave vector as it is simpler internally (string has SSO which is useless in this case).
| const auto module = parse(bin); | ||
|
|
||
| for (const auto param : {0u, 1u}) | ||
| for (const auto param : {0u}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this still failing?
| "00000000"_bytes; // stack_drop | ||
|
|
||
| EXPECT_EQ(code.immediates.substr(br_table_imm_offset, expected_br_imm.size()), expected_br_imm); | ||
| Instr::local_get, 0, 0, 0, 0, Instr::br_table, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are super annoying, I'm not checking all the offsets
|
AMD EPYC 7601, GCC10, no LTO |
d3f0e65 to
963468b
Compare
ceac9f0 to
0f5ed9d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks ok to me.
0723283 to
9f8929a
Compare
|
Haswell 4 GHz, GCC10 LTO |
|
AMD EPYC 7501 2 GHz, GCC10 no-LTO |
| (block | ||
| (br_table 3 2 1 0 4 (get_local 0)) | ||
| (return (i32.const 99)) | ||
| (return (i32.const 0x41)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason these were renumbered? Just to make it easier to read below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I first converted them to hex to easier find them in the bytecode, but this seem not relevant any more.
| if (!module.has_memory()) | ||
| throw validation_error{"memory instructions require imported or defined memory"}; | ||
| break; | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this continue here jumping to the while body? I did not know you can jump out from within switches with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit hackish, but also "minimal" change. The continue only affects loops but break both loops and switches.
| uint32_t offset; | ||
| std::tie(offset, pos) = leb128u_decode<uint32_t>(pos, end); | ||
| push(code.immediates, offset); | ||
| code.instructions.push_back(opcode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, why is the old code (end of loop) using emplace_back(opcode) but the new commits are not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The std::string only has .push_back(). It will be easier to try this instead of std::vector.
| ASSERT_EQ(module->codesec[0].immediates.substr(4), "00000000"_bytes); // load offset. | ||
| auto* const load_instr = const_cast<uint8_t*>(&module->codesec[0].instructions[1]); | ||
| ASSERT_EQ(*load_instr, Instr::i32_load); | ||
| ASSERT_EQ(bytes_view(load_instr + 1, 4), "00000000"_bytes); // load offset. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this bytes_view alternative better because it is faster, or does it result in a nicer error display?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was easier for me to read it like that when modifying the test.
test/unittests/parser_expr_test.cpp
Outdated
| "00000000"_bytes; // stack_drop | ||
|
|
||
| EXPECT_EQ(code.immediates.substr(br_table_imm_offset, expected_br_imm.size()), expected_br_imm); | ||
| EXPECT_EQ(code.immediates.substr(0, expected_br_imm.size()), expected_br_imm); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this 0 now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah the old immediates table here skipped the local for some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test always takes the part of the immediates for the br_table only. Now this part starts at 0 because we removed other immediates. I didn't bother to keep the br_table_imm_offset name. In the end when all immediates are move to instructions we check all bytes anyway.
| const auto [code, pos] = parse_expr(code1_bin, 0, {}, module); | ||
| EXPECT_THAT(code.instructions, | ||
| ElementsAre(Instr::i32_const, 0, 0, 0, 0, Instr::call_indirect, Instr::end)); | ||
| ElementsAre(Instr::i32_const, 0, 0, 0, 0, Instr::call_indirect, 0, 0, 0, 0, Instr::end)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there really a single test affected by call immediates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like. All intermediate commits should pass all tests. Immediates for calls are rather simple and at that time we were writing more wat2wasm tests.
test/unittests/parser_test.cpp
Outdated
| ElementsAre(Instr::local_get, 0, 0, 0, 0, Instr::local_get, 1, 0, 0, 0, Instr::i32_add, | ||
| Instr::local_get, 2, 0, 0, 0, Instr::i32_add, Instr::local_tee, 2, 0, 0, 0, | ||
| Instr::local_get, 0, 0, 0, 0, Instr::i32_add, Instr::end)); | ||
| EXPECT_EQ(c.immediates.size(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it would have been nice to have the final "remove immediates from struct" as a separate commit to see the impact of changes.
9f8929a to
05023ef
Compare
Co-authored-by: Paweł Bylica <[email protected]>
This also drop the immediates array as no longer needed.
05023ef to
1bb954b
Compare
No description provided.