Index: lib/Target/WebAssembly/WebAssemblyRegStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -279,6 +279,7 @@ assert(Def->getParent() == Insert->getParent()); // Check for register dependencies. + std::vector MutableOperands; for (const MachineOperand &MO : Def->operands()) { if (!MO.isReg() || MO.isUndef()) continue; @@ -301,21 +302,11 @@ return false; } - // Ask LiveIntervals whether moving this virtual register use or def to - // Insert will change which value numbers are seen. - // - // If the operand is a use of a register that is also defined in the same - // instruction, test that the newly defined value reaches the insert point, - // since the operand will be moving along with the def. - const LiveInterval &LI = LIS.getInterval(Reg); - VNInfo *DefVNI = - (MO.isDef() || Def->definesRegister(Reg)) ? - LI.getVNInfoAt(LIS.getInstructionIndex(*Def).getRegSlot()) : - LI.getVNInfoBefore(LIS.getInstructionIndex(*Def)); - assert(DefVNI && "Instruction input missing value number"); - VNInfo *InsVNI = LI.getVNInfoBefore(LIS.getInstructionIndex(*Insert)); - if (InsVNI && DefVNI != InsVNI) - return false; + // If one of the operands isn't in SSA form, it has different values at + // different times, and we need to make sure we don't move our use across + // a different def. + if (!MO.isDef() && !MRI.getUniqueVRegDef(Reg)) + MutableOperands.push_back(&MO); } bool Read = false, Write = false, Effects = false, StackPointer = false; @@ -323,7 +314,8 @@ // If the instruction does not access memory and has no side effects, it has // no additional dependencies. - if (!Read && !Write && !Effects && !StackPointer) + bool HasMutableOperands = MutableOperands.size() > 0; + if (!Read && !Write && !Effects && !StackPointer && !HasMutableOperands) return true; // Scan through the intervening instructions between Def and Insert. @@ -343,6 +335,11 @@ return false; if (StackPointer && InterveningStackPointer) return false; + + for (const MachineOperand *MO : MutableOperands) + for (const MachineOperand &OpMO : I->operands()) + if (OpMO.isReg() && OpMO.isDef() && OpMO.getReg() == MO->getReg()) + return false; } return true; Index: test/CodeGen/WebAssembly/userstack.ll =================================================================== --- test/CodeGen/WebAssembly/userstack.ll +++ test/CodeGen/WebAssembly/userstack.ll @@ -232,6 +232,42 @@ ret void } +declare i8* @llvm.stacksave() +declare void @llvm.stackrestore(i8*) + +; CHECK-LABEL: llvm_stack_builtins: +define void @llvm_stack_builtins(i32 %alloc) noredzone { + ; CHECK: i32.load $push[[L11:.+]]=, __stack_pointer($pop{{.+}}) + ; CHECK-NEXT: tee_local $push[[L10:.+]]=, ${{.+}}=, $pop[[L11]] + ; CHECK-NEXT: copy_local $[[STACK:.+]]=, $pop[[L10]] + %stack = call i8* @llvm.stacksave() + + ; Ensure we don't reassign the stacksave local + ; CHECK-NOT: $[[STACK]]= + %dynamic = alloca i32, i32 %alloc + + ; CHECK: i32.store $drop=, __stack_pointer($pop{{.+}}), $[[STACK]] + call void @llvm.stackrestore(i8* %stack) + + ret void +} + +; Not actually using the alloca'd variables exposed an issue with register +; stackification, where copying the stack pointer into the frame pointer was +; moved after the stack pointer was updated for the dynamic alloca. +; CHECK-LABEL: dynamic_alloca_nouse: +define void @dynamic_alloca_nouse(i32 %alloc) noredzone { + ; CHECK: i32.load $push[[L11:.+]]=, __stack_pointer($pop{{.+}}) + ; CHECK-NEXT: tee_local $push[[L10:.+]]=, ${{.+}}=, $pop[[L11]] + ; CHECK-NEXT: copy_local $[[FP:.+]]=, $pop[[L10]] + %dynamic = alloca i32, i32 %alloc + + ; CHECK-NOT: $[[FP]]=, + + ; CHECK: i32.store $drop=, __stack_pointer($pop{{.+}}), $[[FP]] + ret void +} + ; The use of the alloca in a phi causes a CopyToReg DAG node to be generated, ; which has to have special handling because CopyToReg can't have a FI operand ; CHECK-LABEL: copytoreg_fi: