Rework br_table to use BlockCall#5731
Conversation
0b76e30 to
c5da90a
Compare
bjorn3
left a comment
There was a problem hiding this comment.
This is a nice simplification. I'm curious how much of a (negative or positive) perf impact this will have though.
c5da90a to
97a6f3a
Compare
Move the storage for jump tables off of FunctionStencil and onto DataFlowGraph. This change is in service of #5731, making it easier to access the jump table data in the context of helpers like inst_values.
97a6f3a to
32e54d6
Compare
| table, destination, .. | ||
| } => { | ||
| // In the case of a jump table, the situation is tricky because br_table doesn't | ||
| // support arguments. We have to split the critical edge. |
There was a problem hiding this comment.
This code may need to be preserved for invoke depending on how it will be implemented. The exception path gets some block arguments from the personality function, so either those would be added to the block arguments listed in BlockCall of the invoke instruction, or the invoke instruction would only list a Block and synthesize the full block argument list internally. What do you think would be cleaner?
There was a problem hiding this comment.
I like the idea of using a BlockCall on the invoke instruction. I think the path for doing that should be pretty well worn after adding brif, but if you run into any problems using BlockCall I'd be happy to help work through them :)
There was a problem hiding this comment.
The difference between brif and invoke is that brif passes all block arguments in the instruction to the destination block, but for invoke there would be some extra block arguments not specified in the BlockCall. Do you add those at the start or the end? And is there any code that expects there to be no block arguments added? The following is pseudocode to demonstrate the issue:
block0:
v1 = iconst.i64 42
invoke fn0(), block1, block2(v1)
block1:
trap user0
block2(v2: i64, v3: i64):
; this is the landingpad
; either v2 or v3 is the extra argument containing a value passed by the personality function. which one should be the extra argument?
trap user0
There was a problem hiding this comment.
Ah, interesting! I would add them to the end and still use a BlockCall. It's sort of like a partially applied function that way.
There was a problem hiding this comment.
Sure, will try that once I get to adding the invoke instruction.
8894db9 to
7d2d126
Compare
Subscribe to Label ActionDetailsThis issue or pull request has been labeled: "cranelift", "cranelift:area:aarch64", "cranelift:area:machinst", "cranelift:area:x64", "cranelift:meta", "cranelift:wasm", "isle"Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
4433b1f to
2ab75f3
Compare
051be27 to
36b5699
Compare
54ba215 to
9127cb1
Compare
97f706f to
7f362c8
Compare
7f362c8 to
05d217d
Compare
| if operand.is_immediate_or_entityref() { | ||
| if operand.kind.is_block() { | ||
| num_blocks += 1; | ||
| } else if operand.is_immediate_or_entityref() { |
There was a problem hiding this comment.
The changes in this file aren't necessary for this PR, but did show up as necessary when making changes to the old form of br_table.
There was a problem hiding this comment.
I think it's a good assertion to add regardless, so I'm glad you kept this in.
| let block = inst.branch_destination()[0]; | ||
| continue_at(block)? | ||
| if let InstructionData::Jump { destination, .. } = inst { | ||
| continue_at(destination)? | ||
| } else { | ||
| unreachable!() | ||
| } |
There was a problem hiding this comment.
This isn't necessary for the br_table changes, but it seemed nice to avoid the slice creation.
05d217d to
e3d084c
Compare
jameysharp
left a comment
There was a problem hiding this comment.
Looking good! I've been accumulating comments on this PR all afternoon in between other things and no longer have any idea what I've said, but hopefully it'll make sense.
| // so it is not part of the table. We visit the default block | ||
| // first explicitly, to mirror the traversal order of | ||
| // `JumpTableData::all_branches`, and transitively the order of |
There was a problem hiding this comment.
After reading this comment, I was left wondering why this whole function can't just be replaced with a loop over branch_destination. I forgot the boolean argument to the closure depends on these details. Could you add that detail to the comment, for the next time I forget how everything works?
Of course, I think all but one caller of this function ignore the boolean argument, and with this PR, all except that one could be replaced with a small loop over branch_destination. Maybe we should look at whether the call in cranelift/codegen/src/machinst/blockorder.rs really needs to know this distinction, and see if we can eliminate this function entirely now...
There was a problem hiding this comment.
It uses it to record those branch targets as indirect. We might be able to do something gross to recover that information some other way, but I feel like it's fine to duplicate the matching logic here. I'll add the additional comment you mentioned :)
| if operand.is_immediate_or_entityref() { | ||
| if operand.kind.is_block() { | ||
| num_blocks += 1; | ||
| } else if operand.is_immediate_or_entityref() { |
There was a problem hiding this comment.
I think it's a good assertion to add regardless, so I'm glad you kept this in.
| let mut jt = Vec::with_capacity(targets.len()); | ||
| for block in targets { | ||
| let args = self.generate_values_for_block(builder, block)?; | ||
| jt.push(builder.func.dfg.block_call(block, &args)) | ||
| } | ||
|
|
||
| let args = self.generate_values_for_block(builder, default)?; | ||
| let jt_data = JumpTableData::new(builder.func.dfg.block_call(default, &args), &jt); | ||
| let jt = builder.create_jump_table(jt_data); |
There was a problem hiding this comment.
This isn't sufficient to generate br_table with block params. We also need to update the match on BlockTerminatorKind::BrTable elsewhere to use generate_target_block instead of selecting from forward_blocks_without_params. Also that should be the last use of the latter as well as a variety of other machinery which can all be removed now. Hooray!
There was a problem hiding this comment.
Alongside what @jameysharp is saying above, we can now select Switch and BrTable more often, since they no longer require a special block without params.
Could you also update this:
wasmtime/cranelift/fuzzgen/src/function_generator.rs
Lines 1806 to 1815 in e10094d
BrTable and Switch alongside Jump and Br?
The only other places where we use forward_blocks_without_params, is when selecting blocks for Switch targets, and those can also now carry params, so I agree, lets remove it!
Edit: Oops! It looks like the switch interface still does not allow blocks with params! So we may have to keep forward_blocks_without_params 😕 .
That being said, we should still try to select BrTable as often as we can and not just on paramless blocks.
There was a problem hiding this comment.
I believe I've made the changes you both requested, could you take a quick look and make sure?
jameysharp
left a comment
There was a problem hiding this comment.
Looks good, thank you! I have one style suggestion and I guess CI is failing, but I think this is basically ready.
b697873 to
ec4f81d
Compare
ec4f81d to
a6f7a6e
Compare
This has been unused since bytecodealliance#5731
This has been unused since #5731
Rework
br_tableto useBlockCall, allowing us to avoid adding new nodes during ssa construction to hold block arguments. Additionally, many places where we previously matched onInstructionDatato extract branch destinations can be replaced with a use ofbranch_destinationorbranch_destination_mut.