377 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains code to lower WebAssembly MachineInstrs to their11/// 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 required42// 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(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>(Global)) {55 auto *WasmSym = static_cast<MCSymbolWasm *>(Printer.getSymbol(Global));56 // If the symbol doesn't have an explicit WasmSymbolType yet and the57 // GlobalValue is actually a WebAssembly global, then ensure the symbol is a58 // WASM_SYMBOL_TYPE_GLOBAL.59 if (WebAssembly::isWasmVarAddressSpace(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(CurrentFunc, TM, GlobalVT, VTs);67 68 WebAssembly::wasmSymbolSetType(WasmSym, GlobalVT, VTs);69 }70 return WasmSym;71 }72 73 const auto *FuncTy = cast<FunctionType>(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>(Global);81 computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs);82 auto Signature = signatureFromMVTs(Ctx, ResultMVTs, ParamMVTs);83 84 bool InvokeDetected = false;85 auto *WasmSym = Printer.getMCSymbolForFunction(F, 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(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(Sym, 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("GOT symbol references do not support offsets");129 if (WasmSym->isFunction())130 report_fatal_error("Function addresses with offsets not supported");131 if (WasmSym->isGlobal())132 report_fatal_error("Global indexes with offsets not supported");133 if (WasmSym->isTag())134 report_fatal_error("Tag indexes with offsets not supported");135 if (WasmSym->isTable())136 report_fatal_error("Table indexes with offsets not supported");137 138 Expr = MCBinaryExpr::createAdd(139 Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);140 }141 142 return MCOperand::createExpr(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("typeindex"));153 Sym->setSignature(Signature);154 Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);155 const MCExpr *Expr =156 MCSymbolRefExpr::create(Sym, WebAssembly::S_TYPEINDEX, Ctx);157 return MCOperand::createExpr(Expr);158}159 160MCOperand161WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const {162 // For APInt a word is 64 bits on all architectures, see definition in APInt.h163 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(64, 64 * Idx);171 };172 // Annoying special case: if getSignificantBits() <= 64 then InstrEmitter will173 // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we174 // always emit a CImm. So xor NParams with 0x7ffffff to ensure175 // getSignificantBits() > 64176 // See encodeFunctionSignature in WebAssemblyISelDAGtoDAG.cpp177 int NReturns = GetWord() ^ 0x7ffffff;178 for (int I = 0; I < NReturns; I++) {179 Returns.push_back(static_cast<wasm::ValType>(GetWord()));180 }181 int NParams = GetWord();182 for (int I = 0; I < NParams; I++) {183 Params.push_back(static_cast<wasm::ValType>(GetWord()));184 }185 return lowerTypeIndexOperand(std::move(Returns), 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, RetTy, CallerRetTys);195 valTypesFromMVTs(CallerRetTys, 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(MF->getDataLayout()) == MVT::i32208 ? wasm::ValType::I32209 : wasm::ValType::I64;210 211 for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {212 const MachineOperand &MO = MI->getOperand(I);213 214 MCOperand MCOp;215 switch (MO.getType()) {216 default:217 MI->print(errs());218 llvm_unreachable("unknown operand type");219 case MachineOperand::MO_MachineBasicBlock:220 MI->print(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(MO.getReg());229 MCOp = MCOperand::createReg(WAReg);230 break;231 }232 case llvm::MachineOperand::MO_CImmediate: {233 // Lower type index placeholder for ref.test234 // Currently this is the only way that CImmediates show up so panic if we235 // 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(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 index252 // placeholders are Immediates and have an operand type of253 // OPERAND_TYPEINDEX or OPERAND_SIGNATURE.254 if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {255 // Lower type index placeholder for a CALL_INDIRECT instruction256 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(WebAssembly::regClassToValType(263 MRI.getRegClass(MO.getReg())->getID()));264 for (const MachineOperand &MO : MI->explicit_uses())265 if (MO.isReg())266 Params.push_back(WebAssembly::regClassToValType(267 MRI.getRegClass(MO.getReg())->getID()));268 269 // call_indirect instructions have a callee operand at the end which270 // doesn't count as a param.271 if (WebAssembly::isCallIndirect(MI->getOpcode()))272 Params.pop_back();273 274 // return_call_indirect instructions have the return type of the275 // caller276 if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)277 getFunctionReturns(MI, Returns);278 279 MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params));280 break;281 }282 if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {283 // Lower type index placeholder for blocks284 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 of290 // functions (see291 // WebAssemblyCFGStackify::fixEndsAtEndOfFunction). In this case292 // the exact multivalue signature can always be inferred from the293 // return type of the parent function.294 // 2. (catch_ref ...) clause in try_table instruction. Currently all295 // tags we support (cpp_exception and c_longjmp) throws a single296 // pointer, so the multivalue signature for this case will be297 // (ptr, exnref). Having MO_CATCH_BLOCK_SIG target flags means298 // this is a destination of a catch_ref.299 if (MO.getTargetFlags() == WebAssemblyII::MO_CATCH_BLOCK_SIG) {300 Returns = {PtrTy, wasm::ValType::EXNREF};301 } else302 getFunctionReturns(MI, Returns);303 MCOp = lowerTypeIndexOperand(std::move(Returns),304 SmallVector<wasm::ValType, 4>());305 break;306 }307 }308 }309 MCOp = MCOperand::createImm(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(static_cast<uint32_t>(BitPattern));318 else if (Imm->getType()->isDoubleTy())319 MCOp = MCOperand::createDFPImm(BitPattern);320 else321 llvm_unreachable("unknown floating point immediate type");322 break;323 }324 case MachineOperand::MO_GlobalAddress:325 MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));326 break;327 case MachineOperand::MO_ExternalSymbol:328 MCOp = lowerSymbolOperand(MO, 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, MO.getMCSymbol());334 break;335 }336 337 OutMI.addOperand(MCOp);338 }339 340 if (!WasmKeepRegisters)341 removeRegisterOperands(MI, OutMI);342 else if (Desc.variadicOpsAreDefs())343 OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs()));344}345 346static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {347 // Remove all uses of stackified registers to bring the instruction format348 // into its final stack form used thruout MC, and transition opcodes to349 // their _S variant.350 // We do this separate from the above code that still may need these351 // registers for e.g. call_indirect signatures.352 // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for353 // 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 maybe356 // 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 register359 // 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(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 - 1);372 if (MO.isReg()) {373 OutMI.erase(&MO);374 }375 }376}377