This is an archive of the discontinued LLVM Phabricator instance.

[WebAssembly] multivalue stackify fix
ClosedPublic

Authored by samparker on Nov 11 2022, 3:25 AM.

Details

Summary

Suggested fix for bugs: https://github.com/llvm/llvm-project/issues/58297, https://github.com/llvm/llvm-project/issues/55136

Add a check that we're not attempting to move a multivalue instruction past any of it's users.

Diff Detail

Event Timeline

samparker created this revision.Nov 11 2022, 3:25 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 11 2022, 3:25 AM
samparker requested review of this revision.Nov 11 2022, 3:25 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 11 2022, 3:25 AM
Herald added a subscriber: aheejin. · View Herald Transcript
sbc100 added inline comments.Nov 11 2022, 7:24 AM
llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-bug.ll
4 ↗(On Diff #474710)

Did you mean to use the wasi target here? We normally use unknown-unknown for testing generic stuff

samparker added inline comments.Nov 11 2022, 7:36 AM
llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-bug.ll
4 ↗(On Diff #474710)

Okay, fair enough, I just copied it from the github issue... I'll make the change.

samparker updated this revision to Diff 474765.Nov 11 2022, 7:45 AM

Updated target triple.

tlively added inline comments.Nov 11 2022, 9:32 AM
llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
339–340

This doesn't sound quite right. As a counterexample, consider

a, b = foo
bar a
baz b

Here we have a subsequent def (of b) used by a different instruction (baz), but we can still stackify by moving baz before bar.

It would be good to reduce the test case to figure out more precisely what the problem is and come up with a more precise solution. Maybe the problem is that baz and bar (or rather their equivalents in the test) cannot be reordered?

I don't know if you've seen it, but we have a multivalue-stackify.py script that tries to exhaustively enumerate multivalue sequences up to a certain size then filter out the uninteresting ones. The output is checked in as the multivalue-stackify.ll test. It may be worth playing around with that script so that it generates programs that exhibit this problem.

samparker added inline comments.Nov 14 2022, 12:53 AM
llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
339–340

Maybe my comment doesn't make complete sense... But just to clarify, we move the def, not the user, right? And we only attempt to move considering the first def?

So I think your example is the pattern that the existing code will handle just fine: we won't attempt to move anything?

IIUC, the case that currently isn't handled is this:

a, b = foo
baz b
bar a

The existing code only looks at bar and will attempt to sink foo past baz, producing broken code. I'll convert to the test to a MIR which should demonstrate that this is precisely what's going on.

If I'm misunderstanding, please shout!

samparker updated this revision to Diff 475070.Nov 14 2022, 1:30 AM

Using mir test and updated comment.

I don't know if you've seen it, but we have a multivalue-stackify.py script that tries to exhaustively enumerate multivalue sequences up to a certain size then filter out the uninteresting ones.

Cool! My python isn't great tbh... but looking through the generated test, I would have thought that f141 may have triggered this bug:

%t0 = call {i32, i32} @op_0_to_2()
%t1 = extractvalue {i32, i32} %t0, 1
call void @op_1_to_0(i32 %t1)
%t2 = extractvalue {i32, i32} %t0, 0
call void @op_1_to_0(i32 %t2)
ret void
%0:i32, %1:i32 = CALL @op_0_to_2, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def dead $arguments, implicit $sp32, implicit $sp64
CALL @op_1_to_0, %1, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def dead $arguments, implicit $sp32, implicit $sp64
CALL @op_1_to_0, %0, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def dead $arguments, implicit $sp32, implicit $sp64
RETURN implicit-def dead $arguments

Is it the fact that the users are calls and this prevents a move from being attempted..?

I don't know if you've seen it, but we have a multivalue-stackify.py script that tries to exhaustively enumerate multivalue sequences up to a certain size then filter out the uninteresting ones.

Cool! My python isn't great tbh... but looking through the generated test, I would have thought that f141 may have triggered this bug:

%t0 = call {i32, i32} @op_0_to_2()
%t1 = extractvalue {i32, i32} %t0, 1
call void @op_1_to_0(i32 %t1)
%t2 = extractvalue {i32, i32} %t0, 0
call void @op_1_to_0(i32 %t2)
ret void
%0:i32, %1:i32 = CALL @op_0_to_2, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def dead $arguments, implicit $sp32, implicit $sp64
CALL @op_1_to_0, %1, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def dead $arguments, implicit $sp32, implicit $sp64
CALL @op_1_to_0, %0, implicit-def dead $arguments, implicit $sp32, implicit $sp64, implicit-def dead $arguments, implicit $sp32, implicit $sp64
RETURN implicit-def dead $arguments

Is it the fact that the users are calls and this prevents a move from being attempted..?

Yeah, I suspect you're right that the function calls are inhibiting the moves. It would be nice to find a way around that, but looking at the code it looks like it will refuse to reorder a pair of calls no matter what attributes the callee has. I suppose we could add a debug-only flag to change that behavior, but it's probably not worth it. Maybe the script could emit instructions other than function calls instead...

Anyway, mostly LGTM with the exception that it would be good to make the test even easier to read.

llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
339–340

Maybe my comment doesn't make complete sense... But just to clarify, we move the def, not the user, right? And we only attempt to move considering the first def?

Ah, you're right. I had forgotten that we're moving defs here.

llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir
10

Is it possible to come up with a reproducer with a single basic block? Either way, it would be helpful to add comments highlighting the important pieces of the test. There's enough going on here that it's difficult to figure out which parts are important just by looking at it.

samparker added inline comments.Nov 15 2022, 1:13 AM
llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir
10

Yeah, good point. I'll take the reproducer from here since trying to simplify fp128 lowering is too much work!

samparker updated this revision to Diff 475366.Nov 15 2022, 1:22 AM
samparker edited the summary of this revision. (Show Details)

Using smaller reproducer.

tlively accepted this revision.Nov 15 2022, 2:18 PM

LGTM, thanks!

This revision is now accepted and ready to land.Nov 15 2022, 2:18 PM
This revision was landed with ongoing or failed builds.Nov 16 2022, 1:02 AM
This revision was automatically updated to reflect the committed changes.