Skip to content

Commit 82f92e3

Browse files
committed
[WebAssembly][CodeGen] IR support for WebAssembly local variables
This patch adds TargetStackID::WasmLocal. This stack holds locations of values that are only addressable by name -- not via a pointer to memory. For the WebAssembly target, these objects are lowered to WebAssembly local variables, which are managed by the WebAssembly run-time and are not addressable by linear memory. For the WebAssembly target IR indicates that an AllocaInst should be put on TargetStackID::WasmLocal by putting it in the non-integral address space WASM_ADDRESS_SPACE_WASM_VAR, with value 1. SROA will mostly lift these allocations to SSA locals, but any alloca that reaches instruction selection (usually in non-optimized builds) will be assigned the new TargetStackID there. Loads and stores to those values are transformed to new WebAssemblyISD::LOCAL_GET / WebAssemblyISD::LOCAL_SET nodes, which then lower to the type-specific LOCAL_GET_I32 etc instructions via tablegen patterns. Differential Revision: https://reviews.llvm.org/D101140
1 parent f000c4c commit 82f92e3

13 files changed

+249
-7
lines changed

llvm/include/llvm/CodeGen/MIRYamlMapping.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ struct ScalarEnumerationTraits<TargetStackID::Value> {
348348
IO.enumCase(ID, "default", TargetStackID::Default);
349349
IO.enumCase(ID, "sgpr-spill", TargetStackID::SGPRSpill);
350350
IO.enumCase(ID, "scalable-vector", TargetStackID::ScalableVector);
351+
IO.enumCase(ID, "wasm-local", TargetStackID::WasmLocal);
351352
IO.enumCase(ID, "noalloc", TargetStackID::NoAlloc);
352353
}
353354
};

llvm/include/llvm/CodeGen/TargetFrameLowering.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ namespace llvm {
2424
class RegScavenger;
2525

2626
namespace TargetStackID {
27-
enum Value {
28-
Default = 0,
29-
SGPRSpill = 1,
30-
ScalableVector = 2,
31-
NoAlloc = 255
32-
};
27+
enum Value {
28+
Default = 0,
29+
SGPRSpill = 1,
30+
ScalableVector = 2,
31+
WasmLocal = 3,
32+
NoAlloc = 255
33+
};
3334
}
3435

3536
/// Information about stack frame layout on the target. It holds the direction

llvm/lib/Target/AMDGPU/SIFrameLowering.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ bool SIFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
661661
case TargetStackID::SGPRSpill:
662662
return true;
663663
case TargetStackID::ScalableVector:
664+
case TargetStackID::WasmLocal:
664665
return false;
665666
}
666667
llvm_unreachable("Invalid TargetStackID::Value");

llvm/lib/Target/RISCV/RISCVFrameLowering.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ bool RISCVFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
10991099
return true;
11001100
case TargetStackID::NoAlloc:
11011101
case TargetStackID::SGPRSpill:
1102+
case TargetStackID::WasmLocal:
11021103
return false;
11031104
}
11041105
llvm_unreachable("Invalid TargetStackID::Value");

llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,10 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
239239
Changed = true;
240240
}
241241

242-
// Start assigning local numbers after the last parameter.
242+
// Start assigning local numbers after the last parameter and after any
243+
// already-assigned locals.
243244
unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size());
245+
CurLocal += static_cast<unsigned>(MFI.getLocals().size());
244246

245247
// Precompute the set of registers that are unused, so that we can insert
246248
// drops to their defs.

llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
#include "WebAssemblyMachineFunctionInfo.h"
2626
#include "WebAssemblySubtarget.h"
2727
#include "WebAssemblyTargetMachine.h"
28+
#include "llvm/CodeGen/Analysis.h"
2829
#include "llvm/CodeGen/MachineFrameInfo.h"
2930
#include "llvm/CodeGen/MachineFunction.h"
3031
#include "llvm/CodeGen/MachineInstrBuilder.h"
3132
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
3233
#include "llvm/CodeGen/MachineRegisterInfo.h"
34+
#include "llvm/IR/Instructions.h"
3335
#include "llvm/MC/MCAsmInfo.h"
3436
#include "llvm/Support/Debug.h"
3537
using namespace llvm;
@@ -39,6 +41,52 @@ using namespace llvm;
3941
// TODO: wasm64
4042
// TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
4143

