blob: 26339eaef37db7511e47465805393846782d645a [file] [log] [blame]
Dan Gohman10e730a2015-06-29 23:51:55 +00001//- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Dan Gohman10e730a2015-06-29 23:51:55 +00006//
7//===----------------------------------------------------------------------===//
8///
9/// \file
Adrian Prantl5f8f34e42018-05-01 15:54:18 +000010/// This file defines an instruction selector for the WebAssembly target.
Dan Gohman10e730a2015-06-29 23:51:55 +000011///
12//===----------------------------------------------------------------------===//
13
Dan Gohman10e730a2015-06-29 23:51:55 +000014#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
Chandler Carruth6bda14b2017-06-06 11:49:48 +000015#include "WebAssembly.h"
Dan Gohman10e730a2015-06-29 23:51:55 +000016#include "WebAssemblyTargetMachine.h"
17#include "llvm/CodeGen/SelectionDAGISel.h"
Guanzhong Chen42bba4b2019-07-16 22:00:45 +000018#include "llvm/IR/DiagnosticInfo.h"
Dan Gohman10e730a2015-06-29 23:51:55 +000019#include "llvm/IR/Function.h" // To access function attributes.
20#include "llvm/Support/Debug.h"
Craig Topper053cf4d2017-04-28 08:15:33 +000021#include "llvm/Support/KnownBits.h"
Dan Gohman10e730a2015-06-29 23:51:55 +000022#include "llvm/Support/MathExtras.h"
23#include "llvm/Support/raw_ostream.h"
24using namespace llvm;
25
26#define DEBUG_TYPE "wasm-isel"
27
28//===--------------------------------------------------------------------===//
29/// WebAssembly-specific code to select WebAssembly machine instructions for
30/// SelectionDAG operations.
31///
32namespace {
33class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {
34 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
35 /// right decision when generating code for different targets.
36 const WebAssemblySubtarget *Subtarget;
37
38 bool ForCodeSize;
39
40public:
Heejin Ahn18c56a02019-02-04 19:13:39 +000041 WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,
Dan Gohman10e730a2015-06-29 23:51:55 +000042 CodeGenOpt::Level OptLevel)
Heejin Ahn18c56a02019-02-04 19:13:39 +000043 : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr), ForCodeSize(false) {
Dan Gohman10e730a2015-06-29 23:51:55 +000044 }
45
Mehdi Amini117296c2016-10-01 02:56:57 +000046 StringRef getPassName() const override {
Dan Gohman10e730a2015-06-29 23:51:55 +000047 return "WebAssembly Instruction Selection";
48 }
49
50 bool runOnMachineFunction(MachineFunction &MF) override {
Heejin Ahn569f0902019-01-09 23:05:21 +000051 LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
52 "********** Function: "
53 << MF.getName() << '\n');
54
Heejin Ahn5f3a0452019-04-13 16:54:39 +000055 ForCodeSize = MF.getFunction().hasOptSize();
Dan Gohman10e730a2015-06-29 23:51:55 +000056 Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
57 return SelectionDAGISel::runOnMachineFunction(MF);
58 }
59
Justin Bognerc6afd4b2016-05-13 22:44:57 +000060 void Select(SDNode *Node) override;
Dan Gohman10e730a2015-06-29 23:51:55 +000061
Dan Gohmanf19ed562015-11-13 01:42:29 +000062 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
63 std::vector<SDValue> &OutOps) override;
64
JF Bastienb9073fb2015-07-22 21:28:15 +000065// Include the pieces autogenerated from the target description.
66#include "WebAssemblyGenDAGISel.inc"
67
Dan Gohman10e730a2015-06-29 23:51:55 +000068private:
69 // add select functions here...
70};
71} // end anonymous namespace
72
Justin Bognerc6afd4b2016-05-13 22:44:57 +000073void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
JF Bastienb9073fb2015-07-22 21:28:15 +000074 // If we have a custom node, we already have selected!
75 if (Node->isMachineOpcode()) {
Nicola Zaghend34e60c2018-05-14 12:53:11 +000076 LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
JF Bastienb9073fb2015-07-22 21:28:15 +000077 Node->setNodeId(-1);
Justin Bognerc6afd4b2016-05-13 22:44:57 +000078 return;
JF Bastienb9073fb2015-07-22 21:28:15 +000079 }
80
Heejin Ahn55146582019-05-28 22:09:12 +000081 // Few custom selection stuff.
82 SDLoc DL(Node);
83 MachineFunction &MF = CurDAG->getMachineFunction();
JF Bastienb9073fb2015-07-22 21:28:15 +000084 switch (Node->getOpcode()) {
Heejin Ahn55146582019-05-28 22:09:12 +000085 case ISD::ATOMIC_FENCE: {
86 if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())
87 break;
88
89 uint64_t SyncScopeID =
90 cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue();
91 switch (SyncScopeID) {
92 case SyncScope::SingleThread: {
93 // We lower a single-thread fence to a pseudo compiler barrier instruction
94 // preventing instruction reordering. This will not be emitted in final
95 // binary.
96 MachineSDNode *Fence =
97 CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,
98 DL, // debug loc
99 MVT::Other, // outchain type
100 Node->getOperand(0) // inchain
101 );
102 ReplaceNode(Node, Fence);
103 CurDAG->RemoveDeadNode(Node);
104 return;
105 }
106
107 case SyncScope::System: {
108 // For non-emscripten systems, we have not decided on what we should
109 // traslate fences to yet.
110 if (!Subtarget->getTargetTriple().isOSEmscripten())
111 report_fatal_error(
112 "ATOMIC_FENCE is not yet supported in non-emscripten OSes");
113
114 // Wasm does not have a fence instruction, but because all atomic
115 // instructions in wasm are sequentially consistent, we translate a
116 // fence to an idempotent atomic RMW instruction to a linear memory
117 // address. All atomic instructions in wasm are sequentially consistent,
118 // but this is to ensure a fence also prevents reordering of non-atomic
119 // instructions in the VM. Even though LLVM IR's fence instruction does
120 // not say anything about its relationship with non-atomic instructions,
121 // we think this is more user-friendly.
122 //
123 // While any address can work, here we use a value stored in
124 // __stack_pointer wasm global because there's high chance that area is
125 // in cache.
126 //
127 // So the selected instructions will be in the form of:
128 // %addr = get_global $__stack_pointer
129 // %0 = i32.const 0
130 // i32.atomic.rmw.or %addr, %0
131 SDValue StackPtrSym = CurDAG->getTargetExternalSymbol(
132 "__stack_pointer", TLI->getPointerTy(CurDAG->getDataLayout()));
133 MachineSDNode *GetGlobal =
134 CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, // opcode
135 DL, // debug loc
136 MVT::i32, // result type
137 StackPtrSym // __stack_pointer symbol
138 );
139
140 SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32);
141 auto *MMO = MF.getMachineMemOperand(
142 MachinePointerInfo::getUnknownStack(MF),
143 // FIXME Volatile isn't really correct, but currently all LLVM
144 // atomic instructions are treated as volatiles in the backend, so
145 // we should be consistent.
146 MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad |
147 MachineMemOperand::MOStore,
148 4, 4, AAMDNodes(), nullptr, SyncScope::System,
149 AtomicOrdering::SequentiallyConsistent);
150 MachineSDNode *Const0 =
151 CurDAG->getMachineNode(WebAssembly::CONST_I32, DL, MVT::i32, Zero);
152 MachineSDNode *AtomicRMW = CurDAG->getMachineNode(
153 WebAssembly::ATOMIC_RMW_OR_I32, // opcode
154 DL, // debug loc
155 MVT::i32, // result type
156 MVT::Other, // outchain type
157 {
158 Zero, // alignment
159 Zero, // offset
160 SDValue(GetGlobal, 0), // __stack_pointer
161 SDValue(Const0, 0), // OR with 0 to make it idempotent
162 Node->getOperand(0) // inchain
163 });
164
165 CurDAG->setNodeMemRefs(AtomicRMW, {MMO});
166 ReplaceUses(SDValue(Node, 0), SDValue(AtomicRMW, 1));
167 CurDAG->RemoveDeadNode(Node);
168 return;
169 }
170 default:
171 llvm_unreachable("Unknown scope!");
172 }
173 }
174
Guanzhong Chen42bba4b2019-07-16 22:00:45 +0000175 case ISD::GlobalTLSAddress: {
176 const auto *GA = cast<GlobalAddressSDNode>(Node);
177
178 if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
179 report_fatal_error("cannot use thread-local storage without bulk memory",
180 false);
181
Guanzhong Chen0a8d4df2019-07-16 22:22:08 +0000182 // Currently Emscripten does not support dynamic linking with threads.
183 // Therefore, if we have thread-local storage, only the local-exec model
184 // is possible.
185 // TODO: remove this and implement proper TLS models once Emscripten
186 // supports dynamic linking with threads.
Guanzhong Chen42bba4b2019-07-16 22:00:45 +0000187 if (GA->getGlobal()->getThreadLocalMode() !=
Guanzhong Chen0a8d4df2019-07-16 22:22:08 +0000188 GlobalValue::LocalExecTLSModel &&
189 !Subtarget->getTargetTriple().isOSEmscripten()) {
190 report_fatal_error("only -ftls-model=local-exec is supported for now on "
191 "non-Emscripten OSes: variable " +
192 GA->getGlobal()->getName(),
Guanzhong Chen42bba4b2019-07-16 22:00:45 +0000193 false);
194 }
195
196 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
197 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
198
199 SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT);
200 SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress(
201 GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0);
202
203 MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32,
204 DL, MVT::i32, TLSBaseSym);
205 MachineSDNode *TLSOffset = CurDAG->getMachineNode(
206 WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym);
207 MachineSDNode *TLSAddress =
208 CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32,
209 SDValue(TLSBase, 0), SDValue(TLSOffset, 0));
210 ReplaceNode(Node, TLSAddress);
211 return;
212 }
213
214 case ISD::INTRINSIC_WO_CHAIN: {
215 unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue();
216 switch (IntNo) {
217 case Intrinsic::wasm_tls_size: {
218 MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());
219 assert(PtrVT == MVT::i32 && "only wasm32 is supported for now");
220
221 MachineSDNode *TLSSize = CurDAG->getMachineNode(
222 WebAssembly::GLOBAL_GET_I32, DL, PtrVT,
223 CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32));
224 ReplaceNode(Node, TLSSize);
225 return;
226 }
227 }
228 break;
229 }
230
JF Bastienb9073fb2015-07-22 21:28:15 +0000231 default:
232 break;
JF Bastienb9073fb2015-07-22 21:28:15 +0000233 }
234
235 // Select the default instruction.
Justin Bognerc6afd4b2016-05-13 22:44:57 +0000236 SelectCode(Node);
Dan Gohman10e730a2015-06-29 23:51:55 +0000237}
238
Dan Gohmanf19ed562015-11-13 01:42:29 +0000239bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(
240 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
241 switch (ConstraintID) {
242 case InlineAsm::Constraint_i:
243 case InlineAsm::Constraint_m:
244 // We just support simple memory operands that just have a single address
245 // operand and need no special handling.
246 OutOps.push_back(Op);
247 return false;
248 default:
249 break;
250 }
251
252 return true;
253}
254
Dan Gohman10e730a2015-06-29 23:51:55 +0000255/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
256/// for instruction scheduling.
257FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
258 CodeGenOpt::Level OptLevel) {
259 return new WebAssemblyDAGToDAGISel(TM, OptLevel);
260}