1 //- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// This file defines an instruction selector for the WebAssembly target.
12 //===----------------------------------------------------------------------===//
14 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
15 #include "WebAssembly.h"
16 #include "WebAssemblyISelLowering.h"
17 #include "WebAssemblyTargetMachine.h"
18 #include "llvm/CodeGen/MachineFrameInfo.h"
19 #include "llvm/CodeGen/SelectionDAGISel.h"
20 #include "llvm/CodeGen/WasmEHFuncInfo.h"
21 #include "llvm/IR/DiagnosticInfo.h"
22 #include "llvm/IR/Function.h" // To access function attributes.
23 #include "llvm/IR/IntrinsicsWebAssembly.h"
24 #include "llvm/Support/Debug.h"
25 #include "llvm/Support/KnownBits.h"
26 #include "llvm/Support/MathExtras.h"
27 #include "llvm/Support/raw_ostream.h"
31 #define DEBUG_TYPE "wasm-isel"
33 //===--------------------------------------------------------------------===//
34 /// WebAssembly-specific code to select WebAssembly machine instructions for
35 /// SelectionDAG operations.
38 class WebAssemblyDAGToDAGISel final
: public SelectionDAGISel
{
39 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
40 /// right decision when generating code for different targets.
41 const WebAssemblySubtarget
*Subtarget
;
44 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine
&TM
,
45 CodeGenOpt::Level OptLevel
)
46 : SelectionDAGISel(TM
, OptLevel
), Subtarget(nullptr) {
49 StringRef
getPassName() const override
{
50 return "WebAssembly Instruction Selection";
53 bool runOnMachineFunction(MachineFunction
&MF
) override
{
54 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
55 "********** Function: "
56 << MF
.getName() << '\n');
58 Subtarget
= &MF
.getSubtarget
<WebAssemblySubtarget
>();
60 return SelectionDAGISel::runOnMachineFunction(MF
);
63 void PreprocessISelDAG() override
;
65 void Select(SDNode
*Node
) override
;
67 bool SelectInlineAsmMemoryOperand(const SDValue
&Op
, unsigned ConstraintID
,
68 std::vector
<SDValue
> &OutOps
) override
;
69 bool SelectExternRefAddr(const SDValue
&Addr
, const SDValue
&Base
);
71 // Include the pieces autogenerated from the target description.
72 #include "WebAssemblyGenDAGISel.inc"
75 // add select functions here...
77 } // end anonymous namespace
79 void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {
80 // Stack objects that should be allocated to locals are hoisted to WebAssembly
81 // locals when they are first used. However for those without uses, we hoist
82 // them here. It would be nice if there were some hook to do this when they
83 // are added to the MachineFrameInfo, but that's not the case right now.
84 MachineFrameInfo
&FrameInfo
= MF
->getFrameInfo();
85 for (int Idx
= 0; Idx
< FrameInfo
.getObjectIndexEnd(); Idx
++)
86 WebAssemblyFrameLowering::getLocalForStackObject(*MF
, Idx
);
88 SelectionDAGISel::PreprocessISelDAG();
91 static SDValue
getTagSymNode(int Tag
, SelectionDAG
*DAG
) {
92 assert(Tag
== WebAssembly::CPP_EXCEPTION
);
93 auto &MF
= DAG
->getMachineFunction();
94 const auto &TLI
= DAG
->getTargetLoweringInfo();
95 MVT PtrVT
= TLI
.getPointerTy(DAG
->getDataLayout());
96 const char *SymName
= MF
.createExternalSymbolName("__cpp_exception");
97 return DAG
->getTargetExternalSymbol(SymName
, PtrVT
);
100 void WebAssemblyDAGToDAGISel::Select(SDNode
*Node
) {
101 // If we have a custom node, we already have selected!
102 if (Node
->isMachineOpcode()) {
103 LLVM_DEBUG(errs() << "== "; Node
->dump(CurDAG
); errs() << "\n");
108 MVT PtrVT
= TLI
->getPointerTy(CurDAG
->getDataLayout());
109 auto GlobalGetIns
= PtrVT
== MVT::i64
? WebAssembly::GLOBAL_GET_I64
110 : WebAssembly::GLOBAL_GET_I32
;
112 // Few custom selection stuff.
114 MachineFunction
&MF
= CurDAG
->getMachineFunction();
115 switch (Node
->getOpcode()) {
116 case ISD::ATOMIC_FENCE
: {
117 if (!MF
.getSubtarget
<WebAssemblySubtarget
>().hasAtomics())
120 uint64_t SyncScopeID
= Node
->getConstantOperandVal(2);
121 MachineSDNode
*Fence
= nullptr;
122 switch (SyncScopeID
) {
123 case SyncScope::SingleThread
:
124 // We lower a single-thread fence to a pseudo compiler barrier instruction
125 // preventing instruction reordering. This will not be emitted in final
127 Fence
= CurDAG
->getMachineNode(WebAssembly::COMPILER_FENCE
,
129 MVT::Other
, // outchain type
130 Node
->getOperand(0) // inchain
133 case SyncScope::System
:
134 // Currently wasm only supports sequentially consistent atomics, so we
135 // always set the order to 0 (sequentially consistent).
136 Fence
= CurDAG
->getMachineNode(
137 WebAssembly::ATOMIC_FENCE
,
139 MVT::Other
, // outchain type
140 CurDAG
->getTargetConstant(0, DL
, MVT::i32
), // order
141 Node
->getOperand(0) // inchain
145 llvm_unreachable("Unknown scope!");
148 ReplaceNode(Node
, Fence
);
149 CurDAG
->RemoveDeadNode(Node
);
153 case ISD::INTRINSIC_WO_CHAIN
: {
154 unsigned IntNo
= Node
->getConstantOperandVal(0);
156 case Intrinsic::wasm_tls_size
: {
157 MachineSDNode
*TLSSize
= CurDAG
->getMachineNode(
158 GlobalGetIns
, DL
, PtrVT
,
159 CurDAG
->getTargetExternalSymbol("__tls_size", PtrVT
));
160 ReplaceNode(Node
, TLSSize
);
164 case Intrinsic::wasm_tls_align
: {
165 MachineSDNode
*TLSAlign
= CurDAG
->getMachineNode(
166 GlobalGetIns
, DL
, PtrVT
,
167 CurDAG
->getTargetExternalSymbol("__tls_align", PtrVT
));
168 ReplaceNode(Node
, TLSAlign
);
175 case ISD::INTRINSIC_W_CHAIN
: {
176 unsigned IntNo
= Node
->getConstantOperandVal(1);
177 const auto &TLI
= CurDAG
->getTargetLoweringInfo();
178 MVT PtrVT
= TLI
.getPointerTy(CurDAG
->getDataLayout());
180 case Intrinsic::wasm_tls_base
: {
181 MachineSDNode
*TLSBase
= CurDAG
->getMachineNode(
182 GlobalGetIns
, DL
, PtrVT
, MVT::Other
,
183 CurDAG
->getTargetExternalSymbol("__tls_base", PtrVT
),
184 Node
->getOperand(0));
185 ReplaceNode(Node
, TLSBase
);
189 case Intrinsic::wasm_catch_exn
: {
190 int Tag
= Node
->getConstantOperandVal(2);
191 SDValue SymNode
= getTagSymNode(Tag
, CurDAG
);
192 MachineSDNode
*Catch
=
193 CurDAG
->getMachineNode(WebAssembly::CATCH
, DL
,
195 PtrVT
, // exception pointer
196 MVT::Other
// outchain type
199 SymNode
, // exception symbol
200 Node
->getOperand(0) // inchain
202 ReplaceNode(Node
, Catch
);
209 case ISD::INTRINSIC_VOID
: {
210 unsigned IntNo
= Node
->getConstantOperandVal(1);
212 case Intrinsic::wasm_throw
: {
213 int Tag
= Node
->getConstantOperandVal(2);
214 SDValue SymNode
= getTagSymNode(Tag
, CurDAG
);
215 MachineSDNode
*Throw
=
216 CurDAG
->getMachineNode(WebAssembly::THROW
, DL
,
217 MVT::Other
, // outchain type
219 SymNode
, // exception symbol
220 Node
->getOperand(3), // thrown value
221 Node
->getOperand(0) // inchain
223 ReplaceNode(Node
, Throw
);
230 case WebAssemblyISD::CALL
:
231 case WebAssemblyISD::RET_CALL
: {
232 // CALL has both variable operands and variable results, but ISel only
233 // supports one or the other. Split calls into two nodes glued together, one
234 // for the operands and one for the results. These two nodes will be
235 // recombined in a custom inserter hook into a single MachineInstr.
236 SmallVector
<SDValue
, 16> Ops
;
237 for (size_t i
= 1; i
< Node
->getNumOperands(); ++i
) {
238 SDValue Op
= Node
->getOperand(i
);
239 if (i
== 1 && Op
->getOpcode() == WebAssemblyISD::Wrapper
)
240 Op
= Op
->getOperand(0);
244 // Add the chain last
245 Ops
.push_back(Node
->getOperand(0));
246 MachineSDNode
*CallParams
=
247 CurDAG
->getMachineNode(WebAssembly::CALL_PARAMS
, DL
, MVT::Glue
, Ops
);
249 unsigned Results
= Node
->getOpcode() == WebAssemblyISD::CALL
250 ? WebAssembly::CALL_RESULTS
251 : WebAssembly::RET_CALL_RESULTS
;
253 SDValue
Link(CallParams
, 0);
254 MachineSDNode
*CallResults
=
255 CurDAG
->getMachineNode(Results
, DL
, Node
->getVTList(), Link
);
256 ReplaceNode(Node
, CallResults
);
264 // Select the default instruction.
268 bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
269 const SDValue
&Op
, unsigned ConstraintID
, std::vector
<SDValue
> &OutOps
) {
270 switch (ConstraintID
) {
271 case InlineAsm::Constraint_m
:
272 // We just support simple memory operands that just have a single address
273 // operand and need no special handling.
274 OutOps
.push_back(Op
);
283 /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
284 /// for instruction scheduling.
285 FunctionPass
*llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine
&TM
,
286 CodeGenOpt::Level OptLevel
) {
287 return new WebAssemblyDAGToDAGISel(TM
, OptLevel
);