diff options
Diffstat (limited to 'lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp')
| -rw-r--r-- | lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp | 164 |
1 files changed, 118 insertions, 46 deletions
diff --git a/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index d85db14fc679f..fa862fbaa634c 100644 --- a/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -30,6 +30,21 @@ #include "llvm/Support/raw_ostream.h" using namespace llvm; +// Defines llvm::WebAssembly::getStackOpcode to convert register instructions to +// stack instructions +#define GET_INSTRMAP_INFO 1 +#include "WebAssemblyGenInstrInfo.inc" + +// This disables the removal of registers when lowering into MC, as required +// by some current tests. +static cl::opt<bool> + WasmKeepRegisters("wasm-keep-registers", cl::Hidden, + cl::desc("WebAssembly: output stack registers in" + " instruction output for test purposes only."), + cl::init(false)); + +static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI); + MCSymbol * WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { const GlobalValue *Global = MO.getGlobal(); @@ -40,35 +55,13 @@ WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { const TargetMachine &TM = MF.getTarget(); const Function &CurrentFunc = MF.getFunction(); - SmallVector<wasm::ValType, 4> Returns; - SmallVector<wasm::ValType, 4> Params; - - wasm::ValType iPTR = - MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() ? - wasm::ValType::I64 : - wasm::ValType::I32; - - SmallVector<MVT, 4> ResultMVTs; - ComputeLegalValueVTs(CurrentFunc, TM, FuncTy->getReturnType(), ResultMVTs); - // WebAssembly can't currently handle returning tuples. - if (ResultMVTs.size() <= 1) - for (MVT ResultMVT : ResultMVTs) - Returns.push_back(WebAssembly::toValType(ResultMVT)); - else - Params.push_back(iPTR); - - for (Type *Ty : FuncTy->params()) { - SmallVector<MVT, 4> ParamMVTs; - ComputeLegalValueVTs(CurrentFunc, TM, Ty, ParamMVTs); - for (MVT ParamMVT : ParamMVTs) - Params.push_back(WebAssembly::toValType(ParamMVT)); - } - - if (FuncTy->isVarArg()) - Params.push_back(iPTR); + SmallVector<MVT, 1> ResultMVTs; + SmallVector<MVT, 4> ParamMVTs; + ComputeSignatureVTs(FuncTy, CurrentFunc, TM, ParamMVTs, ResultMVTs); - WasmSym->setReturns(std::move(Returns)); - WasmSym->setParams(std::move(Params)); + auto Signature = SignatureFromMVTs(ResultMVTs, ParamMVTs); + WasmSym->setSignature(Signature.get()); + Printer.addSignature(std::move(Signature)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); } @@ -82,10 +75,10 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol( cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name)); const WebAssemblySubtarget &Subtarget = Printer.getSubtarget(); - // __stack_pointer is a global variable; all other external symbols used by - // CodeGen are functions. It's OK to hardcode knowledge of specific symbols - // here; this method is precisely there for fetching the signatures of known - // Clang-provided symbols. + // Except for the two exceptions (__stack_pointer and __cpp_exception), all + // other external symbols used by CodeGen are functions. It's OK to hardcode + // knowledge of specific symbols here; this method is precisely there for + // fetching the signatures of known Clang-provided symbols. if (strcmp(Name, "__stack_pointer") == 0) { WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); WasmSym->setGlobalType(wasm::WasmGlobalType{ @@ -97,27 +90,55 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol( SmallVector<wasm::ValType, 4> Returns; SmallVector<wasm::ValType, 4> Params; - GetSignature(Subtarget, Name, Returns, Params); + if (strcmp(Name, "__cpp_exception") == 0) { + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_EVENT); + // We can't confirm its signature index for now because there can be + // imported exceptions. Set it to be 0 for now. + WasmSym->setEventType( + {wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0}); + // We may have multiple C++ compilation units to be linked together, each of + // which defines the exception symbol. To resolve them, we declare them as + // weak. + WasmSym->setWeak(true); + WasmSym->setExternal(true); - WasmSym->setReturns(std::move(Returns)); - WasmSym->setParams(std::move(Params)); - WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); + // All C++ exceptions are assumed to have a single i32 (for wasm32) or i64 + // (for wasm64) param type and void return type. The reaon is, all C++ + // exception values are pointers, and to share the type section with + // functions, exceptions are assumed to have void return type. + Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64 + : wasm::ValType::I32); + } else { // Function symbols + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); + GetLibcallSignature(Subtarget, Name, Returns, Params); + } + auto Signature = + make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params)); + WasmSym->setSignature(Signature.get()); + Printer.addSignature(std::move(Signature)); return WasmSym; } MCOperand WebAssemblyMCInstLower::LowerSymbolOperand(MCSymbol *Sym, int64_t Offset, - bool IsFunc) const { + bool IsFunc, bool IsGlob, + bool IsEvent) const { MCSymbolRefExpr::VariantKind VK = IsFunc ? MCSymbolRefExpr::VK_WebAssembly_FUNCTION - : MCSymbolRefExpr::VK_None; + : IsGlob ? MCSymbolRefExpr::VK_WebAssembly_GLOBAL + : IsEvent ? MCSymbolRefExpr::VK_WebAssembly_EVENT + : MCSymbolRefExpr::VK_None; const MCExpr *Expr = MCSymbolRefExpr::create(Sym, VK, Ctx); if (Offset != 0) { if (IsFunc) report_fatal_error("Function addresses with offsets not supported"); + if (IsGlob) + report_fatal_error("Global indexes with offsets not supported"); + if (IsEvent) + report_fatal_error("Event indexes with offsets not supported"); Expr = MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Offset, Ctx), Ctx); } @@ -135,6 +156,8 @@ static wasm::ValType getType(const TargetRegisterClass *RC) { return wasm::ValType::F32; if (RC == &WebAssembly::F64RegClass) return wasm::ValType::F64; + if (RC == &WebAssembly::V128RegClass) + return wasm::ValType::V128; llvm_unreachable("Unexpected register class"); } @@ -187,8 +210,10 @@ void WebAssemblyMCInstLower::Lower(const MachineInstr *MI, Params.pop_back(); MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym); - WasmSym->setReturns(std::move(Returns)); - WasmSym->setParams(std::move(Params)); + auto Signature = make_unique<wasm::WasmSignature>(std::move(Returns), + std::move(Params)); + WasmSym->setSignature(Signature.get()); + Printer.addSignature(std::move(Signature)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); const MCExpr *Expr = MCSymbolRefExpr::create( @@ -212,21 +237,68 @@ void WebAssemblyMCInstLower::Lower(const MachineInstr *MI, break; } case MachineOperand::MO_GlobalAddress: - assert(MO.getTargetFlags() == 0 && + assert(MO.getTargetFlags() == WebAssemblyII::MO_NO_FLAG && "WebAssembly does not use target flags on GlobalAddresses"); MCOp = LowerSymbolOperand(GetGlobalAddressSymbol(MO), MO.getOffset(), - MO.getGlobal()->getValueType()->isFunctionTy()); + MO.getGlobal()->getValueType()->isFunctionTy(), + false, false); break; case MachineOperand::MO_ExternalSymbol: // The target flag indicates whether this is a symbol for a // variable or a function. - assert((MO.getTargetFlags() & -2) == 0 && - "WebAssembly uses only one target flag bit on ExternalSymbols"); - MCOp = LowerSymbolOperand(GetExternalSymbolSymbol(MO), /*Offset=*/0, - MO.getTargetFlags() & 1); + assert((MO.getTargetFlags() & ~WebAssemblyII::MO_SYMBOL_MASK) == 0 && + "WebAssembly uses only symbol flags on ExternalSymbols"); + MCOp = LowerSymbolOperand( + GetExternalSymbolSymbol(MO), /*Offset=*/0, + (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_FUNCTION) != 0, + (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0, + (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_EVENT) != 0); + break; + case MachineOperand::MO_MCSymbol: + // This is currently used only for LSDA symbols (GCC_except_table), + // because global addresses or other external symbols are handled above. + assert(MO.getTargetFlags() == 0 && + "WebAssembly does not use target flags on MCSymbol"); + MCOp = LowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, false, + false); break; } OutMI.addOperand(MCOp); } + + if (!WasmKeepRegisters) + removeRegisterOperands(MI, OutMI); +} + +static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) { + // Remove all uses of stackified registers to bring the instruction format + // into its final stack form used thruout MC, and transition opcodes to + // their _S variant. + // We do this seperate from the above code that still may need these + // registers for e.g. call_indirect signatures. + // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for + // details. + // TODO: the code above creates new registers which are then removed here. + // That code could be slightly simplified by not doing that, though maybe + // it is simpler conceptually to keep the code above in "register mode" + // until this transition point. + // FIXME: we are not processing inline assembly, which contains register + // operands, because it is used by later target generic code. + if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm()) + return; + + // Transform to _S instruction. + auto RegOpcode = OutMI.getOpcode(); + auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode); + assert(StackOpcode != -1 && "Failed to stackify instruction"); + OutMI.setOpcode(StackOpcode); + + // Remove register operands. + for (auto I = OutMI.getNumOperands(); I; --I) { + auto &MO = OutMI.getOperand(I - 1); + if (MO.isReg()) { + OutMI.erase(&MO); + } + } } |
