summaryrefslogtreecommitdiff
path: root/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp')
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp179
1 files changed, 123 insertions, 56 deletions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
index 421d353a89e88..1d4e2e3a8f9e5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -36,6 +36,7 @@
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
+#include <iterator>
using namespace llvm;
#define DEBUG_TYPE "wasm-reg-stackify"
@@ -120,6 +121,7 @@ static void convertImplicitDefToConstZero(MachineInstr *MI,
Type::getDoubleTy(MF.getFunction().getContext())));
MI->addOperand(MachineOperand::CreateFPImm(Val));
} else if (RegClass == &WebAssembly::V128RegClass) {
+ // TODO: Replace this with v128.const 0 once that is supported in V8
Register TempReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
MI->setDesc(TII->get(WebAssembly::SPLAT_v4i32));
MI->addOperand(MachineOperand::CreateReg(TempReg, false));
@@ -135,12 +137,12 @@ static void convertImplicitDefToConstZero(MachineInstr *MI,
// Determine whether a call to the callee referenced by
// MI->getOperand(CalleeOpNo) reads memory, writes memory, and/or has side
// effects.
-static void queryCallee(const MachineInstr &MI, unsigned CalleeOpNo, bool &Read,
- bool &Write, bool &Effects, bool &StackPointer) {
+static void queryCallee(const MachineInstr &MI, bool &Read, bool &Write,
+ bool &Effects, bool &StackPointer) {
// All calls can use the stack pointer.
StackPointer = true;
- const MachineOperand &MO = MI.getOperand(CalleeOpNo);
+ const MachineOperand &MO = WebAssembly::getCalleeOp(MI);
if (MO.isGlobal()) {
const Constant *GV = MO.getGlobal();
if (const auto *GA = dyn_cast<GlobalAlias>(GV))
@@ -246,14 +248,14 @@ static void query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read,
}
// Check for writes to __stack_pointer global.
- if (MI.getOpcode() == WebAssembly::GLOBAL_SET_I32 &&
+ if ((MI.getOpcode() == WebAssembly::GLOBAL_SET_I32 ||
+ MI.getOpcode() == WebAssembly::GLOBAL_SET_I64) &&
strcmp(MI.getOperand(0).getSymbolName(), "__stack_pointer") == 0)
StackPointer = true;
// Analyze calls.
if (MI.isCall()) {
- unsigned CalleeOpNo = WebAssembly::getCalleeOpNo(MI.getOpcode());
- queryCallee(MI, CalleeOpNo, Read, Write, Effects, StackPointer);
+ queryCallee(MI, Read, Write, Effects, StackPointer);
}
}
@@ -313,25 +315,59 @@ static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI,
// walking the block.
// TODO: Compute memory dependencies in a way that uses AliasAnalysis to be
// more precise.
-static bool isSafeToMove(const MachineInstr *Def, const MachineInstr *Insert,
- AliasAnalysis &AA, const MachineRegisterInfo &MRI) {
- assert(Def->getParent() == Insert->getParent());
+static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use,
+ const MachineInstr *Insert, AliasAnalysis &AA,
+ const WebAssemblyFunctionInfo &MFI,
+ const MachineRegisterInfo &MRI) {
+ const MachineInstr *DefI = Def->getParent();
+ const MachineInstr *UseI = Use->getParent();
+ assert(DefI->getParent() == Insert->getParent());
+ assert(UseI->getParent() == Insert->getParent());
+
+ // The first def of a multivalue instruction can be stackified by moving,
+ // since the later defs can always be placed into locals if necessary. Later
+ // defs can only be stackified if all previous defs are already stackified
+ // since ExplicitLocals will not know how to place a def in a local if a
+ // subsequent def is stackified. But only one def can be stackified by moving
+ // the instruction, so it must be the first one.
+ //
+ // TODO: This could be loosened to be the first *live* def, but care would
+ // have to be taken to ensure the drops of the initial dead defs can be
+ // placed. This would require checking that no previous defs are used in the
+ // same instruction as subsequent defs.
+ if (Def != DefI->defs().begin())
+ return false;
+
+ // If any subsequent def is used prior to the current value by the same
+ // instruction in which the current value is used, we cannot
+ // stackify. Stackifying in this case would require that def moving below the
+ // current def in the stack, which cannot be achieved, even with locals.
+ for (const auto &SubsequentDef : drop_begin(DefI->defs(), 1)) {
+ for (const auto &PriorUse : UseI->uses()) {
+ if (&PriorUse == Use)
+ break;
+ if (PriorUse.isReg() && SubsequentDef.getReg() == PriorUse.getReg())
+ return false;
+ }
+ }
+
+ // If moving is a semantic nop, it is always allowed
+ const MachineBasicBlock *MBB = DefI->getParent();
+ auto NextI = std::next(MachineBasicBlock::const_iterator(DefI));
+ for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI)
+ ;
+ if (NextI == Insert)
+ return true;
// 'catch' and 'extract_exception' should be the first instruction of a BB and
// cannot move.
- if (Def->getOpcode() == WebAssembly::CATCH ||
- Def->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
- const MachineBasicBlock *MBB = Def->getParent();
- auto NextI = std::next(MachineBasicBlock::const_iterator(Def));
- for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI)
- ;
- if (NextI != Insert)
- return false;
- }
+ if (DefI->getOpcode() == WebAssembly::CATCH ||
+ DefI->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32)
+ return false;
// Check for register dependencies.
SmallVector<unsigned, 4> MutableRegisters;
- for (const MachineOperand &MO : Def->operands()) {
+ for (const MachineOperand &MO : DefI->operands()) {
if (!MO.isReg() || MO.isUndef())
continue;
Register Reg = MO.getReg();
@@ -361,7 +397,7 @@ static bool isSafeToMove(const MachineInstr *Def, const MachineInstr *Insert,
}
bool Read = false, Write = false, Effects = false, StackPointer = false;
- query(*Def, AA, Read, Write, Effects, StackPointer);
+ query(*DefI, AA, Read, Write, Effects, StackPointer);
// If the instruction does not access memory and has no side effects, it has
// no additional dependencies.
@@ -369,8 +405,8 @@ static bool isSafeToMove(const MachineInstr *Def, const MachineInstr *Insert,
if (!Read && !Write && !Effects && !StackPointer && !HasMutableRegisters)
return true;
- // Scan through the intervening instructions between Def and Insert.
- MachineBasicBlock::const_iterator D(Def), I(Insert);
+ // Scan through the intervening instructions between DefI and Insert.
+ MachineBasicBlock::const_iterator D(DefI), I(Insert);
for (--I; I != D; --I) {
bool InterveningRead = false;
bool InterveningWrite = false;
@@ -495,7 +531,7 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op,
if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) {
// No one else is using this register for anything so we can just stackify
// it in place.
- MFI.stackifyVReg(Reg);
+ MFI.stackifyVReg(MRI, Reg);
} else {
// The register may have unrelated uses or defs; create a new register for
// just our one def and use so that we can stackify it.
@@ -512,7 +548,7 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op,
LIS.getInstructionIndex(*Op.getParent()).getRegSlot(),
/*RemoveDeadValNo=*/true);
- MFI.stackifyVReg(NewReg);
+ MFI.stackifyVReg(MRI, NewReg);
DefDIs.updateReg(NewReg);
@@ -541,7 +577,7 @@ static MachineInstr *rematerializeCheapDef(
MachineInstr *Clone = &*std::prev(Insert);
LIS.InsertMachineInstrInMaps(*Clone);
LIS.createAndComputeVirtRegInterval(NewReg);
- MFI.stackifyVReg(NewReg);
+ MFI.stackifyVReg(MRI, NewReg);
imposeStackOrdering(Clone);
LLVM_DEBUG(dbgs() << " - Cloned to "; Clone->dump());
@@ -632,8 +668,8 @@ static MachineInstr *moveAndTeeForMultiUse(
// Finish stackifying the new regs.
LIS.createAndComputeVirtRegInterval(TeeReg);
LIS.createAndComputeVirtRegInterval(DefReg);
- MFI.stackifyVReg(DefReg);
- MFI.stackifyVReg(TeeReg);
+ MFI.stackifyVReg(MRI, DefReg);
+ MFI.stackifyVReg(MRI, TeeReg);
imposeStackOrdering(Def);
imposeStackOrdering(Tee);
@@ -801,32 +837,32 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
CommutingState Commuting;
TreeWalkerState TreeWalker(Insert);
while (!TreeWalker.done()) {
- MachineOperand &Op = TreeWalker.pop();
+ MachineOperand &Use = TreeWalker.pop();
// We're only interested in explicit virtual register operands.
- if (!Op.isReg())
+ if (!Use.isReg())
continue;
- Register Reg = Op.getReg();
- assert(Op.isUse() && "explicit_uses() should only iterate over uses");
- assert(!Op.isImplicit() &&
+ Register Reg = Use.getReg();
+ assert(Use.isUse() && "explicit_uses() should only iterate over uses");
+ assert(!Use.isImplicit() &&
"explicit_uses() should only iterate over explicit operands");
if (Register::isPhysicalRegister(Reg))
continue;
// Identify the definition for this register at this point.
- MachineInstr *Def = getVRegDef(Reg, Insert, MRI, LIS);
- if (!Def)
+ MachineInstr *DefI = getVRegDef(Reg, Insert, MRI, LIS);
+ if (!DefI)
continue;
// Don't nest an INLINE_ASM def into anything, because we don't have
// constraints for $pop outputs.
- if (Def->isInlineAsm())
+ if (DefI->isInlineAsm())
continue;
// Argument instructions represent live-in registers and not real
// instructions.
- if (WebAssembly::isArgument(Def->getOpcode()))
+ if (WebAssembly::isArgument(DefI->getOpcode()))
continue;
// Currently catch's return value register cannot be stackified, because
@@ -843,28 +879,38 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
// register should be assigned to a local to be propagated across
// 'block' boundary now.
//
- // TODO Fix this once we support the multi-value proposal.
- if (Def->getOpcode() == WebAssembly::CATCH)
+ // TODO: Fix this once we support the multivalue blocks
+ if (DefI->getOpcode() == WebAssembly::CATCH)
continue;
+ MachineOperand *Def = DefI->findRegisterDefOperand(Reg);
+ assert(Def != nullptr);
+
// Decide which strategy to take. Prefer to move a single-use value
// over cloning it, and prefer cloning over introducing a tee.
// For moving, we require the def to be in the same block as the use;
// this makes things simpler (LiveIntervals' handleMove function only
// supports intra-block moves) and it's MachineSink's job to catch all
// the sinking opportunities anyway.
- bool SameBlock = Def->getParent() == &MBB;
- bool CanMove = SameBlock && isSafeToMove(Def, Insert, AA, MRI) &&
+ bool SameBlock = DefI->getParent() == &MBB;
+ bool CanMove = SameBlock &&
+ isSafeToMove(Def, &Use, Insert, AA, MFI, MRI) &&
!TreeWalker.isOnStack(Reg);
- if (CanMove && hasOneUse(Reg, Def, MRI, MDT, LIS)) {
- Insert = moveForSingleUse(Reg, Op, Def, MBB, Insert, LIS, MFI, MRI);
- } else if (shouldRematerialize(*Def, AA, TII)) {
+ if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) {
+ Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI);
+
+ // If we are removing the frame base reg completely, remove the debug
+ // info as well.
+ // TODO: Encode this properly as a stackified value.
+ if (MFI.isFrameBaseVirtual() && MFI.getFrameBaseVreg() == Reg)
+ MFI.clearFrameBaseVreg();
+ } else if (shouldRematerialize(*DefI, AA, TII)) {
Insert =
- rematerializeCheapDef(Reg, Op, *Def, MBB, Insert->getIterator(),
+ rematerializeCheapDef(Reg, Use, *DefI, MBB, Insert->getIterator(),
LIS, MFI, MRI, TII, TRI);
- } else if (CanMove &&
- oneUseDominatesOtherUses(Reg, Op, MBB, MRI, MDT, LIS, MFI)) {
- Insert = moveAndTeeForMultiUse(Reg, Op, Def, MBB, Insert, LIS, MFI,
+ } else if (CanMove && oneUseDominatesOtherUses(Reg, Use, MBB, MRI, MDT,
+ LIS, MFI)) {
+ Insert = moveAndTeeForMultiUse(Reg, Use, DefI, MBB, Insert, LIS, MFI,
MRI, TII);
} else {
// We failed to stackify the operand. If the problem was ordering
@@ -875,6 +921,25 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
continue;
}
+ // Stackifying a multivalue def may unlock in-place stackification of
+ // subsequent defs. TODO: Handle the case where the consecutive uses are
+ // not all in the same instruction.
+ auto *SubsequentDef = Insert->defs().begin();
+ auto *SubsequentUse = &Use;
+ while (SubsequentDef != Insert->defs().end() &&
+ SubsequentUse != Use.getParent()->uses().end()) {
+ if (!SubsequentDef->isReg() || !SubsequentUse->isReg())
+ break;
+ unsigned DefReg = SubsequentDef->getReg();
+ unsigned UseReg = SubsequentUse->getReg();
+ // TODO: This single-use restriction could be relaxed by using tees
+ if (DefReg != UseReg || !MRI.hasOneUse(DefReg))
+ break;
+ MFI.stackifyVReg(MRI, DefReg);
+ ++SubsequentDef;
+ ++SubsequentUse;
+ }
+
// If the instruction we just stackified is an IMPLICIT_DEF, convert it
// to a constant 0 so that the def is explicit, and the push/pop
// correspondence is maintained.
@@ -912,18 +977,20 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
for (MachineInstr &MI : MBB) {
if (MI.isDebugInstr())
continue;
- for (MachineOperand &MO : reverse(MI.explicit_operands())) {
+ for (MachineOperand &MO : reverse(MI.explicit_uses())) {
if (!MO.isReg())
continue;
Register Reg = MO.getReg();
-
- if (MFI.isVRegStackified(Reg)) {
- if (MO.isDef())
- Stack.push_back(Reg);
- else
- assert(Stack.pop_back_val() == Reg &&
- "Register stack pop should be paired with a push");
- }
+ if (MFI.isVRegStackified(Reg))
+ assert(Stack.pop_back_val() == Reg &&
+ "Register stack pop should be paired with a push");
+ }
+ for (MachineOperand &MO : MI.defs()) {
+ if (!MO.isReg())
+ continue;
+ Register Reg = MO.getReg();
+ if (MFI.isVRegStackified(Reg))
+ Stack.push_back(MO.getReg());
}
}
// TODO: Generalize this code to support keeping values on the stack across