44+
// In an ideal world, when objects are added to the MachineFrameInfo by
45+
// FunctionLoweringInfo::set, we could somehow hook into target-specific code to
46+
// ensure they are assigned the right stack ID. However there isn't a hook that
47+
// runs between then and DAG building time, though, so instead we hoist stack
48+
// objects lazily when they are first used, and comprehensively after the DAG is
49+
// built via the PreprocessISelDAG hook, called by the
50+
// SelectionDAGISel::runOnMachineFunction. We have to do it in two places
51+
// because we want to do it while building the selection DAG for uses of alloca,
52+
// but not all alloca instructions are used so we have to follow up afterwards.
53+
Optional<unsigned>
54+
WebAssemblyFrameLowering::getLocalForStackObject(MachineFunction &MF,
55+
int FrameIndex) {
56+
MachineFrameInfo &MFI = MF.getFrameInfo();
57+
58+
// If already hoisted to a local, done.
59+
if (MFI.getStackID(FrameIndex) == TargetStackID::WasmLocal)
60+
return static_cast<unsigned>(MFI.getObjectOffset(FrameIndex));
61+
62+
// If not allocated in the object address space, this object will be in
63+
// linear memory.
64+
const AllocaInst *AI = MFI.getObjectAllocation(FrameIndex);
65+
if (!AI ||
66+
!WebAssembly::isWasmVarAddressSpace(AI->getType()->getAddressSpace()))
67+
return None;
68+
69+
// Otherwise, allocate this object in the named value stack, outside of linear
70+
// memory.
71+
SmallVector<EVT, 4> ValueVTs;
72+
const WebAssemblyTargetLowering &TLI =
73+
*MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
74+
WebAssemblyFunctionInfo *FuncInfo = MF.getInfo<WebAssemblyFunctionInfo>();
75+
ComputeValueVTs(TLI, MF.getDataLayout(), AI->getAllocatedType(), ValueVTs);
76+
MFI.setStackID(FrameIndex, TargetStackID::WasmLocal);
77+
// Abuse SP offset to record the index of the first local in the object.
78+
unsigned Local = FuncInfo->getParams().size() + FuncInfo->getLocals().size();
79+
MFI.setObjectOffset(FrameIndex, Local);
80+
// Allocate WebAssembly locals for each non-aggregate component of the
81+
// allocation.
82+
for (EVT ValueVT : ValueVTs)
83+
FuncInfo->addLocal(ValueVT.getSimpleVT());
84+
// Abuse object size to record number of WebAssembly locals allocated to
85+
// this object.
86+
MFI.setObjectSize(FrameIndex, ValueVTs.size());
87+
return static_cast<unsigned>(Local);
88+
}
89+
4290
/// We need a base pointer in the case of having items on the stack that
4391
/// require stricter alignment than the stack pointer itself. Because we need
4492
/// to shift the stack pointer by some unknown amount to force the alignment,
@@ -314,6 +362,16 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
314362
writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL);
315363
}
316364

365+
bool WebAssemblyFrameLowering::isSupportedStackID(
366+
TargetStackID::Value ID) const {
367+
// Use the Object stack for WebAssembly locals which can only be accessed
368+
// by name, not via an address in linear memory.
369+
if (ID == TargetStackID::WasmLocal)
370+
return true;
371+
372+
return TargetFrameLowering::isSupportedStackID(ID);
373+
}
374+
317375
TargetFrameLowering::DwarfFrameBase
318376
WebAssemblyFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const {
319377
DwarfFrameBase Loc;

llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
4343

4444
bool hasFP(const MachineFunction &MF) const override;
4545
bool hasReservedCallFrame(const MachineFunction &MF) const override;
46+
bool isSupportedStackID(TargetStackID::Value ID) const override;
4647
DwarfFrameBase getDwarfFrameBase(const MachineFunction &MF) const override;
4748

4849
bool needsPrologForEH(const MachineFunction &MF) const;
@@ -53,6 +54,11 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
5354
MachineBasicBlock::iterator &InsertStore,
5455
const DebugLoc &DL) const;
5556

