1// WebAssemblyMCInstLower.cpp - Convert WebAssembly MachineInstr to an MCInst //
2//
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
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file contains code to lower WebAssembly MachineInstrs to their
11/// corresponding MCInst records.
12///
13//===----------------------------------------------------------------------===//
14
15#include "WebAssemblyMCInstLower.h"
16#include "MCTargetDesc/WebAssemblyMCAsmInfo.h"
17#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
18#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"
19#include "TargetInfo/WebAssemblyTargetInfo.h"
20#include "Utils/WebAssemblyTypeUtilities.h"
21#include "WebAssemblyAsmPrinter.h"
22#include "WebAssemblyMachineFunctionInfo.h"
23#include "WebAssemblyUtilities.h"
24#include "llvm/ADT/APInt.h"
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/BinaryFormat/Wasm.h"
27#include "llvm/CodeGen/AsmPrinter.h"
28#include "llvm/CodeGen/MachineFunction.h"
29#include "llvm/CodeGen/MachineOperand.h"
30#include "llvm/IR/Constants.h"
31#include "llvm/MC/MCAsmInfo.h"
32#include "llvm/MC/MCContext.h"
33#include "llvm/MC/MCExpr.h"
34#include "llvm/MC/MCInst.h"
35#include "llvm/MC/MCSymbolWasm.h"
36#include "llvm/Support/ErrorHandling.h"
37#include "llvm/Support/raw_ostream.h"
38
39using namespace llvm;
40
41// This disables the removal of registers when lowering into MC, as required
42// by some current tests.
43static cl::opt<bool>
44 WasmKeepRegisters("wasm-keep-registers", cl::Hidden,
45 cl::desc("WebAssembly: output stack registers in"
46 " instruction output for test purposes only."),
47 cl::init(Val: false));
48
49static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
50
51MCSymbol *
52WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
53 const GlobalValue *Global = MO.getGlobal();
54 if (!isa<Function>(Val: Global)) {
55 auto *WasmSym = static_cast<MCSymbolWasm *>(Printer.getSymbol(GV: Global));
56 // If the symbol doesn't have an explicit WasmSymbolType yet and the
57 // GlobalValue is actually a WebAssembly global, then ensure the symbol is a
58 // WASM_SYMBOL_TYPE_GLOBAL.
59 if (WebAssembly::isWasmVarAddressSpace(AS: Global->getAddressSpace()) &&
60 !WasmSym->getType()) {
61 const MachineFunction &MF = *MO.getParent()->getParent()->getParent();
62 const TargetMachine &TM = MF.getTarget();
63 const Function &CurrentFunc = MF.getFunction();
64 Type *GlobalVT = Global->getValueType();
65 SmallVector<MVT, 1> VTs;
66 computeLegalValueVTs(F: CurrentFunc, TM, Ty: GlobalVT, ValueVTs&: VTs);
67
68 WebAssembly::wasmSymbolSetType(Sym: WasmSym, GlobalVT, VTs);
69 }
70 return WasmSym;
71 }
72
73 const auto *FuncTy = cast<FunctionType>(Val: Global->getValueType());
74 const MachineFunction &MF = *MO.getParent()->getParent()->getParent();
75 const TargetMachine &TM = MF.getTarget();
76 const Function &CurrentFunc = MF.getFunction();
77
78 SmallVector<MVT, 1> ResultMVTs;
79 SmallVector<MVT, 4> ParamMVTs;
80 const auto *const F = dyn_cast<Function>(Val: Global);
81 computeSignatureVTs(Ty: FuncTy, TargetFunc: F, ContextFunc: CurrentFunc, TM, Params&: ParamMVTs, Results&: ResultMVTs);
82 auto Signature = signatureFromMVTs(Ctx, Results: ResultMVTs, Params: ParamMVTs);
83
84 bool InvokeDetected = false;
85 auto *WasmSym = Printer.getMCSymbolForFunction(F, Sig: Signature, InvokeDetected);
86 WasmSym->setSignature(Signature);
87 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
88 return WasmSym;
89}
90
91MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
92 const MachineOperand &MO) const {
93 return Printer.getOrCreateWasmSymbol(Name: MO.getSymbolName());
94}
95
96MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
97 MCSymbol *Sym) const {
98 auto Spec = WebAssembly::S_None;
99 unsigned TargetFlags = MO.getTargetFlags();
100
101 switch (TargetFlags) {
102 case WebAssemblyII::MO_NO_FLAG:
103 break;
104 case WebAssemblyII::MO_GOT_TLS:
105 Spec = WebAssembly::S_GOT_TLS;
106 break;
107 case WebAssemblyII::MO_GOT:
108 Spec = WebAssembly::S_GOT;
109 break;
110 case WebAssemblyII::MO_MEMORY_BASE_REL:
111 Spec = WebAssembly::S_MBREL;
112 break;
113 case WebAssemblyII::MO_TLS_BASE_REL:
114 Spec = WebAssembly::S_TLSREL;
115 break;
116 case WebAssemblyII::MO_TABLE_BASE_REL:
117 Spec = WebAssembly::S_TBREL;
118 break;
119 default:
120 llvm_unreachable("Unknown target flag on GV operand");
121 }
122
123 const MCExpr *Expr = MCSymbolRefExpr::create(Symbol: Sym, specifier: Spec, Ctx);
124
125 if (MO.getOffset() != 0) {
126 const auto *WasmSym = static_cast<const MCSymbolWasm *>(Sym);
127 if (TargetFlags == WebAssemblyII::MO_GOT)
128 report_fatal_error(reason: "GOT symbol references do not support offsets");
129 if (WasmSym->isFunction())
130 report_fatal_error(reason: "Function addresses with offsets not supported");
131 if (WasmSym->isGlobal())
132 report_fatal_error(reason: "Global indexes with offsets not supported");
133 if (WasmSym->isTag())
134 report_fatal_error(reason: "Tag indexes with offsets not supported");
135 if (WasmSym->isTable())
136 report_fatal_error(reason: "Table indexes with offsets not supported");
137
138 Expr = MCBinaryExpr::createAdd(
139 LHS: Expr, RHS: MCConstantExpr::create(Value: MO.getOffset(), Ctx), Ctx);
140 }
141
142 return MCOperand::createExpr(Val: Expr);
143}
144
145MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand(
146 SmallVectorImpl<wasm::ValType> &&Returns,
147 SmallVectorImpl<wasm::ValType> &&Params) const {
148 auto Signature = Ctx.createWasmSignature();
149 Signature->Returns = std::move(Returns);
150 Signature->Params = std::move(Params);
151 auto *Sym =
152 static_cast<MCSymbolWasm *>(Printer.createTempSymbol(Name: "typeindex"));
153 Sym->setSignature(Signature);
154 Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
155 const MCExpr *Expr =
156 MCSymbolRefExpr::create(Symbol: Sym, specifier: WebAssembly::S_TYPEINDEX, Ctx);
157 return MCOperand::createExpr(Val: Expr);
158}
159
160MCOperand
161WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const {
162 // For APInt a word is 64 bits on all architectures, see definition in APInt.h
163 auto NumWords = Sig.getNumWords();
164 SmallVector<wasm::ValType, 4> Params;
165 SmallVector<wasm::ValType, 2> Returns;
166
167 int Idx = NumWords;
168 auto GetWord = [&Idx, &Sig]() {
169 Idx--;
170 return Sig.extractBitsAsZExtValue(numBits: 64, bitPosition: 64 * Idx);
171 };
172 // Annoying special case: if getSignificantBits() <= 64 then InstrEmitter will
173 // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we
174 // always emit a CImm. So xor NParams with 0x7ffffff to ensure
175 // getSignificantBits() > 64
176 // See encodeFunctionSignature in WebAssemblyISelDAGtoDAG.cpp
177 int NReturns = GetWord() ^ 0x7ffffff;
178 for (int I = 0; I < NReturns; I++) {
179 Returns.push_back(Elt: static_cast<wasm::ValType>(GetWord()));
180 }
181 int NParams = GetWord();
182 for (int I = 0; I < NParams; I++) {
183 Params.push_back(Elt: static_cast<wasm::ValType>(GetWord()));
184 }
185 return lowerTypeIndexOperand(Returns: std::move(Returns), Params: std::move(Params));
186}
187
188static void getFunctionReturns(const MachineInstr *MI,
189 SmallVectorImpl<wasm::ValType> &Returns) {
190 const Function &F = MI->getMF()->getFunction();
191 const TargetMachine &TM = MI->getMF()->getTarget();
192 Type *RetTy = F.getReturnType();
193 SmallVector<MVT, 4> CallerRetTys;
194 computeLegalValueVTs(F, TM, Ty: RetTy, ValueVTs&: CallerRetTys);
195 valTypesFromMVTs(In: CallerRetTys, Out&: Returns);
196}
197
198void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
199 MCInst &OutMI) const {
200 OutMI.setOpcode(MI->getOpcode());
201
202 const MCInstrDesc &Desc = MI->getDesc();
203 unsigned NumVariadicDefs = MI->getNumExplicitDefs() - Desc.getNumDefs();
204 const MachineFunction *MF = MI->getMF();
205 const auto &TLI =
206 *MF->getSubtarget<WebAssemblySubtarget>().getTargetLowering();
207 wasm::ValType PtrTy = TLI.getPointerTy(DL: MF->getDataLayout()) == MVT::i32
208 ? wasm::ValType::I32
209 : wasm::ValType::I64;
210
211 for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {
212 const MachineOperand &MO = MI->getOperand(i: I);
213
214 MCOperand MCOp;
215 switch (MO.getType()) {
216 default:
217 MI->print(OS&: errs());
218 llvm_unreachable("unknown operand type");
219 case MachineOperand::MO_MachineBasicBlock:
220 MI->print(OS&: errs());
221 llvm_unreachable("MachineBasicBlock operand should have been rewritten");
222 case MachineOperand::MO_Register: {
223 // Ignore all implicit register operands.
224 if (MO.isImplicit())
225 continue;
226 const WebAssemblyFunctionInfo &MFI =
227 *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>();
228 unsigned WAReg = MFI.getWAReg(VReg: MO.getReg());
229 MCOp = MCOperand::createReg(Reg: WAReg);
230 break;
231 }
232 case llvm::MachineOperand::MO_CImmediate: {
233 // Lower type index placeholder for ref.test
234 // Currently this is the only way that CImmediates show up so panic if we
235 // get confused.
236 unsigned DescIndex = I - NumVariadicDefs;
237 assert(DescIndex < Desc.NumOperands && "unexpected CImmediate operand");
238 auto Operands = Desc.operands();
239 const MCOperandInfo &Info = Operands[DescIndex];
240 assert(Info.OperandType == WebAssembly::OPERAND_TYPEINDEX &&
241 "unexpected CImmediate operand");
242 (void)Info;
243 MCOp = lowerEncodedFunctionSignature(Sig: MO.getCImm()->getValue());
244 break;
245 }
246 case MachineOperand::MO_Immediate: {
247 unsigned DescIndex = I - NumVariadicDefs;
248 if (DescIndex < Desc.NumOperands) {
249 auto Operands = Desc.operands();
250 const MCOperandInfo &Info = Operands[DescIndex];
251 // Replace type index placeholder with actual type index. The type index
252 // placeholders are Immediates and have an operand type of
253 // OPERAND_TYPEINDEX or OPERAND_SIGNATURE.
254 if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
255 // Lower type index placeholder for a CALL_INDIRECT instruction
256 SmallVector<wasm::ValType, 4> Returns;
257 SmallVector<wasm::ValType, 4> Params;
258
259 const MachineRegisterInfo &MRI =
260 MI->getParent()->getParent()->getRegInfo();
261 for (const MachineOperand &MO : MI->defs())
262 Returns.push_back(Elt: WebAssembly::regClassToValType(
263 RC: MRI.getRegClass(Reg: MO.getReg())->getID()));
264 for (const MachineOperand &MO : MI->explicit_uses())
265 if (MO.isReg())
266 Params.push_back(Elt: WebAssembly::regClassToValType(
267 RC: MRI.getRegClass(Reg: MO.getReg())->getID()));
268
269 // call_indirect instructions have a callee operand at the end which
270 // doesn't count as a param.
271 if (WebAssembly::isCallIndirect(Opc: MI->getOpcode()))
272 Params.pop_back();
273
274 // return_call_indirect instructions have the return type of the
275 // caller
276 if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)
277 getFunctionReturns(MI, Returns);
278
279 MCOp = lowerTypeIndexOperand(Returns: std::move(Returns), Params: std::move(Params));
280 break;
281 }
282 if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
283 // Lower type index placeholder for blocks
284 auto BT = static_cast<WebAssembly::BlockType>(MO.getImm());
285 assert(BT != WebAssembly::BlockType::Invalid);
286 if (BT == WebAssembly::BlockType::Multivalue) {
287 SmallVector<wasm::ValType, 2> Returns;
288 // Multivalue blocks are emitted in two cases:
289 // 1. When the blocks will never be exited and are at the ends of
290 // functions (see
291 // WebAssemblyCFGStackify::fixEndsAtEndOfFunction). In this case
292 // the exact multivalue signature can always be inferred from the
293 // return type of the parent function.
294 // 2. (catch_ref ...) clause in try_table instruction. Currently all
295 // tags we support (cpp_exception and c_longjmp) throws a single
296 // pointer, so the multivalue signature for this case will be
297 // (ptr, exnref). Having MO_CATCH_BLOCK_SIG target flags means
298 // this is a destination of a catch_ref.
299 if (MO.getTargetFlags() == WebAssemblyII::MO_CATCH_BLOCK_SIG) {
300 Returns = {PtrTy, wasm::ValType::EXNREF};
301 } else
302 getFunctionReturns(MI, Returns);
303 MCOp = lowerTypeIndexOperand(Returns: std::move(Returns),
304 Params: SmallVector<wasm::ValType, 4>());
305 break;
306 }
307 }
308 }
309 MCOp = MCOperand::createImm(Val: MO.getImm());
310 break;
311 }
312 case MachineOperand::MO_FPImmediate: {
313 const ConstantFP *Imm = MO.getFPImm();
314 const uint64_t BitPattern =
315 Imm->getValueAPF().bitcastToAPInt().getZExtValue();
316 if (Imm->getType()->isFloatTy())
317 MCOp = MCOperand::createSFPImm(Val: static_cast<uint32_t>(BitPattern));
318 else if (Imm->getType()->isDoubleTy())
319 MCOp = MCOperand::createDFPImm(Val: BitPattern);
320 else
321 llvm_unreachable("unknown floating point immediate type");
322 break;
323 }
324 case MachineOperand::MO_GlobalAddress:
325 MCOp = lowerSymbolOperand(MO, Sym: GetGlobalAddressSymbol(MO));
326 break;
327 case MachineOperand::MO_ExternalSymbol:
328 MCOp = lowerSymbolOperand(MO, Sym: GetExternalSymbolSymbol(MO));
329 break;
330 case MachineOperand::MO_MCSymbol:
331 assert(MO.getTargetFlags() == 0 &&
332 "WebAssembly does not use target flags on MCSymbol");
333 MCOp = lowerSymbolOperand(MO, Sym: MO.getMCSymbol());
334 break;
335 }
336
337 OutMI.addOperand(Op: MCOp);
338 }
339
340 if (!WasmKeepRegisters)
341 removeRegisterOperands(MI, OutMI);
342 else if (Desc.variadicOpsAreDefs())
343 OutMI.insert(I: OutMI.begin(), Op: MCOperand::createImm(Val: MI->getNumExplicitDefs()));
344}
345
346static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
347 // Remove all uses of stackified registers to bring the instruction format
348 // into its final stack form used thruout MC, and transition opcodes to
349 // their _S variant.
350 // We do this separate from the above code that still may need these
351 // registers for e.g. call_indirect signatures.
352 // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for
353 // details.
354 // TODO: the code above creates new registers which are then removed here.
355 // That code could be slightly simplified by not doing that, though maybe
356 // it is simpler conceptually to keep the code above in "register mode"
357 // until this transition point.
358 // FIXME: we are not processing inline assembly, which contains register
359 // operands, because it is used by later target generic code.
360 if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm())
361 return;
362
363 // Transform to _S instruction.
364 auto RegOpcode = OutMI.getOpcode();
365 auto StackOpcode = WebAssembly::getStackOpcode(Opcode: RegOpcode);
366 assert(StackOpcode != -1 && "Failed to stackify instruction");
367 OutMI.setOpcode(StackOpcode);
368
369 // Remove register operands.
370 for (auto I = OutMI.getNumOperands(); I; --I) {
371 auto &MO = OutMI.getOperand(i: I - 1);
372 if (MO.isReg()) {
373 OutMI.erase(I: &MO);
374 }
375 }
376}
377