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 "TargetInfo/WebAssemblyTargetInfo.h" |
17 | #include "Utils/WebAssemblyTypeUtilities.h" |
18 | #include "WebAssemblyAsmPrinter.h" |
19 | #include "WebAssemblyISelLowering.h" |
20 | #include "WebAssemblyMachineFunctionInfo.h" |
21 | #include "WebAssemblyUtilities.h" |
22 | #include "llvm/CodeGen/AsmPrinter.h" |
23 | #include "llvm/CodeGen/MachineFunction.h" |
24 | #include "llvm/IR/Constants.h" |
25 | #include "llvm/MC/MCAsmInfo.h" |
26 | #include "llvm/MC/MCContext.h" |
27 | #include "llvm/MC/MCExpr.h" |
28 | #include "llvm/MC/MCInst.h" |
29 | #include "llvm/MC/MCSymbolWasm.h" |
30 | #include "llvm/Support/ErrorHandling.h" |
31 | #include "llvm/Support/raw_ostream.h" |
32 | |
33 | using namespace llvm; |
34 | |
35 | // This disables the removal of registers when lowering into MC, as required |
36 | // by some current tests. |
37 | cl::opt<bool> |
38 | WasmKeepRegisters("wasm-keep-registers" , cl::Hidden, |
39 | cl::desc("WebAssembly: output stack registers in" |
40 | " instruction output for test purposes only." ), |
41 | cl::init(Val: false)); |
42 | |
43 | static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI); |
44 | |
45 | MCSymbol * |
46 | WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { |
47 | const GlobalValue *Global = MO.getGlobal(); |
48 | if (!isa<Function>(Val: Global)) { |
49 | auto *WasmSym = cast<MCSymbolWasm>(Val: Printer.getSymbol(GV: Global)); |
50 | // If the symbol doesn't have an explicit WasmSymbolType yet and the |
51 | // GlobalValue is actually a WebAssembly global, then ensure the symbol is a |
52 | // WASM_SYMBOL_TYPE_GLOBAL. |
53 | if (WebAssembly::isWasmVarAddressSpace(AS: Global->getAddressSpace()) && |
54 | !WasmSym->getType()) { |
55 | const MachineFunction &MF = *MO.getParent()->getParent()->getParent(); |
56 | const TargetMachine &TM = MF.getTarget(); |
57 | const Function &CurrentFunc = MF.getFunction(); |
58 | Type *GlobalVT = Global->getValueType(); |
59 | SmallVector<MVT, 1> VTs; |
60 | computeLegalValueVTs(F: CurrentFunc, TM, Ty: GlobalVT, ValueVTs&: VTs); |
61 | |
62 | WebAssembly::wasmSymbolSetType(Sym: WasmSym, GlobalVT, VTs); |
63 | } |
64 | return WasmSym; |
65 | } |
66 | |
67 | const auto *FuncTy = cast<FunctionType>(Val: Global->getValueType()); |
68 | const MachineFunction &MF = *MO.getParent()->getParent()->getParent(); |
69 | const TargetMachine &TM = MF.getTarget(); |
70 | const Function &CurrentFunc = MF.getFunction(); |
71 | |
72 | SmallVector<MVT, 1> ResultMVTs; |
73 | SmallVector<MVT, 4> ParamMVTs; |
74 | const auto *const F = dyn_cast<Function>(Val: Global); |
75 | computeSignatureVTs(Ty: FuncTy, TargetFunc: F, ContextFunc: CurrentFunc, TM, Params&: ParamMVTs, Results&: ResultMVTs); |
76 | auto Signature = signatureFromMVTs(Ctx, Results: ResultMVTs, Params: ParamMVTs); |
77 | |
78 | bool InvokeDetected = false; |
79 | auto *WasmSym = Printer.getMCSymbolForFunction( |
80 | F, EnableEmEH: WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj, |
81 | Sig: Signature, InvokeDetected); |
82 | WasmSym->setSignature(Signature); |
83 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); |
84 | return WasmSym; |
85 | } |
86 | |
87 | MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol( |
88 | const MachineOperand &MO) const { |
89 | return Printer.getOrCreateWasmSymbol(Name: MO.getSymbolName()); |
90 | } |
91 | |
92 | MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO, |
93 | MCSymbol *Sym) const { |
94 | MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; |
95 | unsigned TargetFlags = MO.getTargetFlags(); |
96 | |
97 | switch (TargetFlags) { |
98 | case WebAssemblyII::MO_NO_FLAG: |
99 | break; |
100 | case WebAssemblyII::MO_GOT_TLS: |
101 | Kind = MCSymbolRefExpr::VK_WASM_GOT_TLS; |
102 | break; |
103 | case WebAssemblyII::MO_GOT: |
104 | Kind = MCSymbolRefExpr::VK_GOT; |
105 | break; |
106 | case WebAssemblyII::MO_MEMORY_BASE_REL: |
107 | Kind = MCSymbolRefExpr::VK_WASM_MBREL; |
108 | break; |
109 | case WebAssemblyII::MO_TLS_BASE_REL: |
110 | Kind = MCSymbolRefExpr::VK_WASM_TLSREL; |
111 | break; |
112 | case WebAssemblyII::MO_TABLE_BASE_REL: |
113 | Kind = MCSymbolRefExpr::VK_WASM_TBREL; |
114 | break; |
115 | default: |
116 | llvm_unreachable("Unknown target flag on GV operand" ); |
117 | } |
118 | |
119 | const MCExpr *Expr = MCSymbolRefExpr::create(Symbol: Sym, Kind, Ctx); |
120 | |
121 | if (MO.getOffset() != 0) { |
122 | const auto *WasmSym = cast<MCSymbolWasm>(Val: Sym); |
123 | if (TargetFlags == WebAssemblyII::MO_GOT) |
124 | report_fatal_error(reason: "GOT symbol references do not support offsets" ); |
125 | if (WasmSym->isFunction()) |
126 | report_fatal_error(reason: "Function addresses with offsets not supported" ); |
127 | if (WasmSym->isGlobal()) |
128 | report_fatal_error(reason: "Global indexes with offsets not supported" ); |
129 | if (WasmSym->isTag()) |
130 | report_fatal_error(reason: "Tag indexes with offsets not supported" ); |
131 | if (WasmSym->isTable()) |
132 | report_fatal_error(reason: "Table indexes with offsets not supported" ); |
133 | |
134 | Expr = MCBinaryExpr::createAdd( |
135 | LHS: Expr, RHS: MCConstantExpr::create(Value: MO.getOffset(), Ctx), Ctx); |
136 | } |
137 | |
138 | return MCOperand::createExpr(Val: Expr); |
139 | } |
140 | |
141 | MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand( |
142 | SmallVectorImpl<wasm::ValType> &&Returns, |
143 | SmallVectorImpl<wasm::ValType> &&Params) const { |
144 | auto Signature = Ctx.createWasmSignature(); |
145 | Signature->Returns = std::move(Returns); |
146 | Signature->Params = std::move(Params); |
147 | MCSymbol *Sym = Printer.createTempSymbol(Name: "typeindex" ); |
148 | auto *WasmSym = cast<MCSymbolWasm>(Val: Sym); |
149 | WasmSym->setSignature(Signature); |
150 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); |
151 | const MCExpr *Expr = |
152 | MCSymbolRefExpr::create(Symbol: WasmSym, Kind: MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx); |
153 | return MCOperand::createExpr(Val: Expr); |
154 | } |
155 | |
156 | static void getFunctionReturns(const MachineInstr *MI, |
157 | SmallVectorImpl<wasm::ValType> &Returns) { |
158 | const Function &F = MI->getMF()->getFunction(); |
159 | const TargetMachine &TM = MI->getMF()->getTarget(); |
160 | Type *RetTy = F.getReturnType(); |
161 | SmallVector<MVT, 4> CallerRetTys; |
162 | computeLegalValueVTs(F, TM, Ty: RetTy, ValueVTs&: CallerRetTys); |
163 | valTypesFromMVTs(In: CallerRetTys, Out&: Returns); |
164 | } |
165 | |
166 | void WebAssemblyMCInstLower::lower(const MachineInstr *MI, |
167 | MCInst &OutMI) const { |
168 | OutMI.setOpcode(MI->getOpcode()); |
169 | |
170 | const MCInstrDesc &Desc = MI->getDesc(); |
171 | unsigned NumVariadicDefs = MI->getNumExplicitDefs() - Desc.getNumDefs(); |
172 | for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) { |
173 | const MachineOperand &MO = MI->getOperand(i: I); |
174 | |
175 | MCOperand MCOp; |
176 | switch (MO.getType()) { |
177 | default: |
178 | MI->print(OS&: errs()); |
179 | llvm_unreachable("unknown operand type" ); |
180 | case MachineOperand::MO_MachineBasicBlock: |
181 | MI->print(OS&: errs()); |
182 | llvm_unreachable("MachineBasicBlock operand should have been rewritten" ); |
183 | case MachineOperand::MO_Register: { |
184 | // Ignore all implicit register operands. |
185 | if (MO.isImplicit()) |
186 | continue; |
187 | const WebAssemblyFunctionInfo &MFI = |
188 | *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>(); |
189 | unsigned WAReg = MFI.getWAReg(VReg: MO.getReg()); |
190 | MCOp = MCOperand::createReg(Reg: WAReg); |
191 | break; |
192 | } |
193 | case MachineOperand::MO_Immediate: { |
194 | unsigned DescIndex = I - NumVariadicDefs; |
195 | if (DescIndex < Desc.NumOperands) { |
196 | const MCOperandInfo &Info = Desc.operands()[DescIndex]; |
197 | if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) { |
198 | SmallVector<wasm::ValType, 4> Returns; |
199 | SmallVector<wasm::ValType, 4> Params; |
200 | |
201 | const MachineRegisterInfo &MRI = |
202 | MI->getParent()->getParent()->getRegInfo(); |
203 | for (const MachineOperand &MO : MI->defs()) |
204 | Returns.push_back(Elt: WebAssembly::regClassToValType( |
205 | RC: MRI.getRegClass(Reg: MO.getReg())->getID())); |
206 | for (const MachineOperand &MO : MI->explicit_uses()) |
207 | if (MO.isReg()) |
208 | Params.push_back(Elt: WebAssembly::regClassToValType( |
209 | RC: MRI.getRegClass(Reg: MO.getReg())->getID())); |
210 | |
211 | // call_indirect instructions have a callee operand at the end which |
212 | // doesn't count as a param. |
213 | if (WebAssembly::isCallIndirect(Opc: MI->getOpcode())) |
214 | Params.pop_back(); |
215 | |
216 | // return_call_indirect instructions have the return type of the |
217 | // caller |
218 | if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT) |
219 | getFunctionReturns(MI, Returns); |
220 | |
221 | MCOp = lowerTypeIndexOperand(Returns: std::move(Returns), Params: std::move(Params)); |
222 | break; |
223 | } else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) { |
224 | auto BT = static_cast<WebAssembly::BlockType>(MO.getImm()); |
225 | assert(BT != WebAssembly::BlockType::Invalid); |
226 | if (BT == WebAssembly::BlockType::Multivalue) { |
227 | SmallVector<wasm::ValType, 1> Returns; |
228 | getFunctionReturns(MI, Returns); |
229 | MCOp = lowerTypeIndexOperand(Returns: std::move(Returns), |
230 | Params: SmallVector<wasm::ValType, 4>()); |
231 | break; |
232 | } |
233 | } |
234 | } |
235 | MCOp = MCOperand::createImm(Val: MO.getImm()); |
236 | break; |
237 | } |
238 | case MachineOperand::MO_FPImmediate: { |
239 | const ConstantFP *Imm = MO.getFPImm(); |
240 | const uint64_t BitPattern = |
241 | Imm->getValueAPF().bitcastToAPInt().getZExtValue(); |
242 | if (Imm->getType()->isFloatTy()) |
243 | MCOp = MCOperand::createSFPImm(Val: static_cast<uint32_t>(BitPattern)); |
244 | else if (Imm->getType()->isDoubleTy()) |
245 | MCOp = MCOperand::createDFPImm(Val: BitPattern); |
246 | else |
247 | llvm_unreachable("unknown floating point immediate type" ); |
248 | break; |
249 | } |
250 | case MachineOperand::MO_GlobalAddress: |
251 | MCOp = lowerSymbolOperand(MO, Sym: GetGlobalAddressSymbol(MO)); |
252 | break; |
253 | case MachineOperand::MO_ExternalSymbol: |
254 | MCOp = lowerSymbolOperand(MO, Sym: GetExternalSymbolSymbol(MO)); |
255 | break; |
256 | case MachineOperand::MO_MCSymbol: |
257 | assert(MO.getTargetFlags() == 0 && |
258 | "WebAssembly does not use target flags on MCSymbol" ); |
259 | MCOp = lowerSymbolOperand(MO, Sym: MO.getMCSymbol()); |
260 | break; |
261 | } |
262 | |
263 | OutMI.addOperand(Op: MCOp); |
264 | } |
265 | |
266 | if (!WasmKeepRegisters) |
267 | removeRegisterOperands(MI, OutMI); |
268 | else if (Desc.variadicOpsAreDefs()) |
269 | OutMI.insert(I: OutMI.begin(), Op: MCOperand::createImm(Val: MI->getNumExplicitDefs())); |
270 | } |
271 | |
272 | static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) { |
273 | // Remove all uses of stackified registers to bring the instruction format |
274 | // into its final stack form used thruout MC, and transition opcodes to |
275 | // their _S variant. |
276 | // We do this separate from the above code that still may need these |
277 | // registers for e.g. call_indirect signatures. |
278 | // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for |
279 | // details. |
280 | // TODO: the code above creates new registers which are then removed here. |
281 | // That code could be slightly simplified by not doing that, though maybe |
282 | // it is simpler conceptually to keep the code above in "register mode" |
283 | // until this transition point. |
284 | // FIXME: we are not processing inline assembly, which contains register |
285 | // operands, because it is used by later target generic code. |
286 | if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm()) |
287 | return; |
288 | |
289 | // Transform to _S instruction. |
290 | auto RegOpcode = OutMI.getOpcode(); |
291 | auto StackOpcode = WebAssembly::getStackOpcode(Opcode: RegOpcode); |
292 | assert(StackOpcode != -1 && "Failed to stackify instruction" ); |
293 | OutMI.setOpcode(StackOpcode); |
294 | |
295 | // Remove register operands. |
296 | for (auto I = OutMI.getNumOperands(); I; --I) { |
297 | auto &MO = OutMI.getOperand(i: I - 1); |
298 | if (MO.isReg()) { |
299 | OutMI.erase(I: &MO); |
300 | } |
301 | } |
302 | } |
303 | |