#include "WebAssemblyFrameLowering.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "Utils/WebAssemblyTypeUtilities.h"
#include "WebAssembly.h"
#include "WebAssemblyInstrInfo.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyTargetMachine.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Instructions.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "wasm-frame-info"
Optional<unsigned>
WebAssemblyFrameLowering::getLocalForStackObject(MachineFunction &MF,
int FrameIndex) {
MachineFrameInfo &MFI = MF.getFrameInfo();
if (MFI.getStackID(FrameIndex) == TargetStackID::WasmLocal)
return static_cast<unsigned>(MFI.getObjectOffset(FrameIndex));
const AllocaInst *AI = MFI.getObjectAllocation(FrameIndex);
if (!AI ||
!WebAssembly::isWasmVarAddressSpace(AI->getType()->getAddressSpace()))
return None;
SmallVector<EVT, 4> ValueVTs;
const WebAssemblyTargetLowering &TLI =
*MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
WebAssemblyFunctionInfo *FuncInfo = MF.getInfo<WebAssemblyFunctionInfo>();
ComputeValueVTs(TLI, MF.getDataLayout(), AI->getAllocatedType(), ValueVTs);
MFI.setStackID(FrameIndex, TargetStackID::WasmLocal);
unsigned Local = FuncInfo->getParams().size() + FuncInfo->getLocals().size();
MFI.setObjectOffset(FrameIndex, Local);
for (EVT ValueVT : ValueVTs)
FuncInfo->addLocal(ValueVT.getSimpleVT());
MFI.setObjectSize(FrameIndex, ValueVTs.size());
return static_cast<unsigned>(Local);
}
bool WebAssemblyFrameLowering::hasBP(const MachineFunction &MF) const {
const auto *RegInfo =
MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
return RegInfo->hasStackRealignment(MF);
}
bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
bool HasFixedSizedObjects = MFI.getStackSize() > 0;
bool NeedsFixedReference = !hasBP(MF) || HasFixedSizedObjects;
return MFI.isFrameAddressTaken() ||
(MFI.hasVarSizedObjects() && NeedsFixedReference) ||
MFI.hasStackMap() || MFI.hasPatchPoint();
}
bool WebAssemblyFrameLowering::hasReservedCallFrame(
const MachineFunction &MF) const {
return !MF.getFrameInfo().hasVarSizedObjects();
}
bool WebAssemblyFrameLowering::needsSPForLocalFrame(
const MachineFunction &MF) const {
auto &MFI = MF.getFrameInfo();
return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF);
}
bool WebAssemblyFrameLowering::needsPrologForEH(
const MachineFunction &MF) const {
auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType();
return EHType == ExceptionHandling::Wasm &&
MF.getFunction().hasPersonalityFn() && MF.getFrameInfo().hasCalls();
}
bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF) const {
return needsSPForLocalFrame(MF) || needsPrologForEH(MF);
}
bool WebAssemblyFrameLowering::needsSPWriteback(
const MachineFunction &MF) const {
auto &MFI = MF.getFrameInfo();
assert(needsSP(MF));
bool CanUseRedZone = MFI.getStackSize() <= RedZoneSize && !MFI.hasCalls() &&
!MF.getFunction().hasFnAttribute(Attribute::NoRedZone);
return needsSPForLocalFrame(MF) && !CanUseRedZone;
}
unsigned WebAssemblyFrameLowering::getSPReg(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::SP64
: WebAssembly::SP32;
}
unsigned WebAssemblyFrameLowering::getFPReg(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::FP64
: WebAssembly::FP32;
}
unsigned
WebAssemblyFrameLowering::getOpcConst(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::CONST_I64
: WebAssembly::CONST_I32;
}
unsigned WebAssemblyFrameLowering::getOpcAdd(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::ADD_I64
: WebAssembly::ADD_I32;
}
unsigned WebAssemblyFrameLowering::getOpcSub(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::SUB_I64
: WebAssembly::SUB_I32;
}
unsigned WebAssemblyFrameLowering::getOpcAnd(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::AND_I64
: WebAssembly::AND_I32;
}
unsigned
WebAssemblyFrameLowering::getOpcGlobGet(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::GLOBAL_GET_I64
: WebAssembly::GLOBAL_GET_I32;
}
unsigned
WebAssemblyFrameLowering::getOpcGlobSet(const MachineFunction &MF) {
return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
? WebAssembly::GLOBAL_SET_I64
: WebAssembly::GLOBAL_SET_I32;
}
void WebAssemblyFrameLowering::writeSPToGlobal(
unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const {
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
const char *ES = "__stack_pointer";
auto *SPSymbol = MF.createExternalSymbolName(ES);
BuildMI(MBB, InsertStore, DL, TII->get(getOpcGlobSet(MF)))
.addExternalSymbol(SPSymbol)
.addReg(SrcReg);
}
MachineBasicBlock::iterator
WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const {
assert(!I->getOperand(0).getImm() && (hasFP(MF) || hasBP(MF)) &&
"Call frame pseudos should only be used for dynamic stack adjustment");
auto &ST = MF.getSubtarget<WebAssemblySubtarget>();
const auto *TII = ST.getInstrInfo();
if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
needsSPWriteback(MF)) {
DebugLoc DL = I->getDebugLoc();
writeSPToGlobal(getSPReg(MF), MF, MBB, I, DL);
}
return MBB.erase(I);
}
void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
auto &MFI = MF.getFrameInfo();
assert(MFI.getCalleeSavedInfo().empty() &&
"WebAssembly should not have callee-saved registers");
if (!needsSP(MF))
return;
uint64_t StackSize = MFI.getStackSize();
auto &ST = MF.getSubtarget<WebAssemblySubtarget>();
const auto *TII = ST.getInstrInfo();
auto &MRI = MF.getRegInfo();
auto InsertPt = MBB.begin();
while (InsertPt != MBB.end() &&
WebAssembly::isArgument(InsertPt->getOpcode()))
++InsertPt;
DebugLoc DL;
const TargetRegisterClass *PtrRC =
MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
unsigned SPReg = getSPReg(MF);
if (StackSize)
SPReg = MRI.createVirtualRegister(PtrRC);
const char *ES = "__stack_pointer";
auto *SPSymbol = MF.createExternalSymbolName(ES);
BuildMI(MBB, InsertPt, DL, TII->get(getOpcGlobGet(MF)), SPReg)
.addExternalSymbol(SPSymbol);
bool HasBP = hasBP(MF);
if (HasBP) {
auto FI = MF.getInfo<WebAssemblyFunctionInfo>();
Register BasePtr = MRI.createVirtualRegister(PtrRC);
FI->setBasePointerVreg(BasePtr);
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), BasePtr)
.addReg(SPReg);
}
if (StackSize) {
Register OffsetReg = MRI.createVirtualRegister(PtrRC);
BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), OffsetReg)
.addImm(StackSize);
BuildMI(MBB, InsertPt, DL, TII->get(getOpcSub(MF)), getSPReg(MF))
.addReg(SPReg)
.addReg(OffsetReg);
}
if (HasBP) {
Register BitmaskReg = MRI.createVirtualRegister(PtrRC);
Align Alignment = MFI.getMaxAlign();
BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), BitmaskReg)
.addImm((int64_t) ~(Alignment.value() - 1));
BuildMI(MBB, InsertPt, DL, TII->get(getOpcAnd(MF)), getSPReg(MF))
.addReg(getSPReg(MF))
.addReg(BitmaskReg);
}
if (hasFP(MF)) {
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), getFPReg(MF))
.addReg(getSPReg(MF));
}
if (StackSize && needsSPWriteback(MF)) {
writeSPToGlobal(getSPReg(MF), MF, MBB, InsertPt, DL);
}
}
void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
uint64_t StackSize = MF.getFrameInfo().getStackSize();
if (!needsSP(MF) || !needsSPWriteback(MF))
return;
auto &ST = MF.getSubtarget<WebAssemblySubtarget>();
const auto *TII = ST.getInstrInfo();
auto &MRI = MF.getRegInfo();
auto InsertPt = MBB.getFirstTerminator();
DebugLoc DL;
if (InsertPt != MBB.end())
DL = InsertPt->getDebugLoc();
unsigned SPReg = 0;
unsigned SPFPReg = hasFP(MF) ? getFPReg(MF) : getSPReg(MF);
if (hasBP(MF)) {
auto FI = MF.getInfo<WebAssemblyFunctionInfo>();
SPReg = FI->getBasePointerVreg();
} else if (StackSize) {
const TargetRegisterClass *PtrRC =
MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
Register OffsetReg = MRI.createVirtualRegister(PtrRC);
BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), OffsetReg)
.addImm(StackSize);
SPReg = MRI.createVirtualRegister(PtrRC);
BuildMI(MBB, InsertPt, DL, TII->get(getOpcAdd(MF)), SPReg)
.addReg(SPFPReg)
.addReg(OffsetReg);
} else {
SPReg = SPFPReg;
}
writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL);
}
bool WebAssemblyFrameLowering::isSupportedStackID(
TargetStackID::Value ID) const {
if (ID == TargetStackID::WasmLocal)
return true;
return TargetFrameLowering::isSupportedStackID(ID);
}
TargetFrameLowering::DwarfFrameBase
WebAssemblyFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const {
DwarfFrameBase Loc;
Loc.Kind = DwarfFrameBase::WasmFrameBase;
const WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
if (needsSP(MF) && MFI.isFrameBaseVirtual()) {
unsigned LocalNum = MFI.getFrameBaseLocal();
Loc.Location.WasmLoc = {WebAssembly::TI_LOCAL, LocalNum};
} else {
Loc.Location.WasmLoc = {WebAssembly::TI_GLOBAL_RELOC, 0};
}
return Loc;
}