57+
// Returns the index of the WebAssembly local to which the stack object
58+
// FrameIndex in MF should be allocated, or None.
59+
static Optional<unsigned> getLocalForStackObject(MachineFunction &MF,
60+
int FrameIndex);
61+
5662
static unsigned getSPReg(const MachineFunction &MF);
5763
static unsigned getFPReg(const MachineFunction &MF);
5864
static unsigned getOpcConst(const MachineFunction &MF);

llvm/lib/Target/WebAssembly/WebAssemblyISD.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ HANDLE_NODETYPE(CALL)
1717
HANDLE_NODETYPE(RET_CALL)
1818
HANDLE_NODETYPE(RETURN)
1919
HANDLE_NODETYPE(ARGUMENT)
20+
HANDLE_NODETYPE(LOCAL_GET)
21+
HANDLE_NODETYPE(LOCAL_SET)
2022
// A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol
2123
HANDLE_NODETYPE(Wrapper)
2224
// A special wapper used in PIC code for __memory_base/__table_base relative

llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
1515
#include "WebAssembly.h"
1616
#include "WebAssemblyTargetMachine.h"
17+
#include "llvm/CodeGen/MachineFrameInfo.h"
1718
#include "llvm/CodeGen/SelectionDAGISel.h"
1819
#include "llvm/IR/DiagnosticInfo.h"
1920
#include "llvm/IR/Function.h" // To access function attributes.
@@ -56,6 +57,8 @@ class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
5657
return SelectionDAGISel::runOnMachineFunction(MF);
5758
}
5859

60+
void PreprocessISelDAG() override;
61+
5962
void Select(SDNode *Node) override;
6063

6164
bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
@@ -69,6 +72,18 @@ class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
6972
};
7073
} // end anonymous namespace
7174

75+
void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
76+
// Stack objects that should be allocated to locals are hoisted to WebAssembly
77+
// locals when they are first used. However for those without uses, we hoist
78+
// them here. It would be nice if there were some hook to do this when they
79+
// are added to the MachineFrameInfo, but that's not the case right now.
80+
MachineFrameInfo &FrameInfo = MF->getFrameInfo();
81+
for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)
82+
WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);
83+
84+
SelectionDAGISel::PreprocessISelDAG();
85+
}
86+
7287
void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
7388
// If we have a custom node, we already have selected!
7489
if (Node->isMachineOpcode()) {

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,15 @@ static bool IsWebAssemblyGlobal(SDValue Op) {
12761276
return false;
12771277
}
12781278

1279+
static Optional<unsigned> IsWebAssemblyLocal(SDValue Op, SelectionDAG &DAG) {
1280+
const FrameIndexSDNode *FI = dyn_cast<FrameIndexSDNode>(Op);
1281+
if (!FI)
1282+
return None;
1283+
1284+
auto &MF = DAG.getMachineFunction();
1285+
return WebAssemblyFrameLowering::getLocalForStackObject(MF, FI->getIndex());
1286+
}
1287+
12791288
SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op,
12801289
SelectionDAG &DAG) const {
12811290
SDLoc DL(Op);
@@ -1295,6 +1304,17 @@ SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op,
12951304
SN->getMemoryVT(), SN->getMemOperand());
12961305
}
12971306

1307+
if (Optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) {
1308+
if (!Offset->isUndef())
1309+
report_fatal_error("unexpected offset when storing to webassembly local",
1310+
false);
1311+
1312+
SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32);
1313+
SDVTList Tys = DAG.getVTList(MVT::Other); // The chain.
1314+
SDValue Ops[] = {SN->getChain(), Idx, Value};
1315+
return DAG.getNode(WebAssemblyISD::LOCAL_SET, DL, Tys, Ops);
1316+
}
1317+
12981318
return Op;
12991319
}
13001320

@@ -1316,6 +1336,20 @@ SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op,
13161336
LN->getMemoryVT(), LN->getMemOperand());
13171337
}
13181338

1339+
if (Optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) {
1340+
if (!Offset->isUndef())
1341+
report_fatal_error(
1342+
"unexpected offset when loading from webassembly local", false);
1343+
1344+
SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32);
1345+
EVT LocalVT = LN->getValueType(0);
1346+
SDValue LocalGet = DAG.getNode(WebAssemblyISD::LOCAL_GET, DL, LocalVT,
1347+
{LN->getChain(), Idx});
1348+
SDValue Result = DAG.getMergeValues({LocalGet, LN->getChain()}, DL);
1349+
assert(Result->getNumValues() == 2 && "Loads must carry a chain!");
1350+
return Result;
1351+
}
1352+
13191353
return Op;
13201354
}
13211355

llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ def SDT_WebAssemblyCallSeqEnd :
7272
SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>;
7373
def SDT_WebAssemblyBrTable : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
7474
def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>;
75+
def SDT_WebAssemblyLocalGet : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>;
76+
def SDT_WebAssemblyLocalSet : SDTypeProfile<0, 2, [SDTCisVT<0, i32>]>;
7577
def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>;
7678
def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
7779
SDTCisPtrTy<0>]>;
@@ -114,6 +116,12 @@ def WebAssemblyglobal_get :
114116
def WebAssemblyglobal_set :
115117
SDNode<"WebAssemblyISD::GLOBAL_SET", SDT_WebAssemblyGlobalSet,
116118
[SDNPHasChain, SDNPMayStore, SDNPMemOperand]>;
119+
def WebAssemblylocal_get :
120+
SDNode<"WebAssemblyISD::LOCAL_GET", SDT_WebAssemblyLocalGet,
121+
[SDNPHasChain, SDNPMayLoad]>;
122+
def WebAssemblylocal_set :
123+
SDNode<"WebAssemblyISD::LOCAL_SET", SDT_WebAssemblyLocalSet,
124+
[SDNPHasChain, SDNPMayStore]>;
117125

118126
//===----------------------------------------------------------------------===//
119127
// WebAssembly-specific Operands.
@@ -332,6 +340,10 @@ multiclass LOCAL<WebAssemblyRegClass rc, Operand global_op> {
332340
def : Pat<(WebAssemblyglobal_set
333341
vt:$src, (WebAssemblywrapper tglobaladdr:$addr)),
334342
(!cast<NI>("GLOBAL_SET_" # rc) tglobaladdr:$addr, vt:$src)>;
343+
def : Pat<(vt (WebAssemblylocal_get (i32 timm:$local))),
344+
(!cast<NI>("LOCAL_GET_" # rc) timm:$local)>;
345+
def : Pat<(WebAssemblylocal_set timm:$local, vt:$src),
346+
(!cast<NI>("LOCAL_SET_" # rc) timm:$local, vt:$src)>;
335347
}
336348
}
337349
defm "" : LOCAL<I32, global_op32>;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
; RUN: llc -mtriple=wasm32-unknown-unknown -asm-verbose=false < %s | FileCheck %s --check-prefix=CHECKCG
2+
; RUN: llc -mtriple=wasm32-unknown-unknown -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=CHECKISEL
3+
4+
%f32_cell = type float addrspace(1)*
5+
6+
; CHECKISEL-LABEL: name: ir_local_f32
7+
; CHECKISEL: stack:
8+
; CHECKISEL: id: 0, name: retval, type: default, offset: 1, size: 1, alignment: 4,
9+
; CHECKISEL-NEXT: stack-id: wasm-local
10+
11+
; CHECKCG-LABEL: ir_local_f32:
12+
; CHECKCG-NEXT: .functype ir_local_f32 (f32) -> (f32)
13+
; CHECKCG-NEXT: .local f32
14+
; CHECKCG-NEXT: local.get 0
15+
; CHECKCG-NEXT: local.set 1
16+
17+
define float @ir_local_f32(float %arg) {
18+
%retval = alloca float, addrspace(1)
19+
store float %arg, %f32_cell %retval
20+
%reloaded = load float, %f32_cell %retval
21+
ret float %reloaded
22+
}

0 commit comments

Comments
 (0)