diff options
Diffstat (limited to 'llvm/lib/Target/WebAssembly')
43 files changed, 1593 insertions, 613 deletions
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp index 1cba0843f891..1e2d3888fe1c 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -15,10 +15,9 @@ #include "AsmParser/WebAssemblyAsmTypeCheck.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "MCTargetDesc/WebAssemblyTargetStreamer.h" #include "TargetInfo/WebAssemblyTargetInfo.h" -#include "Utils/WebAssemblyTypeUtilities.h" -#include "Utils/WebAssemblyUtilities.h" #include "WebAssembly.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" @@ -248,15 +247,14 @@ public: WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, const MCInstrInfo &MII, const MCTargetOptions &Options) : MCTargetAsmParser(Options, STI, MII), Parser(Parser), - Lexer(Parser.getLexer()), - is64(STI.getTargetTriple().isArch64Bit()), + Lexer(Parser.getLexer()), is64(STI.getTargetTriple().isArch64Bit()), TC(Parser, MII, is64), SkipTypeCheck(Options.MCNoTypeCheck) { setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); // Don't type check if this is inline asm, since that is a naked sequence of // instructions without a function/locals decl. auto &SM = Parser.getSourceManager(); auto BufferName = - SM.getBufferInfo(SM.getMainFileID()).Buffer->getBufferIdentifier(); + SM.getBufferInfo(SM.getMainFileID()).Buffer->getBufferIdentifier(); if (BufferName == "<inline asm>") SkipTypeCheck = true; } @@ -323,7 +321,9 @@ public: } } - void push(NestingType NT) { NestingStack.push_back({NT, wasm::WasmSignature()}); } + void push(NestingType NT, wasm::WasmSignature Sig = wasm::WasmSignature()) { + NestingStack.push_back({NT, Sig}); + } bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) { if (NestingStack.empty()) @@ -337,6 +337,19 @@ public: return false; } + // Pop a NestingType and push a new NestingType with the same signature. Used + // for if-else and try-catch(_all). + bool popAndPushWithSameSignature(StringRef Ins, NestingType PopNT, + NestingType PushNT) { + if (NestingStack.empty()) + return error(Twine("End of block construct with no start: ") + Ins); + auto Sig = NestingStack.back().Sig; + if (pop(Ins, PopNT)) + return true; + push(PushNT, Sig); + return false; + } + bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) { auto Err = !NestingStack.empty(); while (!NestingStack.empty()) { @@ -588,17 +601,14 @@ public: push(If); ExpectBlockType = true; } else if (Name == "else") { - if (pop(Name, If)) + if (popAndPushWithSameSignature(Name, If, Else)) return true; - push(Else); } else if (Name == "catch") { - if (pop(Name, Try)) + if (popAndPushWithSameSignature(Name, Try, Try)) return true; - push(Try); } else if (Name == "catch_all") { - if (pop(Name, Try)) + if (popAndPushWithSameSignature(Name, Try, CatchAll)) return true; - push(CatchAll); } else if (Name == "end_if") { if (pop(Name, If, Else)) return true; @@ -638,10 +648,10 @@ public: if (parseSignature(Signature.get())) return true; // Got signature as block type, don't need more - ExpectBlockType = false; TC.setLastSig(*Signature.get()); if (ExpectBlockType) NestingStack.back().Sig = *Signature.get(); + ExpectBlockType = false; auto &Ctx = getContext(); // The "true" here will cause this to be a nameless symbol. MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true); @@ -691,7 +701,7 @@ public: parseSingleInteger(true, Operands); if (checkForP2AlignIfLoadStore(Operands, Name)) return true; - } else if(Lexer.is(AsmToken::Real)) { + } else if (Lexer.is(AsmToken::Real)) { if (parseSingleFloat(true, Operands)) return true; } else if (!parseSpecialFloatMaybe(true, Operands)) { @@ -775,31 +785,23 @@ public: // This function processes wasm-specific directives streamed to // WebAssemblyTargetStreamer, all others go to the generic parser // (see WasmAsmParser). - bool ParseDirective(AsmToken DirectiveID) override { - // This function has a really weird return value behavior that is different - // from all the other parsing functions: - // - return true && no tokens consumed -> don't know this directive / let - // the generic parser handle it. - // - return true && tokens consumed -> a parsing error occurred. - // - return false -> processed this directive successfully. + ParseStatus parseDirective(AsmToken DirectiveID) override { assert(DirectiveID.getKind() == AsmToken::Identifier); auto &Out = getStreamer(); auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer()); auto &Ctx = Out.getContext(); - // TODO: any time we return an error, at least one token must have been - // consumed, otherwise this will not signal an error to the caller. if (DirectiveID.getString() == ".globaltype") { auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; if (expect(AsmToken::Comma, ",")) - return true; + return ParseStatus::Failure; auto TypeTok = Lexer.getTok(); auto TypeName = expectIdent(); if (TypeName.empty()) - return true; + return ParseStatus::Failure; auto Type = WebAssembly::parseType(TypeName); if (!Type) return error("Unknown type in .globaltype directive: ", TypeTok); @@ -810,6 +812,8 @@ public: if (isNext(AsmToken::Comma)) { TypeTok = Lexer.getTok(); auto Id = expectIdent(); + if (Id.empty()) + return ParseStatus::Failure; if (Id == "immutable") Mutable = false; else @@ -829,14 +833,14 @@ public: // .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]] auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; if (expect(AsmToken::Comma, ",")) - return true; + return ParseStatus::Failure; auto ElemTypeTok = Lexer.getTok(); auto ElemTypeName = expectIdent(); if (ElemTypeName.empty()) - return true; + return ParseStatus::Failure; std::optional<wasm::ValType> ElemType = WebAssembly::parseType(ElemTypeName); if (!ElemType) @@ -844,7 +848,7 @@ public: wasm::WasmLimits Limits = DefaultLimits(); if (isNext(AsmToken::Comma) && parseLimits(&Limits)) - return true; + return ParseStatus::Failure; // Now that we have the name and table type, we can actually create the // symbol @@ -864,7 +868,7 @@ public: // parses the locals separately. auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); if (WasmSym->isDefined()) { // We push 'Function' either when a label is parsed or a .functype @@ -880,7 +884,7 @@ public: if (CurrentState != FunctionLabel) { // This .functype indicates a start of a function. if (ensureEmptyNestingStack()) - return true; + return ParseStatus::Failure; push(Function); } CurrentState = FunctionStart; @@ -888,7 +892,7 @@ public: } auto Signature = std::make_unique<wasm::WasmSignature>(); if (parseSignature(Signature.get())) - return true; + return ParseStatus::Failure; TC.funcDecl(*Signature); WasmSym->setSignature(Signature.get()); addSignature(std::move(Signature)); @@ -901,47 +905,56 @@ public: if (DirectiveID.getString() == ".export_name") { auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; if (expect(AsmToken::Comma, ",")) - return true; + return ParseStatus::Failure; auto ExportName = expectIdent(); + if (ExportName.empty()) + return ParseStatus::Failure; auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); WasmSym->setExportName(storeName(ExportName)); TOut.emitExportName(WasmSym, ExportName); + return expect(AsmToken::EndOfStatement, "EOL"); } if (DirectiveID.getString() == ".import_module") { auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; if (expect(AsmToken::Comma, ",")) - return true; + return ParseStatus::Failure; auto ImportModule = expectIdent(); + if (ImportModule.empty()) + return ParseStatus::Failure; auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); WasmSym->setImportModule(storeName(ImportModule)); TOut.emitImportModule(WasmSym, ImportModule); + return expect(AsmToken::EndOfStatement, "EOL"); } if (DirectiveID.getString() == ".import_name") { auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; if (expect(AsmToken::Comma, ",")) - return true; + return ParseStatus::Failure; auto ImportName = expectIdent(); + if (ImportName.empty()) + return ParseStatus::Failure; auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); WasmSym->setImportName(storeName(ImportName)); TOut.emitImportName(WasmSym, ImportName); + return expect(AsmToken::EndOfStatement, "EOL"); } if (DirectiveID.getString() == ".tagtype") { auto SymName = expectIdent(); if (SymName.empty()) - return true; + return ParseStatus::Failure; auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); auto Signature = std::make_unique<wasm::WasmSignature>(); if (parseRegTypeList(Signature->Params)) - return true; + return ParseStatus::Failure; WasmSym->setSignature(Signature.get()); addSignature(std::move(Signature)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG); @@ -956,7 +969,7 @@ public: Lexer.getTok()); SmallVector<wasm::ValType, 4> Locals; if (parseRegTypeList(Locals)) - return true; + return ParseStatus::Failure; TC.localDecl(Locals); TOut.emitLocal(Locals); CurrentState = FunctionLocals; @@ -967,7 +980,8 @@ public: DirectiveID.getString() == ".int16" || DirectiveID.getString() == ".int32" || DirectiveID.getString() == ".int64") { - if (CheckDataSection()) return true; + if (CheckDataSection()) + return ParseStatus::Failure; const MCExpr *Val; SMLoc End; if (Parser.parseExpression(Val, End)) @@ -979,7 +993,8 @@ public: } if (DirectiveID.getString() == ".asciz") { - if (CheckDataSection()) return true; + if (CheckDataSection()) + return ParseStatus::Failure; std::string S; if (Parser.parseEscapedString(S)) return error("Cannot parse string constant: ", Lexer.getTok()); @@ -987,7 +1002,7 @@ public: return expect(AsmToken::EndOfStatement, "EOL"); } - return true; // We didn't process this directive. + return ParseStatus::NoMatch; // We didn't process this directive. } // Called either when the first instruction is parsed of the function ends. diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp index b323b265b562..bc0cb2d10cdb 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp @@ -15,10 +15,9 @@ #include "AsmParser/WebAssemblyAsmTypeCheck.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "MCTargetDesc/WebAssemblyTargetStreamer.h" #include "TargetInfo/WebAssemblyTargetInfo.h" -#include "Utils/WebAssemblyTypeUtilities.h" -#include "Utils/WebAssemblyUtilities.h" #include "WebAssembly.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" @@ -45,16 +44,18 @@ extern StringRef GetMnemonic(unsigned Opc); namespace llvm { WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser, - const MCInstrInfo &MII, bool is64) - : Parser(Parser), MII(MII), is64(is64) { -} + const MCInstrInfo &MII, + bool is64) + : Parser(Parser), MII(MII), is64(is64) {} void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) { LocalTypes.assign(Sig.Params.begin(), Sig.Params.end()); ReturnTypes.assign(Sig.Returns.begin(), Sig.Returns.end()); + BrStack.emplace_back(Sig.Returns.begin(), Sig.Returns.end()); } -void WebAssemblyAsmTypeCheck::localDecl(const SmallVector<wasm::ValType, 4> &Locals) { +void WebAssemblyAsmTypeCheck::localDecl( + const SmallVectorImpl<wasm::ValType> &Locals) { LocalTypes.insert(LocalTypes.end(), Locals.begin(), Locals.end()); } @@ -117,38 +118,63 @@ bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst, auto Local = static_cast<size_t>(Inst.getOperand(0).getImm()); if (Local >= LocalTypes.size()) return typeError(ErrorLoc, StringRef("no local type specified for index ") + - std::to_string(Local)); + std::to_string(Local)); Type = LocalTypes[Local]; return false; } +static std::optional<std::string> +checkStackTop(const SmallVectorImpl<wasm::ValType> &ExpectedStackTop, + const SmallVectorImpl<wasm::ValType> &Got) { + for (size_t I = 0; I < ExpectedStackTop.size(); I++) { + auto EVT = ExpectedStackTop[I]; + auto PVT = Got[Got.size() - ExpectedStackTop.size() + I]; + if (PVT != EVT) + return std::string{"got "} + WebAssembly::typeToString(PVT) + + ", expected " + WebAssembly::typeToString(EVT); + } + return std::nullopt; +} + +bool WebAssemblyAsmTypeCheck::checkBr(SMLoc ErrorLoc, size_t Level) { + if (Level >= BrStack.size()) + return typeError(ErrorLoc, + StringRef("br: invalid depth ") + std::to_string(Level)); + const SmallVector<wasm::ValType, 4> &Expected = + BrStack[BrStack.size() - Level - 1]; + if (Expected.size() > Stack.size()) + return typeError(ErrorLoc, "br: insufficient values on the type stack"); + auto IsStackTopInvalid = checkStackTop(Expected, Stack); + if (IsStackTopInvalid) + return typeError(ErrorLoc, "br " + IsStackTopInvalid.value()); + return false; +} + bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc, bool PopVals) { + if (!PopVals) + BrStack.pop_back(); if (LastSig.Returns.size() > Stack.size()) return typeError(ErrorLoc, "end: insufficient values on the type stack"); - + if (PopVals) { for (auto VT : llvm::reverse(LastSig.Returns)) { - if (popType(ErrorLoc, VT)) + if (popType(ErrorLoc, VT)) return true; } return false; } - - for (size_t i = 0; i < LastSig.Returns.size(); i++) { - auto EVT = LastSig.Returns[i]; - auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i]; - if (PVT != EVT) - return typeError( - ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) + - ", expected " + WebAssembly::typeToString(EVT)); - } + + auto IsStackTopInvalid = checkStackTop(LastSig.Returns, Stack); + if (IsStackTopInvalid) + return typeError(ErrorLoc, "end " + IsStackTopInvalid.value()); return false; } bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc, - const wasm::WasmSignature& Sig) { + const wasm::WasmSignature &Sig) { for (auto VT : llvm::reverse(Sig.Params)) - if (popType(ErrorLoc, VT)) return true; + if (popType(ErrorLoc, VT)) + return true; Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end()); return false; } @@ -187,7 +213,7 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst, [[fallthrough]]; default: return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + - " missing .globaltype"); + " missing .globaltype"); } return false; } @@ -272,22 +298,77 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst, return true; if (popType(ErrorLoc, wasm::ValType::I32)) return true; + } else if (Name == "memory.fill") { + Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32; + if (popType(ErrorLoc, Type)) + return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + if (popType(ErrorLoc, Type)) + return true; + } else if (Name == "memory.copy") { + Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32; + if (popType(ErrorLoc, Type)) + return true; + if (popType(ErrorLoc, Type)) + return true; + if (popType(ErrorLoc, Type)) + return true; + } else if (Name == "memory.init") { + Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + if (popType(ErrorLoc, Type)) + return true; } else if (Name == "drop") { if (popType(ErrorLoc, {})) return true; + } else if (Name == "try" || Name == "block" || Name == "loop" || + Name == "if") { + if (Name == "if" && popType(ErrorLoc, wasm::ValType::I32)) + return true; + if (Name == "loop") + BrStack.emplace_back(LastSig.Params.begin(), LastSig.Params.end()); + else + BrStack.emplace_back(LastSig.Returns.begin(), LastSig.Returns.end()); } else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" || - Name == "else" || Name == "end_try") { - if (checkEnd(ErrorLoc, Name == "else")) + Name == "else" || Name == "end_try" || Name == "catch" || + Name == "catch_all" || Name == "delegate") { + if (checkEnd(ErrorLoc, + Name == "else" || Name == "catch" || Name == "catch_all")) + return true; + Unreachable = false; + if (Name == "catch") { + const MCSymbolRefExpr *SymRef; + if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef)) + return true; + const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); + const auto *Sig = WasmSym->getSignature(); + if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG) + return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") + + WasmSym->getName() + + " missing .tagtype"); + // catch instruction pushes values whose types are specified in the tag's + // "params" part + Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end()); + } + } else if (Name == "br") { + const MCOperand &Operand = Inst.getOperand(0); + if (!Operand.isImm()) + return false; + if (checkBr(ErrorLoc, static_cast<size_t>(Operand.getImm()))) return true; - if (Name == "end_block") - Unreachable = false; } else if (Name == "return") { if (endOfFunction(ErrorLoc)) return true; } else if (Name == "call_indirect" || Name == "return_call_indirect") { // Function value. - if (popType(ErrorLoc, wasm::ValType::I32)) return true; - if (checkSig(ErrorLoc, LastSig)) return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + if (checkSig(ErrorLoc, LastSig)) + return true; if (Name == "return_call_indirect" && endOfFunction(ErrorLoc)) return true; } else if (Name == "call" || Name == "return_call") { @@ -300,22 +381,10 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst, return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") + WasmSym->getName() + " missing .functype"); - if (checkSig(ErrorLoc, *Sig)) return true; - if (Name == "return_call" && endOfFunction(ErrorLoc)) + if (checkSig(ErrorLoc, *Sig)) return true; - } else if (Name == "catch") { - const MCSymbolRefExpr *SymRef; - if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef)) + if (Name == "return_call" && endOfFunction(ErrorLoc)) return true; - const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); - const auto *Sig = WasmSym->getSignature(); - if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG) - return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") + - WasmSym->getName() + - " missing .tagtype"); - // catch instruction pushes values whose types are specified in the tag's - // "params" part - Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end()); } else if (Name == "unreachable") { Unreachable = true; } else if (Name == "ref.is_null") { diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h index 9c190e6beae7..6fa95c392975 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h @@ -29,6 +29,7 @@ class WebAssemblyAsmTypeCheck final { const MCInstrInfo &MII; SmallVector<wasm::ValType, 8> Stack; + SmallVector<SmallVector<wasm::ValType, 4>, 8> BrStack; SmallVector<wasm::ValType, 16> LocalTypes; SmallVector<wasm::ValType, 4> ReturnTypes; wasm::WasmSignature LastSig; @@ -42,6 +43,7 @@ class WebAssemblyAsmTypeCheck final { bool popRefType(SMLoc ErrorLoc); bool getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type); bool checkEnd(SMLoc ErrorLoc, bool PopVals = false); + bool checkBr(SMLoc ErrorLoc, size_t Level); bool checkSig(SMLoc ErrorLoc, const wasm::WasmSignature &Sig); bool getSymRef(SMLoc ErrorLoc, const MCInst &Inst, const MCSymbolRefExpr *&SymRef); @@ -49,16 +51,18 @@ class WebAssemblyAsmTypeCheck final { bool getTable(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type); public: - WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, bool is64); + WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, + bool is64); void funcDecl(const wasm::WasmSignature &Sig); - void localDecl(const SmallVector<wasm::ValType, 4> &Locals); + void localDecl(const SmallVectorImpl<wasm::ValType> &Locals); void setLastSig(const wasm::WasmSignature &Sig) { LastSig = Sig; } bool endOfFunction(SMLoc ErrorLoc); bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst, OperandVector &Operands); void Clear() { Stack.clear(); + BrStack.clear(); LocalTypes.clear(); ReturnTypes.clear(); TypeErrorThisFunction = false; @@ -68,4 +72,4 @@ public: } // end namespace llvm -#endif // LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H +#endif // LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp index 1f07b1619b49..2c3604cc72d2 100644 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -14,8 +14,8 @@ /// //===----------------------------------------------------------------------===// +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "TargetInfo/WebAssemblyTargetInfo.h" -#include "Utils/WebAssemblyTypeUtilities.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDecoderOps.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp index b925519e6162..a9673ab344d3 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp @@ -13,8 +13,7 @@ #include "MCTargetDesc/WebAssemblyInstPrinter.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "Utils/WebAssemblyTypeUtilities.h" -#include "Utils/WebAssemblyUtilities.h" +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "WebAssembly.h" #include "WebAssemblyMachineFunctionInfo.h" #include "llvm/ADT/SmallSet.h" diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h index c81c3a3d9ffa..51e4d3656ba4 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h @@ -16,8 +16,8 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/CodeGen/MachineValueType.h" #include "llvm/MC/MCInstPrinter.h" -#include "llvm/Support/MachineValueType.h" namespace llvm { diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp index 5727708a84ad..9d43c0052d52 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "WebAssemblyMCAsmInfo.h" -#include "Utils/WebAssemblyUtilities.h" -#include "llvm/ADT/Triple.h" +#include "WebAssemblyMCTargetDesc.h" +#include "llvm/TargetParser/Triple.h" using namespace llvm; diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp index cd692f4dda33..634ed10d4df5 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" @@ -25,6 +26,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/SMLoc.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -37,28 +39,31 @@ STATISTIC(MCNumFixups, "Number of MC fixups created."); namespace { class WebAssemblyMCCodeEmitter final : public MCCodeEmitter { const MCInstrInfo &MCII; - + MCContext &Ctx; // Implementation generated by tablegen. uint64_t getBinaryCodeForInstr(const MCInst &MI, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; - void encodeInstruction(const MCInst &MI, raw_ostream &OS, + void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const override; public: - WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) : MCII(MCII) {} + WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) + : MCII(MCII), Ctx{Ctx} {} }; } // end anonymous namespace -MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) { - return new WebAssemblyMCCodeEmitter(MCII); +MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, + MCContext &Ctx) { + return new WebAssemblyMCCodeEmitter(MCII, Ctx); } void WebAssemblyMCCodeEmitter::encodeInstruction( - const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const { + const MCInst &MI, SmallVectorImpl<char> &CB, + SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { + raw_svector_ostream OS(CB); uint64_t Start = OS.tell(); uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI); @@ -119,7 +124,10 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( support::endian::write<uint64_t>(OS, MO.getImm(), support::little); break; case WebAssembly::OPERAND_GLOBAL: - llvm_unreachable("wasm globals should only be accessed symbolicly"); + Ctx.reportError( + SMLoc(), + Twine("Wasm globals should only be accessed symbolically!")); + break; default: encodeULEB128(uint64_t(MO.getImm()), OS); } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp index 97dbc35c991b..e8f58a19d25e 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp @@ -35,6 +35,26 @@ using namespace llvm; #define GET_REGINFO_MC_DESC #include "WebAssemblyGenRegisterInfo.inc" +// Exception handling & setjmp-longjmp handling related options. + +// Emscripten's asm.js-style exception handling +cl::opt<bool> WebAssembly::WasmEnableEmEH( + "enable-emscripten-cxx-exceptions", + cl::desc("WebAssembly Emscripten-style exception handling"), + cl::init(false)); +// Emscripten's asm.js-style setjmp/longjmp handling +cl::opt<bool> WebAssembly::WasmEnableEmSjLj( + "enable-emscripten-sjlj", + cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), + cl::init(false)); +// Exception handling using wasm EH instructions +cl::opt<bool> + WebAssembly::WasmEnableEH("wasm-enable-eh", + cl::desc("WebAssembly exception handling")); +// setjmp/longjmp handling using wasm EH instrutions +cl::opt<bool> WebAssembly::WasmEnableSjLj( + "wasm-enable-sjlj", cl::desc("WebAssembly setjmp/longjmp handling")); + static MCAsmInfo *createMCAsmInfo(const MCRegisterInfo & /*MRI*/, const Triple &TT, const MCTargetOptions &Options) { @@ -64,7 +84,7 @@ static MCInstPrinter *createMCInstPrinter(const Triple & /*T*/, static MCCodeEmitter *createCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) { - return createWebAssemblyMCCodeEmitter(MCII); + return createWebAssemblyMCCodeEmitter(MCII, Ctx); } static MCAsmBackend *createAsmBackend(const Target & /*T*/, diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index 476955e434f2..fc33cebaa48a 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -16,6 +16,7 @@ #include "../WebAssemblySubtarget.h" #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/Support/DataTypes.h" #include <memory> @@ -28,7 +29,8 @@ class MCInstrInfo; class MCObjectTargetWriter; class Triple; -MCCodeEmitter *createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII); +MCCodeEmitter *createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, + MCContext &Ctx); MCAsmBackend *createWebAssemblyAsmBackend(const Triple &TT); @@ -36,6 +38,13 @@ std::unique_ptr<MCObjectTargetWriter> createWebAssemblyWasmObjectWriter(bool Is64Bit, bool IsEmscripten); namespace WebAssembly { + +// Exception handling / setjmp-longjmp handling command-line options +extern cl::opt<bool> WasmEnableEmEH; // asm.js-style EH +extern cl::opt<bool> WasmEnableEmSjLj; // asm.js-style SjLJ +extern cl::opt<bool> WasmEnableEH; // EH using Wasm EH instructions +extern cl::opt<bool> WasmEnableSjLj; // SjLj using Wasm EH instructions + enum OperandType { /// Basic block label in a branch construct. OPERAND_BASIC_BLOCK = MCOI::OPERAND_FIRST_TARGET, @@ -272,6 +281,50 @@ inline unsigned GetDefaultP2Align(unsigned Opc) { return Align; } +inline bool isConst(unsigned Opc) { + switch (Opc) { + case WebAssembly::CONST_I32: + case WebAssembly::CONST_I32_S: + case WebAssembly::CONST_I64: + case WebAssembly::CONST_I64_S: + case WebAssembly::CONST_F32: + case WebAssembly::CONST_F32_S: + case WebAssembly::CONST_F64: + case WebAssembly::CONST_F64_S: + case WebAssembly::CONST_V128_I8x16: + case WebAssembly::CONST_V128_I8x16_S: + case WebAssembly::CONST_V128_I16x8: + case WebAssembly::CONST_V128_I16x8_S: + case WebAssembly::CONST_V128_I32x4: + case WebAssembly::CONST_V128_I32x4_S: + case WebAssembly::CONST_V128_I64x2: + case WebAssembly::CONST_V128_I64x2_S: + case WebAssembly::CONST_V128_F32x4: + case WebAssembly::CONST_V128_F32x4_S: + case WebAssembly::CONST_V128_F64x2: + case WebAssembly::CONST_V128_F64x2_S: + return true; + default: + return false; + } +} + +inline bool isScalarConst(unsigned Opc) { + switch (Opc) { + case WebAssembly::CONST_I32: + case WebAssembly::CONST_I32_S: + case WebAssembly::CONST_I64: + case WebAssembly::CONST_I64_S: + case WebAssembly::CONST_F32: + case WebAssembly::CONST_F32_S: + case WebAssembly::CONST_F64: + case WebAssembly::CONST_F64_S: + return true; + default: + return false; + } +} + inline bool isArgument(unsigned Opc) { switch (Opc) { case WebAssembly::ARGUMENT_i32: diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp new file mode 100644 index 000000000000..b7b5b2a97c59 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp @@ -0,0 +1,124 @@ +//===- WebAssemblyMCTypeUtilities.cpp - WebAssembly Type Utility Functions-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements several utility functions for WebAssembly type parsing. +/// +//===----------------------------------------------------------------------===// + +#include "WebAssemblyMCTypeUtilities.h" +#include "WebAssemblyMCTargetDesc.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace llvm; + +std::optional<wasm::ValType> WebAssembly::parseType(StringRef Type) { + // FIXME: can't use StringSwitch because wasm::ValType doesn't have a + // "invalid" value. + if (Type == "i32") + return wasm::ValType::I32; + if (Type == "i64") + return wasm::ValType::I64; + if (Type == "f32") + return wasm::ValType::F32; + if (Type == "f64") + return wasm::ValType::F64; + if (Type == "v128" || Type == "i8x16" || Type == "i16x8" || Type == "i32x4" || + Type == "i64x2" || Type == "f32x4" || Type == "f64x2") + return wasm::ValType::V128; + if (Type == "funcref") + return wasm::ValType::FUNCREF; + if (Type == "externref") + return wasm::ValType::EXTERNREF; + return std::nullopt; +} + +WebAssembly::BlockType WebAssembly::parseBlockType(StringRef Type) { + // Multivalue block types are handled separately in parseSignature + return StringSwitch<WebAssembly::BlockType>(Type) + .Case("i32", WebAssembly::BlockType::I32) + .Case("i64", WebAssembly::BlockType::I64) + .Case("f32", WebAssembly::BlockType::F32) + .Case("f64", WebAssembly::BlockType::F64) + .Case("v128", WebAssembly::BlockType::V128) + .Case("funcref", WebAssembly::BlockType::Funcref) + .Case("externref", WebAssembly::BlockType::Externref) + .Case("void", WebAssembly::BlockType::Void) + .Default(WebAssembly::BlockType::Invalid); +} + +// We have various enums representing a subset of these types, use this +// function to convert any of them to text. +const char *WebAssembly::anyTypeToString(unsigned Type) { + switch (Type) { + case wasm::WASM_TYPE_I32: + return "i32"; + case wasm::WASM_TYPE_I64: + return "i64"; + case wasm::WASM_TYPE_F32: + return "f32"; + case wasm::WASM_TYPE_F64: + return "f64"; + case wasm::WASM_TYPE_V128: + return "v128"; + case wasm::WASM_TYPE_FUNCREF: + return "funcref"; + case wasm::WASM_TYPE_EXTERNREF: + return "externref"; + case wasm::WASM_TYPE_FUNC: + return "func"; + case wasm::WASM_TYPE_NORESULT: + return "void"; + default: + return "invalid_type"; + } +} + +const char *WebAssembly::typeToString(wasm::ValType Type) { + return anyTypeToString(static_cast<unsigned>(Type)); +} + +std::string WebAssembly::typeListToString(ArrayRef<wasm::ValType> List) { + std::string S; + for (const auto &Type : List) { + if (&Type != &List[0]) + S += ", "; + S += WebAssembly::typeToString(Type); + } + return S; +} + +std::string WebAssembly::signatureToString(const wasm::WasmSignature *Sig) { + std::string S("("); + S += typeListToString(Sig->Params); + S += ") -> ("; + S += typeListToString(Sig->Returns); + S += ")"; + return S; +} + +wasm::ValType WebAssembly::regClassToValType(unsigned RC) { + switch (RC) { + case WebAssembly::I32RegClassID: + return wasm::ValType::I32; + case WebAssembly::I64RegClassID: + return wasm::ValType::I64; + case WebAssembly::F32RegClassID: + return wasm::ValType::F32; + case WebAssembly::F64RegClassID: + return wasm::ValType::F64; + case WebAssembly::V128RegClassID: + return wasm::ValType::V128; + case WebAssembly::FUNCREFRegClassID: + return wasm::ValType::FUNCREF; + case WebAssembly::EXTERNREFRegClassID: + return wasm::ValType::EXTERNREF; + default: + llvm_unreachable("unexpected type"); + } +} diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h new file mode 100644 index 000000000000..18018dfc6d6f --- /dev/null +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h @@ -0,0 +1,73 @@ +//===-- WebAssemblyMCTypeUtilities - WebAssembly Type Utilities-*- C++ -*-====// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the declaration of the WebAssembly-specific type parsing +/// utility functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTYPEUTILITIES_H +#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTYPEUTILITIES_H + +#include "llvm/BinaryFormat/Wasm.h" + +namespace llvm { + +namespace WebAssembly { + +/// Used as immediate MachineOperands for block signatures +enum class BlockType : unsigned { + Invalid = 0x00, + Void = 0x40, + I32 = unsigned(wasm::ValType::I32), + I64 = unsigned(wasm::ValType::I64), + F32 = unsigned(wasm::ValType::F32), + F64 = unsigned(wasm::ValType::F64), + V128 = unsigned(wasm::ValType::V128), + Externref = unsigned(wasm::ValType::EXTERNREF), + Funcref = unsigned(wasm::ValType::FUNCREF), + // Multivalue blocks (and other non-void blocks) are only emitted when the + // blocks will never be exited and are at the ends of functions (see + // WebAssemblyCFGStackify::fixEndsAtEndOfFunction). They also are never made + // to pop values off the stack, so the exact multivalue signature can always + // be inferred from the return type of the parent function in MCInstLower. + Multivalue = 0xffff, +}; + +inline bool isRefType(wasm::ValType Type) { + return Type == wasm::ValType::EXTERNREF || Type == wasm::ValType::FUNCREF; +} + +// Convert ValType or a list/signature of ValTypes to a string. + +// Convert an unsinged integer, which can be among wasm::ValType enum, to its +// type name string. If the input is not within wasm::ValType, returns +// "invalid_type". +const char *anyTypeToString(unsigned Type); +const char *typeToString(wasm::ValType Type); +// Convert a list of ValTypes into a string in the format of +// "type0, type1, ... typeN" +std::string typeListToString(ArrayRef<wasm::ValType> List); +// Convert a wasm signature into a string in the format of +// "(params) -> (results)", where params and results are a string of ValType +// lists. +std::string signatureToString(const wasm::WasmSignature *Sig); + +// Convert a register class ID to a wasm ValType. +wasm::ValType regClassToValType(unsigned RC); + +// Convert StringRef to ValType / HealType / BlockType + +std::optional<wasm::ValType> parseType(StringRef Type); +BlockType parseBlockType(StringRef Type); + +} // end namespace WebAssembly +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp index 2da219d54c73..f389ee2f50d8 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -14,7 +14,7 @@ #include "MCTargetDesc/WebAssemblyTargetStreamer.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "Utils/WebAssemblyTypeUtilities.h" +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSubtargetInfo.h" diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h index 522f6356c28b..72d36a251a91 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -16,8 +16,8 @@ #define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYTARGETSTREAMER_H #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/CodeGen/MachineValueType.h" #include "llvm/MC/MCStreamer.h" -#include "llvm/Support/MachineValueType.h" namespace llvm { diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp index 405712906c40..43c67b4b4749 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -91,6 +91,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType( return wasm::R_WASM_TYPE_INDEX_LEB; case MCSymbolRefExpr::VK_None: break; + case MCSymbolRefExpr::VK_WASM_FUNCINDEX: + return wasm::R_WASM_FUNCTION_INDEX_I32; default: report_fatal_error("unknown VariantKind"); break; diff --git a/llvm/lib/Target/WebAssembly/README.txt b/llvm/lib/Target/WebAssembly/README.txt index ab1cd8f0f84a..8dc2d7fcc733 100644 --- a/llvm/lib/Target/WebAssembly/README.txt +++ b/llvm/lib/Target/WebAssembly/README.txt @@ -17,7 +17,7 @@ includes standard libraries, tools, and packaging for producing WebAssembly applications that can run in browsers and other environments. wasi-sdk provides a more minimal C/C++ SDK based on clang, llvm and a libc based -on musl, for producing WebAssemmbly applictions that use the WASI ABI. +on musl, for producing WebAssembly applications that use the WASI ABI. Rust provides WebAssembly support integrated into Cargo. There are two main options: diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp index 998905402b39..bf5db09e05de 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp @@ -21,41 +21,6 @@ using namespace llvm; -std::optional<wasm::ValType> WebAssembly::parseType(StringRef Type) { - // FIXME: can't use StringSwitch because wasm::ValType doesn't have a - // "invalid" value. - if (Type == "i32") - return wasm::ValType::I32; - if (Type == "i64") - return wasm::ValType::I64; - if (Type == "f32") - return wasm::ValType::F32; - if (Type == "f64") - return wasm::ValType::F64; - if (Type == "v128" || Type == "i8x16" || Type == "i16x8" || Type == "i32x4" || - Type == "i64x2" || Type == "f32x4" || Type == "f64x2") - return wasm::ValType::V128; - if (Type == "funcref") - return wasm::ValType::FUNCREF; - if (Type == "externref") - return wasm::ValType::EXTERNREF; - return std::nullopt; -} - -WebAssembly::BlockType WebAssembly::parseBlockType(StringRef Type) { - // Multivalue block types are handled separately in parseSignature - return StringSwitch<WebAssembly::BlockType>(Type) - .Case("i32", WebAssembly::BlockType::I32) - .Case("i64", WebAssembly::BlockType::I64) - .Case("f32", WebAssembly::BlockType::F32) - .Case("f64", WebAssembly::BlockType::F64) - .Case("v128", WebAssembly::BlockType::V128) - .Case("funcref", WebAssembly::BlockType::Funcref) - .Case("externref", WebAssembly::BlockType::Externref) - .Case("void", WebAssembly::BlockType::Void) - .Default(WebAssembly::BlockType::Invalid); -} - MVT WebAssembly::parseMVT(StringRef Type) { return StringSwitch<MVT>(Type) .Case("i32", MVT::i32) @@ -72,56 +37,6 @@ MVT WebAssembly::parseMVT(StringRef Type) { .Default(MVT::INVALID_SIMPLE_VALUE_TYPE); } -// We have various enums representing a subset of these types, use this -// function to convert any of them to text. -const char *WebAssembly::anyTypeToString(unsigned Type) { - switch (Type) { - case wasm::WASM_TYPE_I32: - return "i32"; - case wasm::WASM_TYPE_I64: - return "i64"; - case wasm::WASM_TYPE_F32: - return "f32"; - case wasm::WASM_TYPE_F64: - return "f64"; - case wasm::WASM_TYPE_V128: - return "v128"; - case wasm::WASM_TYPE_FUNCREF: - return "funcref"; - case wasm::WASM_TYPE_EXTERNREF: - return "externref"; - case wasm::WASM_TYPE_FUNC: - return "func"; - case wasm::WASM_TYPE_NORESULT: - return "void"; - default: - return "invalid_type"; - } -} - -const char *WebAssembly::typeToString(wasm::ValType Type) { - return anyTypeToString(static_cast<unsigned>(Type)); -} - -std::string WebAssembly::typeListToString(ArrayRef<wasm::ValType> List) { - std::string S; - for (const auto &Type : List) { - if (&Type != &List[0]) - S += ", "; - S += WebAssembly::typeToString(Type); - } - return S; -} - -std::string WebAssembly::signatureToString(const wasm::WasmSignature *Sig) { - std::string S("("); - S += typeListToString(Sig->Params); - S += ") -> ("; - S += typeListToString(Sig->Returns); - S += ")"; - return S; -} - wasm::ValType WebAssembly::toValType(MVT Type) { switch (Type.SimpleTy) { case MVT::i32: @@ -148,34 +63,13 @@ wasm::ValType WebAssembly::toValType(MVT Type) { } } -wasm::ValType WebAssembly::regClassToValType(unsigned RC) { - switch (RC) { - case WebAssembly::I32RegClassID: - return wasm::ValType::I32; - case WebAssembly::I64RegClassID: - return wasm::ValType::I64; - case WebAssembly::F32RegClassID: - return wasm::ValType::F32; - case WebAssembly::F64RegClassID: - return wasm::ValType::F64; - case WebAssembly::V128RegClassID: - return wasm::ValType::V128; - case WebAssembly::FUNCREFRegClassID: - return wasm::ValType::FUNCREF; - case WebAssembly::EXTERNREFRegClassID: - return wasm::ValType::EXTERNREF; - default: - llvm_unreachable("unexpected type"); - } -} - wasm::ValType WebAssembly::regClassToValType(const TargetRegisterClass *RC) { assert(RC != nullptr); return regClassToValType(RC->getID()); } void WebAssembly::wasmSymbolSetType(MCSymbolWasm *Sym, const Type *GlobalVT, - const SmallVector<MVT, 1> &VTs) { + const ArrayRef<MVT> &VTs) { assert(!Sym->getType()); // Tables are represented as Arrays in LLVM IR therefore @@ -183,13 +77,13 @@ void WebAssembly::wasmSymbolSetType(MCSymbolWasm *Sym, const Type *GlobalVT, // that is a reference type. wasm::ValType ValTy; bool IsTable = false; - if (GlobalVT->isArrayTy() && - WebAssembly::isRefType(GlobalVT->getArrayElementType())) { + if (GlobalVT->isArrayTy() && WebAssembly::isWebAssemblyReferenceType( + GlobalVT->getArrayElementType())) { IsTable = true; const Type *ElTy = GlobalVT->getArrayElementType(); - if (WebAssembly::isExternrefType(ElTy)) + if (WebAssembly::isWebAssemblyExternrefType(ElTy)) ValTy = wasm::ValType::EXTERNREF; - else if (WebAssembly::isFuncrefType(ElTy)) + else if (WebAssembly::isWebAssemblyFuncrefType(ElTy)) ValTy = wasm::ValType::FUNCREF; else report_fatal_error("unhandled reference type"); diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h index 33f3bf31595d..9f58d7582fab 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h @@ -15,10 +15,12 @@ #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYTYPEUTILITIES_H #define LLVM_LIB_TARGET_WEBASSEMBLY_UTILS_WEBASSEMBLYTYPEUTILITIES_H +#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/WasmAddressSpaces.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/MC/MCSymbolWasm.h" -#include "llvm/Support/MachineValueType.h" namespace llvm { @@ -26,99 +28,36 @@ class TargetRegisterClass; namespace WebAssembly { -/// Used as immediate MachineOperands for block signatures -enum class BlockType : unsigned { - Invalid = 0x00, - Void = 0x40, - I32 = unsigned(wasm::ValType::I32), - I64 = unsigned(wasm::ValType::I64), - F32 = unsigned(wasm::ValType::F32), - F64 = unsigned(wasm::ValType::F64), - V128 = unsigned(wasm::ValType::V128), - Externref = unsigned(wasm::ValType::EXTERNREF), - Funcref = unsigned(wasm::ValType::FUNCREF), - // Multivalue blocks (and other non-void blocks) are only emitted when the - // blocks will never be exited and are at the ends of functions (see - // WebAssemblyCFGStackify::fixEndsAtEndOfFunction). They also are never made - // to pop values off the stack, so the exact multivalue signature can always - // be inferred from the return type of the parent function in MCInstLower. - Multivalue = 0xffff, -}; - -enum WasmAddressSpace : unsigned { - // Default address space, for pointers to linear memory (stack, heap, data). - WASM_ADDRESS_SPACE_DEFAULT = 0, - // A non-integral address space for pointers to named objects outside of - // linear memory: WebAssembly globals or WebAssembly locals. Loads and stores - // to these pointers are lowered to global.get / global.set or local.get / - // local.set, as appropriate. - WASM_ADDRESS_SPACE_VAR = 1, - // A non-integral address space for externref values - WASM_ADDRESS_SPACE_EXTERNREF = 10, - // A non-integral address space for funcref values - WASM_ADDRESS_SPACE_FUNCREF = 20, -}; - -inline bool isDefaultAddressSpace(unsigned AS) { - return AS == WASM_ADDRESS_SPACE_DEFAULT; -} -inline bool isWasmVarAddressSpace(unsigned AS) { - return AS == WASM_ADDRESS_SPACE_VAR; -} -inline bool isValidAddressSpace(unsigned AS) { - return isDefaultAddressSpace(AS) || isWasmVarAddressSpace(AS); -} -inline bool isFuncrefType(const Type *Ty) { - return isa<PointerType>(Ty) && - Ty->getPointerAddressSpace() == - WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF; +/// Return true if this is a WebAssembly Externref Type. +inline bool isWebAssemblyExternrefType(const Type *Ty) { + return Ty->getPointerAddressSpace() == + WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF; } -inline bool isExternrefType(const Type *Ty) { - return isa<PointerType>(Ty) && - Ty->getPointerAddressSpace() == - WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF; -} -inline bool isRefType(const Type *Ty) { - return isFuncrefType(Ty) || isExternrefType(Ty); + +/// Return true if this is a WebAssembly Funcref Type. +inline bool isWebAssemblyFuncrefType(const Type *Ty) { + return Ty->getPointerAddressSpace() == + WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF; } -inline bool isRefType(wasm::ValType Type) { - return Type == wasm::ValType::EXTERNREF || Type == wasm::ValType::FUNCREF; +/// Return true if this is a WebAssembly Reference Type. +inline bool isWebAssemblyReferenceType(const Type *Ty) { + return isWebAssemblyExternrefType(Ty) || isWebAssemblyFuncrefType(Ty); } // Convert StringRef to ValType / HealType / BlockType -std::optional<wasm::ValType> parseType(StringRef Type); -BlockType parseBlockType(StringRef Type); MVT parseMVT(StringRef Type); -// Convert ValType or a list/signature of ValTypes to a string. - -// Convert an unsinged integer, which can be among wasm::ValType enum, to its -// type name string. If the input is not within wasm::ValType, returns -// "invalid_type". -const char *anyTypeToString(unsigned Type); -const char *typeToString(wasm::ValType Type); -// Convert a list of ValTypes into a string in the format of -// "type0, type1, ... typeN" -std::string typeListToString(ArrayRef<wasm::ValType> List); -// Convert a wasm signature into a string in the format of -// "(params) -> (results)", where params and results are a string of ValType -// lists. -std::string signatureToString(const wasm::WasmSignature *Sig); - // Convert a MVT into its corresponding wasm ValType. wasm::ValType toValType(MVT Type); -// Convert a register class ID to a wasm ValType. -wasm::ValType regClassToValType(unsigned RC); - // Convert a register class to a wasm ValType. wasm::ValType regClassToValType(const TargetRegisterClass *RC); /// Sets a Wasm Symbol Type. void wasmSymbolSetType(MCSymbolWasm *Sym, const Type *GlobalVT, - const SmallVector<MVT, 1> &VTs); + const ArrayRef<MVT> &VTs); } // end namespace WebAssembly } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.cpp index a1e0db692390..8d7fa4dc3dee 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.cpp @@ -18,30 +18,6 @@ #include "llvm/MC/MCContext.h" using namespace llvm; -// Exception handling & setjmp-longjmp handling related options. These are -// defined here to be shared between WebAssembly and its subdirectories. - -// Emscripten's asm.js-style exception handling -cl::opt<bool> WebAssembly::WasmEnableEmEH( - "enable-emscripten-cxx-exceptions", - cl::desc("WebAssembly Emscripten-style exception handling"), - cl::init(false)); -// Emscripten's asm.js-style setjmp/longjmp handling -cl::opt<bool> WebAssembly::WasmEnableEmSjLj( - "enable-emscripten-sjlj", - cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), - cl::init(false)); -// Exception handling using wasm EH instructions -cl::opt<bool> - WebAssembly::WasmEnableEH("wasm-enable-eh", - cl::desc("WebAssembly exception handling"), - cl::init(false)); -// setjmp/longjmp handling using wasm EH instrutions -cl::opt<bool> - WebAssembly::WasmEnableSjLj("wasm-enable-sjlj", - cl::desc("WebAssembly setjmp/longjmp handling"), - cl::init(false)); - // Function names in libc++abi and libunwind const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch"; const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow"; diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h index d0639208fda9..7f28fb1858a6 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h @@ -33,12 +33,6 @@ namespace WebAssembly { bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI); bool mayThrow(const MachineInstr &MI); -// Exception handling / setjmp-longjmp handling command-line options -extern cl::opt<bool> WasmEnableEmEH; // asm.js-style EH -extern cl::opt<bool> WasmEnableEmSjLj; // asm.js-style SjLJ -extern cl::opt<bool> WasmEnableEH; // EH using Wasm EH instructions -extern cl::opt<bool> WasmEnableSjLj; // SjLj using Wasm EH instructions - // Exception-related function names extern const char *const ClangCallTerminateFn; extern const char *const CxaBeginCatchFn; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 60b1b3f5fc27..d492bec97d46 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -25,8 +25,10 @@ #include "WebAssemblyRegisterInfo.h" #include "WebAssemblyRuntimeLibcallSignatures.h" #include "WebAssemblyTargetMachine.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -301,8 +303,8 @@ void WebAssemblyAsmPrinter::emitDecls(const Module &M) { // only find symbols that have been used. Unused symbols from globals will // not be found here. MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>(); - for (const auto &Name : MMIW.MachineSymbolsUsed) { - auto *WasmSym = cast<MCSymbolWasm>(getOrCreateWasmSymbol(Name.getKey())); + for (StringRef Name : MMIW.MachineSymbolsUsed) { + auto *WasmSym = cast<MCSymbolWasm>(getOrCreateWasmSymbol(Name)); if (WasmSym->isFunction()) { // TODO(wvo): is there any case where this overlaps with the call to // emitFunctionType in the loop below? @@ -438,6 +440,7 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { EmitProducerInfo(M); EmitTargetFeatures(M); + EmitFunctionAttributes(M); } void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { @@ -556,6 +559,48 @@ void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { OutStreamer->popSection(); } +void WebAssemblyAsmPrinter::EmitFunctionAttributes(Module &M) { + auto V = M.getNamedGlobal("llvm.global.annotations"); + if (!V) + return; + + // Group all the custom attributes by name. + MapVector<StringRef, SmallVector<MCSymbol *, 4>> CustomSections; + const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0)); + for (Value *Op : CA->operands()) { + auto *CS = cast<ConstantStruct>(Op); + // The first field is a pointer to the annotated variable. + Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts(); + // Only annotated functions are supported for now. + if (!isa<Function>(AnnotatedVar)) + continue; + auto *F = cast<Function>(AnnotatedVar); + + // The second field is a pointer to a global annotation string. + auto *GV = cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts()); + StringRef AnnotationString; + getConstantStringInfo(GV, AnnotationString); + auto *Sym = cast<MCSymbolWasm>(getSymbol(F)); + CustomSections[AnnotationString].push_back(Sym); + } + + // Emit a custom section for each unique attribute. + for (const auto &[Name, Symbols] : CustomSections) { + MCSectionWasm *CustomSection = OutContext.getWasmSection( + ".custom_section.llvm.func_attr.annotate." + Name, SectionKind::getMetadata()); + OutStreamer->pushSection(); + OutStreamer->switchSection(CustomSection); + + for (auto &Sym : Symbols) { + OutStreamer->emitValue( + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_WASM_FUNCINDEX, + OutContext), + 4); + } + OutStreamer->popSection(); + } +} + void WebAssemblyAsmPrinter::emitConstantPool() { emitDecls(*MMI->getModule()); assert(MF->getConstantPool()->getConstants().empty() && diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h index 65d6ee415180..c30e0155c81e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -66,6 +66,7 @@ public: void emitEndOfAsmFile(Module &M) override; void EmitProducerInfo(Module &M); void EmitTargetFeatures(Module &M); + void EmitFunctionAttributes(Module &M); void emitSymbolType(const MCSymbolWasm *Sym); void emitGlobalVariable(const GlobalVariable *GV) override; void emitJumpTableInfo() override; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 70c187af73a5..cc8052352b38 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -1291,6 +1291,7 @@ bool WebAssemblyCFGStackify::fixCatchUnwindMismatches(MachineFunction &MF) { // end_try const auto *EHInfo = MF.getWasmEHFuncInfo(); + assert(EHInfo); SmallVector<const MachineBasicBlock *, 8> EHPadStack; // For EH pads that have catch unwind mismatches, a map of <EH pad, its // correct unwind destination>. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp index 9a6acd157a74..f3f54a5fb501 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp @@ -65,14 +65,14 @@ FunctionPass *llvm::createWebAssemblyDebugFixup() { // Because Wasm cannot access values in LLVM virtual registers in the debugger, // these dangling DBG_VALUEs in effect kill the effect of any previous DBG_VALUE // associated with the variable, which will appear as "optimized out". -static void nullifyDanglingDebugValues(MachineBasicBlock &MBB, - const TargetInstrInfo *TII) { +static void setDanglingDebugValuesUndef(MachineBasicBlock &MBB, + const TargetInstrInfo *TII) { for (auto &MI : llvm::make_early_inc_range(MBB)) { if (MI.isDebugValue() && MI.getDebugOperand(0).isReg() && !MI.isUndefDebugValue()) { - LLVM_DEBUG(dbgs() << "Warning: dangling DBG_VALUE nullified: " << MI + LLVM_DEBUG(dbgs() << "Warning: dangling DBG_VALUE set to undef: " << MI << "\n"); - MI.getDebugOperand(0).setReg(Register()); + MI.setDebugValueUndef(); } } } @@ -154,7 +154,7 @@ bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) { assert(Stack.empty() && "WebAssemblyDebugFixup: Stack not empty at end of basic block!"); - nullifyDanglingDebugValues(MBB, TII); + setDanglingDebugValuesUndef(MBB, TII); } return true; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp index 55be64ad7da0..fd510f85a8a3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp @@ -12,52 +12,388 @@ //===----------------------------------------------------------------------===// #include "WebAssemblyDebugValueManager.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssembly.h" #include "WebAssemblyMachineFunctionInfo.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/IR/DebugInfoMetadata.h" using namespace llvm; -WebAssemblyDebugValueManager::WebAssemblyDebugValueManager( - MachineInstr *Instr) { +WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) + : Def(Def) { // This code differs from MachineInstr::collectDebugValues in that it scans - // the whole BB, not just contiguous DBG_VALUEs. - if (!Instr->getOperand(0).isReg()) + // the whole BB, not just contiguous DBG_VALUEs, until another definition to + // the same register is encountered. + if (!Def->getOperand(0).isReg()) return; - CurrentReg = Instr->getOperand(0).getReg(); + CurrentReg = Def->getOperand(0).getReg(); - MachineBasicBlock::iterator DI = *Instr; - ++DI; - for (MachineBasicBlock::iterator DE = Instr->getParent()->end(); DI != DE; - ++DI) { - if (DI->isDebugValue() && - DI->hasDebugOperandForReg(Instr->getOperand(0).getReg())) - DbgValues.push_back(&*DI); + for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), + ME = Def->getParent()->end(); + MI != ME; ++MI) { + // If another definition appears, stop + if (MI->definesRegister(CurrentReg)) + break; + if (MI->isDebugValue() && MI->hasDebugOperandForReg(CurrentReg)) + DbgValues.push_back(&*MI); } } -void WebAssemblyDebugValueManager::move(MachineInstr *Insert) { - MachineBasicBlock *MBB = Insert->getParent(); - for (MachineInstr *DBI : reverse(DbgValues)) - MBB->splice(Insert, DBI->getParent(), DBI); +// Returns true if both A and B are the same CONST_I32/I64/F32/F64 instructions. +// Doesn't include CONST_V128. +static bool isSameScalarConst(const MachineInstr *A, const MachineInstr *B) { + if (A->getOpcode() != B->getOpcode() || + !WebAssembly::isScalarConst(A->getOpcode()) || + !WebAssembly::isScalarConst(B->getOpcode())) + return false; + const MachineOperand &OpA = A->getOperand(1), &OpB = B->getOperand(1); + if ((OpA.isImm() && OpB.isImm() && OpA.getImm() == OpB.getImm()) || + (OpA.isFPImm() && OpB.isFPImm() && OpA.getFPImm() == OpB.getFPImm()) || + (OpA.isGlobal() && OpB.isGlobal() && OpA.getGlobal() == OpB.getGlobal())) + return true; + return false; } -void WebAssemblyDebugValueManager::updateReg(unsigned Reg) { - for (auto *DBI : DbgValues) - for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) - MO.setReg(Reg); - CurrentReg = Reg; +SmallVector<MachineInstr *, 1> +WebAssemblyDebugValueManager::getSinkableDebugValues( + MachineInstr *Insert) const { + if (DbgValues.empty()) + return {}; + // DBG_VALUEs between Def and Insert + SmallVector<MachineInstr *, 8> DbgValuesInBetween; + + if (Def->getParent() == Insert->getParent()) { + // When Def and Insert are within the same BB, check if Insert comes after + // Def, because we only support sinking. + bool DefFirst = false; + for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), + ME = Def->getParent()->end(); + MI != ME; ++MI) { + if (&*MI == Insert) { + DefFirst = true; + break; + } + if (MI->isDebugValue()) + DbgValuesInBetween.push_back(&*MI); + } + if (!DefFirst) // Not a sink + return {}; + + } else { // Def and Insert are in different BBs + // If Def and Insert are in different BBs, we only handle a simple case in + // which Insert's BB is a successor of Def's BB. + if (!Def->getParent()->isSuccessor(Insert->getParent())) + return {}; + + // Gather DBG_VALUEs between 'Def~Def BB's end' and + // 'Insert BB's begin~Insert' + for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), + ME = Def->getParent()->end(); + MI != ME; ++MI) { + if (MI->isDebugValue()) + DbgValuesInBetween.push_back(&*MI); + } + for (MachineBasicBlock::iterator MI = Insert->getParent()->begin(), + ME = Insert->getIterator(); + MI != ME; ++MI) { + if (MI->isDebugValue()) + DbgValuesInBetween.push_back(&*MI); + } + } + + // Gather DebugVariables that are seen between Def and Insert, excluding our + // own DBG_VALUEs in DbgValues. + SmallDenseMap<DebugVariable, SmallVector<MachineInstr *, 2>> + SeenDbgVarToDbgValues; + for (auto *DV : DbgValuesInBetween) { + if (!llvm::is_contained(DbgValues, DV)) { + DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(), + DV->getDebugLoc()->getInlinedAt()); + SeenDbgVarToDbgValues[Var].push_back(DV); + } + } + + // Gather sinkable DBG_VALUEs. We should not sink a DBG_VALUE if there is + // another DBG_VALUE between Def and Insert referring to the same + // DebugVariable. For example, + // %0 = someinst + // DBG_VALUE %0, !"a", !DIExpression() // Should not sink with %0 + // %1 = anotherinst + // DBG_VALUE %1, !"a", !DIExpression() + // Where if %0 were to sink, the DBG_VAUE should not sink with it, as that + // would re-order assignments. + SmallVector<MachineInstr *, 1> SinkableDbgValues; + MachineRegisterInfo &MRI = Def->getParent()->getParent()->getRegInfo(); + for (auto *DV : DbgValues) { + DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(), + DV->getDebugLoc()->getInlinedAt()); + auto It = SeenDbgVarToDbgValues.find(Var); + if (It == SeenDbgVarToDbgValues.end()) { + SinkableDbgValues.push_back(DV); + continue; + } + if (!WebAssembly::isScalarConst(Def->getOpcode())) + continue; + auto &OverlappingDbgValues = It->second; + bool Sinkable = true; + for (auto *OverlappingDV : OverlappingDbgValues) { + MachineOperand &DbgOp = OverlappingDV->getDebugOperand(0); + if (!DbgOp.isReg()) { + Sinkable = false; + break; + } + Register OtherReg = DbgOp.getReg(); + MachineInstr *OtherDef = MRI.getUniqueVRegDef(OtherReg); + // We have an exception to allow encoutering other DBG_VALUEs with the + // smae DebugVariables, only when they are referring to the same scalar + // CONST instruction. For example, + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", !DIExpression() // Can sink with %0 + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"a", !DIExpression() + // When %0 were to be sunk/cloneed, the DBG_VALUE can be sunk/cloned with + // it because even though the second DBG_VALUE refers to the same + // DebugVariable, its value in effect is the same CONST instruction. + // + // This is to allow a case that can happen with RegStackify's + // "rematerializeCheapDef". For example, we have this program with two + // BBs: + // bb0: + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", ... + // ... + // INST0 ..., $0 ... + // bb1: + // INST1 ..., $0 ... + // INST2 ..., $0 ... + // + // We process bb0 first. Because %0 is used multiple times, %0 is cloned + // before INST0: + // bb0: + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", ... + // ... + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"a", ... + // INST0 ..., $1 ... + // + // And when we process bb1, we clone %0 and its DBG_VALUE again: + // bb0: + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", ... + // ... + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"a", ... + // INST0 ..., $1 ... + // bb1: + // %2 = CONST_I32 1 + // DBG_VALUE %2, !"a", ... // !!! + // INST1 ..., $2 ... + // %3 = CONST_I32 1 + // DBG_VALUE %3, !"a", ... // !!! + // INST2 ..., $3 ... + // + // But (without this exception) the cloned DBG_VALUEs marked with !!! are + // not possible to be cloned, because there is a previously cloned + // 'DBG_VALUE %1, !"a"' at the end of bb0 referring to the same + // DebugVariable "a". But in this case they are OK to be cloned, because + // the interfering DBG_VALUE is pointing to the same 'CONST_I32 1', + // because it was cloned from the same instruction. + if (!OtherDef || !isSameScalarConst(Def, OtherDef)) { + Sinkable = false; + break; + } + } + if (Sinkable) + SinkableDbgValues.push_back(DV); + } + return SinkableDbgValues; +} + +// Returns true if the insertion point is the same as the current place. +// Following DBG_VALUEs for 'Def' are ignored. +bool WebAssemblyDebugValueManager::isInsertSamePlace( + MachineInstr *Insert) const { + if (Def->getParent() != Insert->getParent()) + return false; + for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), + ME = Insert; + MI != ME; ++MI) { + if (!llvm::is_contained(DbgValues, MI)) { + return false; + } + } + return true; +} + +// Returns true if any instruction in MBB has the same debug location as DL. +// Also returns true if DL is an empty location. +static bool hasSameDebugLoc(const MachineBasicBlock *MBB, DebugLoc DL) { + for (const auto &MI : *MBB) + if (MI.getDebugLoc() == DL) + return true; + return false; } -void WebAssemblyDebugValueManager::clone(MachineInstr *Insert, - unsigned NewReg) { +// Sink 'Def', and also sink its eligible DBG_VALUEs to the place before +// 'Insert'. Convert the original DBG_VALUEs into undefs. +// +// For DBG_VALUEs to sink properly, if 'Def' and 'Insert' are within the same +// BB, 'Insert' should be below 'Def'; if they are in different BBs, 'Insert' +// should be in one of 'Def's BBs successors. Def will be sunk regardless of the +// location. +// +// This DebugValueManager's new Def and DbgValues will be updated to the newly +// sinked Def + DBG_VALUEs. +void WebAssemblyDebugValueManager::sink(MachineInstr *Insert) { + // In case Def is requested to be sunk to + // the same place, we don't need to do anything. If we actually do the sink, + // it will create unnecessary undef DBG_VALUEs. For example, if the original + // code is: + // %0 = someinst // Def + // DBG_VALUE %0, ... + // %1 = anotherinst // Insert + // + // If we actually sink %0 and the following DBG_VALUE and setting the original + // DBG_VALUE undef, the result will be: + // DBG_VALUE %noreg, ... // Unnecessary! + // %0 = someinst // Def + // DBG_VALUE %0, ... + // %1 = anotherinst // Insert + if (isInsertSamePlace(Insert)) + return; + MachineBasicBlock *MBB = Insert->getParent(); MachineFunction *MF = MBB->getParent(); - for (MachineInstr *DBI : reverse(DbgValues)) { - MachineInstr *Clone = MF->CloneMachineInstr(DBI); - for (auto &MO : Clone->getDebugOperandsForReg(CurrentReg)) - MO.setReg(NewReg); + + // Get the list of sinkable DBG_VALUEs. This should be done before sinking + // Def, because we need to examine instructions between Def and Insert. + SmallVector<MachineInstr *, 1> SinkableDbgValues = + getSinkableDebugValues(Insert); + + // Sink Def first. + // + // When moving to a different BB, we preserve the debug loc only if the + // destination BB contains the same location. See + // https://llvm.org/docs/HowToUpdateDebugInfo.html#when-to-preserve-an-instruction-location. + if (Def->getParent() != MBB && !hasSameDebugLoc(MBB, Def->getDebugLoc())) + Def->setDebugLoc(DebugLoc()); + MBB->splice(Insert, Def->getParent(), Def); + + if (DbgValues.empty()) + return; + + // Clone sinkable DBG_VALUEs and insert them. + SmallVector<MachineInstr *, 1> NewDbgValues; + for (MachineInstr *DV : SinkableDbgValues) { + MachineInstr *Clone = MF->CloneMachineInstr(DV); MBB->insert(Insert, Clone); + NewDbgValues.push_back(Clone); + } + + // When sinking a Def and its DBG_VALUEs, we shouldn't just remove the + // original DBG_VALUE instructions; we should set them to undef not to create + // an impossible combination of variable assignments in the original program. + // For example, this is the original program in order: + // %0 = CONST_I32 0 + // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 + // %2 = CONST_I32 2 + // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 1 + // %3 = CONST_I32 3 + // DBG_VALUE %3, !"b", !DIExpression() // a = 2, b = 3 + // + // If %2 were to sink below %3, if we just sink DBG_VALUE %1 with it, the + // debug info will show the variable "b" is updated to 2, creating the + // variable assignment combination of (a = 0, b = 3), which is not possible in + // the original program: + // %0 = CONST_I32 0 + // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 + // %3 = CONST_I32 3 + // DBG_VALUE %3, !"b", !DIExpression() // a = 0, b = 3 (Incorrect!) + // %2 = CONST_I32 2 + // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 3 + // + // To fix this,we leave an undef DBG_VALUE in its original place, so that the + // result will be + // %0 = CONST_I32 0 + // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 + // DBG_VALUE $noreg, !"a", !DIExpression() // a = ?, b = 1 + // %3 = CONST_I32 3 + // DBG_VALUE %3, !"b", !DIExpression() // a = ?, b = 3 + // %2 = CONST_I32 2 + // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 3 + // Now in the middle "a" will be shown as "optimized out", but it wouldn't + // show the impossible combination of (a = 0, b = 3). + for (MachineInstr *DV : DbgValues) + DV->setDebugValueUndef(); + + DbgValues.swap(NewDbgValues); +} + +// Clone 'Def', and also clone its eligible DBG_VALUEs to the place before +// 'Insert'. +// +// For DBG_VALUEs to be cloned properly, if 'Def' and 'Insert' are within the +// same BB, 'Insert' should be below 'Def'; if they are in different BBs, +// 'Insert' should be in one of 'Def's BBs successors. Def will be cloned +// regardless of the location. +// +// If NewReg is not $noreg, the newly cloned DBG_VALUEs will have the new +// register as its operand. +void WebAssemblyDebugValueManager::cloneSink(MachineInstr *Insert, + Register NewReg, + bool CloneDef) const { + MachineBasicBlock *MBB = Insert->getParent(); + MachineFunction *MF = MBB->getParent(); + + SmallVector<MachineInstr *> SinkableDbgValues = + getSinkableDebugValues(Insert); + + // Clone Def first. + if (CloneDef) { + MachineInstr *Clone = MF->CloneMachineInstr(Def); + // When cloning to a different BB, we preserve the debug loc only if the + // destination BB contains the same location. See + // https://llvm.org/docs/HowToUpdateDebugInfo.html#when-to-preserve-an-instruction-location. + if (Def->getParent() != MBB && !hasSameDebugLoc(MBB, Def->getDebugLoc())) + Clone->setDebugLoc(DebugLoc()); + if (NewReg != CurrentReg && NewReg.isValid()) + Clone->getOperand(0).setReg(NewReg); + MBB->insert(Insert, Clone); + } + + if (DbgValues.empty()) + return; + + // Clone sinkable DBG_VALUEs and insert them. + SmallVector<MachineInstr *, 1> NewDbgValues; + for (MachineInstr *DV : SinkableDbgValues) { + MachineInstr *Clone = MF->CloneMachineInstr(DV); + MBB->insert(Insert, Clone); + NewDbgValues.push_back(Clone); + } + + if (NewReg != CurrentReg && NewReg.isValid()) + for (auto *DBI : NewDbgValues) + for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) + MO.setReg(NewReg); +} + +// Update the register for Def and DBG_VALUEs. +void WebAssemblyDebugValueManager::updateReg(Register Reg) { + if (Reg != CurrentReg && Reg.isValid()) { + for (auto *DBI : DbgValues) + for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) + MO.setReg(Reg); + CurrentReg = Reg; + Def->getOperand(0).setReg(Reg); } } @@ -70,3 +406,10 @@ void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) { MO.ChangeToTargetIndex(IndexType, LocalId); } } + +// Remove Def, and set its DBG_VALUEs to undef. +void WebAssemblyDebugValueManager::removeDef() { + Def->removeFromParent(); + for (MachineInstr *DV : DbgValues) + DV->setDebugValueUndef(); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h index c2dd56909304..9ef3da758947 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h @@ -9,6 +9,9 @@ /// \file /// This file contains the declaration of the WebAssembly-specific /// manager for DebugValues associated with the specific MachineInstr. +/// This pass currently does not handle DBG_VALUE_LISTs; they are assumed to +/// have been set to undef in NullifyDebugValueLists pass. +/// TODO Handle DBG_VALUE_LIST /// //===----------------------------------------------------------------------===// @@ -16,22 +19,37 @@ #define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYDEBUGVALUEMANAGER_H #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/Register.h" namespace llvm { class MachineInstr; class WebAssemblyDebugValueManager { - SmallVector<MachineInstr *, 2> DbgValues; - unsigned CurrentReg; + MachineInstr *Def; + SmallVector<MachineInstr *, 1> DbgValues; + Register CurrentReg; + SmallVector<MachineInstr *, 1> + getSinkableDebugValues(MachineInstr *Insert) const; + bool isInsertSamePlace(MachineInstr *Insert) const; public: - WebAssemblyDebugValueManager(MachineInstr *Instr); + WebAssemblyDebugValueManager(MachineInstr *Def); - void move(MachineInstr *Insert); - void updateReg(unsigned Reg); - void clone(MachineInstr *Insert, unsigned NewReg); + // Sink 'Def', and also sink its eligible DBG_VALUEs to the place before + // 'Insert'. Convert the original DBG_VALUEs into undefs. + void sink(MachineInstr *Insert); + // Clone 'Def' (optionally), and also clone its eligible DBG_VALUEs to the + // place before 'Insert'. + void cloneSink(MachineInstr *Insert, Register NewReg = Register(), + bool CloneDef = true) const; + // Update the register for Def and DBG_VALUEs. + void updateReg(Register Reg); + // Replace the current register in DBG_VALUEs with the given LocalId target + // index. void replaceWithLocal(unsigned LocalId); + // Remove Def, and set its DBG_VALUEs to undef. + void removeDef(); }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp index 7e63b6b97632..ab3512cfd640 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp @@ -121,6 +121,7 @@ void WebAssemblyExceptionInfo::recalculate( // and A's unwind destination is B and B's is C. When we visit B before A, we // end up extracting C only out of B but not out of A. const auto *EHInfo = MF.getWasmEHFuncInfo(); + assert(EHInfo); SmallVector<std::pair<WebAssemblyException *, WebAssemblyException *>> UnwindWEVec; for (auto *DomNode : depth_first(&MDT)) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp index eeec0fc671cc..84fd34d73b63 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -267,15 +267,42 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { // Replace tee instructions with local.tee. The difference is that tee // instructions have two defs, while local.tee instructions have one def // and an index of a local to write to. + // + // - Before: + // TeeReg, Reg = TEE DefReg + // INST ..., TeeReg, ... + // INST ..., Reg, ... + // INST ..., Reg, ... + // * DefReg: may or may not be stackified + // * Reg: not stackified + // * TeeReg: stackified + // + // - After (when DefReg was already stackified): + // TeeReg = LOCAL_TEE LocalId1, DefReg + // INST ..., TeeReg, ... + // INST ..., Reg, ... + // INST ..., Reg, ... + // * Reg: mapped to LocalId1 + // * TeeReg: stackified + // + // - After (when DefReg was not already stackified): + // NewReg = LOCAL_GET LocalId1 + // TeeReg = LOCAL_TEE LocalId2, NewReg + // INST ..., TeeReg, ... + // INST ..., Reg, ... + // INST ..., Reg, ... + // * DefReg: mapped to LocalId1 + // * Reg: mapped to LocalId2 + // * TeeReg: stackified if (WebAssembly::isTee(MI.getOpcode())) { assert(MFI.isVRegStackified(MI.getOperand(0).getReg())); assert(!MFI.isVRegStackified(MI.getOperand(1).getReg())); - Register OldReg = MI.getOperand(2).getReg(); - const TargetRegisterClass *RC = MRI.getRegClass(OldReg); + Register DefReg = MI.getOperand(2).getReg(); + const TargetRegisterClass *RC = MRI.getRegClass(DefReg); // Stackify the input if it isn't stackified yet. - if (!MFI.isVRegStackified(OldReg)) { - unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg); + if (!MFI.isVRegStackified(DefReg)) { + unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, DefReg); Register NewReg = MRI.createVirtualRegister(RC); unsigned Opc = getLocalGetOpcode(RC); BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg) @@ -352,7 +379,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg); // If this register operand is tied to another operand, we can't // change it to an immediate. Untie it first. - MI.untieRegOperand(MI.getOperandNo(&MO)); + MI.untieRegOperand(MO.getOperandNo()); MO.ChangeToImmediate(LocalId); continue; } @@ -369,7 +396,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { if (MI.isInlineAsm()) { unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg); // Untie it first if this reg operand is tied to another operand. - MI.untieRegOperand(MI.getOperandNo(&MO)); + MI.untieRegOperand(MO.getOperandNo()); MO.ChangeToImmediate(LocalId); continue; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index df79e55ce4b6..9aacddb0187e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -249,8 +249,22 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { SmallVector<SDValue, 16> Ops; for (size_t i = 1; i < Node->getNumOperands(); ++i) { SDValue Op = Node->getOperand(i); - if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) - Op = Op->getOperand(0); + // Remove the wrapper when the call target is a function, an external + // symbol (which will be lowered to a library function), or an alias of + // a function. If the target is not a function/external symbol, we + // shouldn't remove the wrapper, because we cannot call it directly and + // instead we want it to be loaded with a CONST instruction and called + // with a call_indirect later. + if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) { + SDValue NewOp = Op->getOperand(0); + if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(NewOp.getNode())) { + if (isa<Function>( + GlobalOp->getGlobal()->stripPointerCastsAndAliases())) + Op = NewOp; + } else if (isa<ExternalSymbolSDNode>(NewOp.getNode())) { + Op = NewOp; + } + } Ops.push_back(Op); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 94544800a6fb..f00d02ad4190 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -125,8 +125,8 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( setOperationAction(Op, T, Expand); // Note supported floating-point library function operators that otherwise // default to expand. - for (auto Op : - {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT}) + for (auto Op : {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, + ISD::FRINT, ISD::FROUNDEVEN}) setOperationAction(Op, T, Legal); // Support minimum and maximum, which otherwise default to expand. setOperationAction(ISD::FMINIMUM, T, Legal); @@ -157,6 +157,12 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // SIMD-specific configuration if (Subtarget->hasSIMD128()) { + // Combine vector mask reductions into alltrue/anytrue + setTargetDAGCombine(ISD::SETCC); + + // Convert vector to integer bitcasts to bitmask + setTargetDAGCombine(ISD::BITCAST); + // Hoist bitcasts out of shuffles setTargetDAGCombine(ISD::VECTOR_SHUFFLE); @@ -196,7 +202,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // Support splatting for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, - MVT::v2f64}) + MVT::v2f64}) setOperationAction(ISD::SPLAT_VECTOR, T, Legal); // Custom lowering since wasm shifts must have a scalar shift amount @@ -241,7 +247,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // Expand float operations supported for scalars but not SIMD for (auto Op : {ISD::FCOPYSIGN, ISD::FLOG, ISD::FLOG2, ISD::FLOG10, - ISD::FEXP, ISD::FEXP2, ISD::FRINT}) + ISD::FEXP, ISD::FEXP2}) for (auto T : {MVT::v4f32, MVT::v2f64}) setOperationAction(Op, T, Expand); @@ -258,6 +264,12 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // But saturating fp_to_int converstions are for (auto Op : {ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT}) setOperationAction(Op, MVT::v4i32, Custom); + + // Support vector extending + for (auto T : MVT::integer_fixedlen_vector_valuetypes()) { + setOperationAction(ISD::SIGN_EXTEND_VECTOR_INREG, T, Custom); + setOperationAction(ISD::ZERO_EXTEND_VECTOR_INREG, T, Custom); + } } // As a special case, these operators use the type to mean the type to @@ -534,11 +546,12 @@ LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB, assert(CallResults.getOpcode() == WebAssembly::CALL_RESULTS || CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS); - bool IsIndirect = CallParams.getOperand(0).isReg(); + bool IsIndirect = + CallParams.getOperand(0).isReg() || CallParams.getOperand(0).isFI(); bool IsRetCall = CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS; bool IsFuncrefCall = false; - if (IsIndirect) { + if (IsIndirect && CallParams.getOperand(0).isReg()) { Register Reg = CallParams.getOperand(0).getReg(); const MachineFunction *MF = BB->getParent(); const MachineRegisterInfo &MRI = MF->getRegInfo(); @@ -1201,8 +1214,8 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, // Lastly, if this is a call to a funcref we need to add an instruction // table.set to the chain and transform the call. - if (CLI.CB && - WebAssembly::isFuncrefType(CLI.CB->getCalledOperand()->getType())) { + if (CLI.CB && WebAssembly::isWebAssemblyFuncrefType( + CLI.CB->getCalledOperand()->getType())) { // In the absence of function references proposal where a funcref call is // lowered to call_ref, using reference types we generate a table.set to set // the funcref to a special table used solely for this purpose, followed by @@ -1373,6 +1386,11 @@ void WebAssemblyTargetLowering::ReplaceNodeResults( // SIGN_EXTEND_INREG, but for non-vector sign extends the result might be an // illegal type. break; + case ISD::SIGN_EXTEND_VECTOR_INREG: + case ISD::ZERO_EXTEND_VECTOR_INREG: + // Do not add any results, signifying that N should not be custom lowered. + // EXTEND_VECTOR_INREG is implemented for some vectors, but not all. + break; default: llvm_unreachable( "ReplaceNodeResults not implemented for this op for WebAssembly!"); @@ -1423,6 +1441,9 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, return LowerIntrinsic(Op, DAG); case ISD::SIGN_EXTEND_INREG: return LowerSIGN_EXTEND_INREG(Op, DAG); + case ISD::ZERO_EXTEND_VECTOR_INREG: + case ISD::SIGN_EXTEND_VECTOR_INREG: + return LowerEXTEND_VECTOR_INREG(Op, DAG); case ISD::BUILD_VECTOR: return LowerBUILD_VECTOR(Op, DAG); case ISD::VECTOR_SHUFFLE: @@ -1822,7 +1843,8 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, const SDValue &MaskIdx = Op.getOperand(OpIdx + 1); if (MaskIdx.isUndef() || cast<ConstantSDNode>(MaskIdx.getNode())->getZExtValue() >= 32) { - Ops[OpIdx++] = DAG.getConstant(0, DL, MVT::i32); + bool isTarget = MaskIdx.getNode()->getOpcode() == ISD::TargetConstant; + Ops[OpIdx++] = DAG.getConstant(0, DL, MVT::i32, isTarget); } else { Ops[OpIdx++] = MaskIdx; } @@ -1875,6 +1897,48 @@ WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, Op.getOperand(1)); } +SDValue +WebAssemblyTargetLowering::LowerEXTEND_VECTOR_INREG(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT VT = Op.getValueType(); + SDValue Src = Op.getOperand(0); + EVT SrcVT = Src.getValueType(); + + if (SrcVT.getVectorElementType() == MVT::i1 || + SrcVT.getVectorElementType() == MVT::i64) + return SDValue(); + + assert(VT.getScalarSizeInBits() % SrcVT.getScalarSizeInBits() == 0 && + "Unexpected extension factor."); + unsigned Scale = VT.getScalarSizeInBits() / SrcVT.getScalarSizeInBits(); + + if (Scale != 2 && Scale != 4 && Scale != 8) + return SDValue(); + + unsigned Ext; + switch (Op.getOpcode()) { + case ISD::ZERO_EXTEND_VECTOR_INREG: + Ext = WebAssemblyISD::EXTEND_LOW_U; + break; + case ISD::SIGN_EXTEND_VECTOR_INREG: + Ext = WebAssemblyISD::EXTEND_LOW_S; + break; + } + + SDValue Ret = Src; + while (Scale != 1) { + Ret = DAG.getNode(Ext, DL, + Ret.getValueType() + .widenIntegerVectorElementType(*DAG.getContext()) + .getHalfNumVectorElementsVT(*DAG.getContext()), + Ret); + Scale /= 2; + } + assert(Ret.getValueType() == VT); + return Ret; +} + static SDValue LowerConvertLow(SDValue Op, SelectionDAG &DAG) { SDLoc DL(Op); if (Op.getValueType() != MVT::v2f64) @@ -2150,7 +2214,8 @@ SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, assert((LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) && "Unexpected out of bounds negative value"); if (Const && LaneBits != 64 && Val > (1ll << (LaneBits - 1)) - 1) { - auto NewVal = ((uint64_t)Val % (1ll << LaneBits)) - (1ll << LaneBits); + uint64_t Mask = (1ll << LaneBits) - 1; + auto NewVal = (((uint64_t)Val & Mask) - (1ll << LaneBits)) & Mask; ConstLanes.push_back(DAG.getConstant(NewVal, SDLoc(Lane), LaneT)); } else { ConstLanes.push_back(Lane); @@ -2240,7 +2305,7 @@ WebAssemblyTargetLowering::LowerAccessVectorElement(SDValue Op, SelectionDAG &DAG) const { // Allow constant lane indices, expand variable lane indices SDNode *IdxNode = Op.getOperand(Op.getNumOperands() - 1).getNode(); - if (isa<ConstantSDNode>(IdxNode) || IdxNode->isUndef()) { + if (isa<ConstantSDNode>(IdxNode)) { // Ensure the index type is i32 to match the tablegen patterns uint64_t Idx = cast<ConstantSDNode>(IdxNode)->getZExtValue(); SmallVector<SDValue, 3> Ops(Op.getNode()->ops()); @@ -2287,10 +2352,43 @@ SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op, // Only manually lower vector shifts assert(Op.getSimpleValueType().isVector()); - auto ShiftVal = DAG.getSplatValue(Op.getOperand(1)); + uint64_t LaneBits = Op.getValueType().getScalarSizeInBits(); + auto ShiftVal = Op.getOperand(1); + + // Try to skip bitmask operation since it is implied inside shift instruction + auto SkipImpliedMask = [](SDValue MaskOp, uint64_t MaskBits) { + if (MaskOp.getOpcode() != ISD::AND) + return MaskOp; + SDValue LHS = MaskOp.getOperand(0); + SDValue RHS = MaskOp.getOperand(1); + if (MaskOp.getValueType().isVector()) { + APInt MaskVal; + if (!ISD::isConstantSplatVector(RHS.getNode(), MaskVal)) + std::swap(LHS, RHS); + + if (ISD::isConstantSplatVector(RHS.getNode(), MaskVal) && + MaskVal == MaskBits) + MaskOp = LHS; + } else { + if (!isa<ConstantSDNode>(RHS.getNode())) + std::swap(LHS, RHS); + + auto ConstantRHS = dyn_cast<ConstantSDNode>(RHS.getNode()); + if (ConstantRHS && ConstantRHS->getAPIntValue() == MaskBits) + MaskOp = LHS; + } + + return MaskOp; + }; + + // Skip vector and operation + ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1); + ShiftVal = DAG.getSplatValue(ShiftVal); if (!ShiftVal) return unrollVectorShift(Op, DAG); + // Skip scalar and operation + ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1); // Use anyext because none of the high bits can affect the shift ShiftVal = DAG.getAnyExtOrTrunc(ShiftVal, DL, MVT::i32); @@ -2656,12 +2754,92 @@ static SDValue performTruncateCombine(SDNode *N, return truncateVectorWithNARROW(OutVT, In, DL, DAG); } +static SDValue performBitcastCombine(SDNode *N, + TargetLowering::DAGCombinerInfo &DCI) { + auto &DAG = DCI.DAG; + SDLoc DL(N); + SDValue Src = N->getOperand(0); + EVT VT = N->getValueType(0); + EVT SrcVT = Src.getValueType(); + + // bitcast <N x i1> to iN + // ==> bitmask + if (DCI.isBeforeLegalize() && VT.isScalarInteger() && + SrcVT.isFixedLengthVector() && SrcVT.getScalarType() == MVT::i1) { + unsigned NumElts = SrcVT.getVectorNumElements(); + if (NumElts != 2 && NumElts != 4 && NumElts != 8 && NumElts != 16) + return SDValue(); + EVT Width = MVT::getIntegerVT(128 / NumElts); + return DAG.getZExtOrTrunc( + DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, + {DAG.getConstant(Intrinsic::wasm_bitmask, DL, MVT::i32), + DAG.getSExtOrTrunc(N->getOperand(0), DL, + SrcVT.changeVectorElementType(Width))}), + DL, VT); + } + + return SDValue(); +} + +static SDValue performSETCCCombine(SDNode *N, + TargetLowering::DAGCombinerInfo &DCI) { + auto &DAG = DCI.DAG; + + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(2))->get(); + SDLoc DL(N); + EVT VT = N->getValueType(0); + + // setcc (iN (bitcast (vNi1 X))), 0, ne + // ==> any_true (vNi1 X) + // setcc (iN (bitcast (vNi1 X))), 0, eq + // ==> xor (any_true (vNi1 X)), -1 + // setcc (iN (bitcast (vNi1 X))), -1, eq + // ==> all_true (vNi1 X) + // setcc (iN (bitcast (vNi1 X))), -1, ne + // ==> xor (all_true (vNi1 X)), -1 + if (DCI.isBeforeLegalize() && VT.isScalarInteger() && + (Cond == ISD::SETEQ || Cond == ISD::SETNE) && + (isNullConstant(RHS) || isAllOnesConstant(RHS)) && + LHS->getOpcode() == ISD::BITCAST) { + EVT FromVT = LHS->getOperand(0).getValueType(); + if (FromVT.isFixedLengthVector() && + FromVT.getVectorElementType() == MVT::i1) { + int Intrin = isNullConstant(RHS) ? Intrinsic::wasm_anytrue + : Intrinsic::wasm_alltrue; + unsigned NumElts = FromVT.getVectorNumElements(); + if (NumElts != 2 && NumElts != 4 && NumElts != 8 && NumElts != 16) + return SDValue(); + EVT Width = MVT::getIntegerVT(128 / NumElts); + SDValue Ret = DAG.getZExtOrTrunc( + DAG.getNode( + ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, + {DAG.getConstant(Intrin, DL, MVT::i32), + DAG.getSExtOrTrunc(LHS->getOperand(0), DL, + FromVT.changeVectorElementType(Width))}), + DL, MVT::i1); + if ((isNullConstant(RHS) && (Cond == ISD::SETEQ)) || + (isAllOnesConstant(RHS) && (Cond == ISD::SETNE))) { + Ret = DAG.getNOT(DL, Ret, MVT::i1); + } + return DAG.getZExtOrTrunc(Ret, DL, VT); + } + } + + return SDValue(); +} + SDValue WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { switch (N->getOpcode()) { default: return SDValue(); + case ISD::BITCAST: + return performBitcastCombine(N, DCI); + case ISD::SETCC: + return performSETCCCombine(N, DCI); case ISD::VECTOR_SHUFFLE: return performVECTOR_SHUFFLECombine(N, DCI); case ISD::SIGN_EXTEND: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h index f70f85fe6ddd..ecf5d5b1ea5d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -131,6 +131,7 @@ private: SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const; SDValue LowerIntrinsic(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSIGN_EXTEND_INREG(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerEXTEND_VECTOR_INREG(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; @@ -140,11 +141,6 @@ private: SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const; SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const; - // Helper for LoadLoad and LowerStore - bool MatchTableForLowering(SelectionDAG &DAG, const SDLoc &DL, - const SDValue &Base, GlobalAddressSDNode *&GA, - SDValue &Idx) const; - // Custom DAG combine hooks SDValue PerformDAGCombine(SDNode *N, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td index 6a123f8f4030..ca9a5ef9dda1 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -73,7 +73,7 @@ defm RET_CALL : "return_call \t$callee", "return_call\t$callee", 0x12>, Requires<[HasTailCall]>; -let isReturn = 1 in +let isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in defm RET_CALL_INDIRECT : I<(outs), (ins TypeIndex:$type, table32_op:$table, variable_ops), (outs), (ins TypeIndex:$type, table32_op:$table), [], diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td index 104f5f7d2e68..cc9a9f86f683 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td @@ -76,6 +76,10 @@ def : Pat<(fcopysign F32:$lhs, F64:$rhs), def : Pat<(frint f32:$src), (NEAREST_F32 f32:$src)>; def : Pat<(frint f64:$src), (NEAREST_F64 f64:$src)>; +// WebAssembly always rounds ties-to-even, so map froundeven to fnearbyint. +def : Pat<(froundeven f32:$src), (NEAREST_F32 f32:$src)>; +def : Pat<(froundeven f64:$src), (NEAREST_F64 f64:$src)>; + let isCommutable = 1 in { defm EQ : ComparisonFP<SETOEQ, "eq ", 0x5b, 0x61>; defm NE : ComparisonFP<SETUNE, "ne ", 0x5c, 0x62>; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td index ad2ec40b8b31..8cd41d7017a0 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -376,7 +376,7 @@ multiclass SIMDStoreLane<Vec vec, bits<32> simdop> { [], name#"\t${off}(${addr})$p2align, $vec, $idx", name#"\t$off$p2align, $idx", simdop>; defm STORE_LANE_#vec#_A64 : - SIMD_I<(outs V128:$dst), + SIMD_I<(outs), (ins P2Align:$p2align, offset64_op:$off, vec_i8imm_op:$idx, I64:$addr, V128:$vec), (outs), (ins P2Align:$p2align, offset64_op:$off, vec_i8imm_op:$idx), @@ -529,6 +529,22 @@ defm SHUFFLE : def wasm_shuffle_t : SDTypeProfile<1, 18, []>; def wasm_shuffle : SDNode<"WebAssemblyISD::SHUFFLE", wasm_shuffle_t>; foreach vec = AllVecs in { +// The @llvm.wasm.shuffle intrinsic has immediate arguments that become TargetConstants. +def : Pat<(vec.vt (wasm_shuffle (vec.vt V128:$x), (vec.vt V128:$y), + (i32 timm:$m0), (i32 timm:$m1), + (i32 timm:$m2), (i32 timm:$m3), + (i32 timm:$m4), (i32 timm:$m5), + (i32 timm:$m6), (i32 timm:$m7), + (i32 timm:$m8), (i32 timm:$m9), + (i32 timm:$mA), (i32 timm:$mB), + (i32 timm:$mC), (i32 timm:$mD), + (i32 timm:$mE), (i32 timm:$mF))), + (SHUFFLE $x, $y, + imm:$m0, imm:$m1, imm:$m2, imm:$m3, + imm:$m4, imm:$m5, imm:$m6, imm:$m7, + imm:$m8, imm:$m9, imm:$mA, imm:$mB, + imm:$mC, imm:$mD, imm:$mE, imm:$mF)>; +// Normal shufflevector instructions may have normal constant arguemnts. def : Pat<(vec.vt (wasm_shuffle (vec.vt V128:$x), (vec.vt V128:$y), (i32 LaneIdx32:$m0), (i32 LaneIdx32:$m1), (i32 LaneIdx32:$m2), (i32 LaneIdx32:$m3), @@ -971,6 +987,12 @@ def : Pat<(wasm_shr_s (v4i32 V128:$lhs), (and I32:$rhs, 31)), def : Pat<(wasm_shr_u (v4i32 V128:$lhs), (and I32:$rhs, 31)), (SHR_U_I32x4 V128:$lhs, I32:$rhs)>; +def : Pat<(wasm_shl (v2i64 V128:$lhs), (and I32:$rhs, 63)), + (SHL_I64x2 V128:$lhs, I32:$rhs)>; +def : Pat<(wasm_shr_s (v2i64 V128:$lhs), (and I32:$rhs, 63)), + (SHR_S_I64x2 V128:$lhs, I32:$rhs)>; +def : Pat<(wasm_shr_u (v2i64 V128:$lhs), (and I32:$rhs, 63)), + (SHR_U_I64x2 V128:$lhs, I32:$rhs)>; def : Pat<(wasm_shl (v2i64 V128:$lhs), (trunc (and I64:$rhs, 63))), (SHL_I64x2 V128:$lhs, (I32_WRAP_I64 I64:$rhs))>; def : Pat<(wasm_shr_s (v2i64 V128:$lhs), (trunc (and I64:$rhs, 63))), @@ -1136,6 +1158,14 @@ defm FLOOR : SIMDUnary<F64x2, ffloor, "floor", 0x75>; defm TRUNC: SIMDUnary<F64x2, ftrunc, "trunc", 0x7a>; defm NEAREST: SIMDUnary<F64x2, fnearbyint, "nearest", 0x94>; +// WebAssembly doesn't expose inexact exceptions, so map frint to fnearbyint. +def : Pat<(v4f32 (frint (v4f32 V128:$src))), (NEAREST_F32x4 V128:$src)>; +def : Pat<(v2f64 (frint (v2f64 V128:$src))), (NEAREST_F64x2 V128:$src)>; + +// WebAssembly always rounds ties-to-even, so map froundeven to fnearbyint. +def : Pat<(v4f32 (froundeven (v4f32 V128:$src))), (NEAREST_F32x4 V128:$src)>; +def : Pat<(v2f64 (froundeven (v2f64 V128:$src))), (NEAREST_F64x2 V128:$src)>; + //===----------------------------------------------------------------------===// // Floating-point binary arithmetic //===----------------------------------------------------------------------===// @@ -1166,13 +1196,21 @@ defm MIN : SIMDBinaryFP<fminimum, "min", 232>; defm MAX : SIMDBinaryFP<fmaximum, "max", 233>; // Pseudo-minimum: pmin -def pmin : PatFrag<(ops node:$lhs, node:$rhs), - (vselect (setolt $rhs, $lhs), $rhs, $lhs)>; +def pmin : PatFrags<(ops node:$lhs, node:$rhs), [ + (vselect (setolt $rhs, $lhs), $rhs, $lhs), + (vselect (setole $rhs, $lhs), $rhs, $lhs), + (vselect (setogt $lhs, $rhs), $rhs, $lhs), + (vselect (setoge $lhs, $rhs), $rhs, $lhs) +]>; defm PMIN : SIMDBinaryFP<pmin, "pmin", 234>; // Pseudo-maximum: pmax -def pmax : PatFrag<(ops node:$lhs, node:$rhs), - (vselect (setolt $lhs, $rhs), $rhs, $lhs)>; +def pmax : PatFrags<(ops node:$lhs, node:$rhs), [ + (vselect (setogt $rhs, $lhs), $rhs, $lhs), + (vselect (setoge $rhs, $lhs), $rhs, $lhs), + (vselect (setolt $lhs, $rhs), $rhs, $lhs), + (vselect (setole $lhs, $rhs), $rhs, $lhs) +]>; defm PMAX : SIMDBinaryFP<pmax, "pmax", 235>; // Also match the pmin/pmax cases where the operands are int vectors (but the diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index 5faa098b94ad..4b8fdcf3a5b3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -267,7 +267,7 @@ /// ///===----------------------------------------------------------------------===// -#include "Utils/WebAssemblyUtilities.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssembly.h" #include "WebAssemblyTargetMachine.h" #include "llvm/ADT/StringExtras.h" @@ -580,7 +580,7 @@ Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallBase *CI) { FunctionType *CalleeFTy = CI->getFunctionType(); std::string Sig = getSignature(CalleeFTy); - if (InvokeWrappers.find(Sig) != InvokeWrappers.end()) + if (InvokeWrappers.contains(Sig)) return InvokeWrappers[Sig]; // Put the pointer to the callee as first argument @@ -1217,7 +1217,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) { Constant *Clause = LPI->getClause(I); // TODO Handle filters (= exception specifications). - // https://bugs.llvm.org/show_bug.cgi?id=50396 + // https://github.com/llvm/llvm-project/issues/49740 if (LPI->isCatch(I)) FMCArgs.push_back(Clause); } @@ -1726,10 +1726,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( // that requires multivalue support in the toolchain, which is currently not // very reliable. We instead throw and catch a pointer to a struct value of // type 'struct __WasmLongjmpArgs', which is defined in Emscripten. - Instruction *CatchCI = + Instruction *LongjmpArgs = IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::C_LONGJMP)}, "thrown"); - Value *LongjmpArgs = - IRB.CreateBitCast(CatchCI, LongjmpArgsTy->getPointerTo(), "longjmp.args"); Value *EnvField = IRB.CreateConstGEP2_32(LongjmpArgsTy, LongjmpArgs, 0, 0, "env_gep"); Value *ValField = diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp index 6fd87f10150d..94b6e41e87d0 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp @@ -62,8 +62,9 @@ bool WebAssemblyLowerRefTypesIntPtrConv::runOnFunction(Function &F) { for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { PtrToIntInst *PTI = dyn_cast<PtrToIntInst>(&*I); IntToPtrInst *ITP = dyn_cast<IntToPtrInst>(&*I); - if (!(PTI && WebAssembly::isRefType(PTI->getPointerOperand()->getType())) && - !(ITP && WebAssembly::isRefType(ITP->getDestTy()))) + if (!(PTI && WebAssembly::isWebAssemblyReferenceType( + PTI->getPointerOperand()->getType())) && + !(ITP && WebAssembly::isWebAssemblyReferenceType(ITP->getDestTy()))) continue; UndefValue *U = UndefValue::get(I->getType()); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 85ece58f98b3..5ceeebdeab5e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -140,8 +140,8 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO, } MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand( - SmallVector<wasm::ValType, 1> &&Returns, - SmallVector<wasm::ValType, 4> &&Params) const { + SmallVectorImpl<wasm::ValType> &&Returns, + SmallVectorImpl<wasm::ValType> &&Params) const { auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params)); MCSymbol *Sym = Printer.createTempSymbol("typeindex"); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h index d79c54097eb7..9f08499e5cde 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h @@ -34,8 +34,8 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower { MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; - MCOperand lowerTypeIndexOperand(SmallVector<wasm::ValType, 1> &&, - SmallVector<wasm::ValType, 4> &&) const; + MCOperand lowerTypeIndexOperand(SmallVectorImpl<wasm::ValType> &&, + SmallVectorImpl<wasm::ValType> &&) const; public: WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp index 5d8c58dcc334..b58f7a0152ae 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp @@ -9,7 +9,7 @@ /// \file /// Nullify DBG_VALUE_LISTs instructions as a temporary measure before we /// implement DBG_VALUE_LIST handling in WebAssemblyDebugValueManager. -/// See https://bugs.llvm.org/show_bug.cgi?id=50361. +/// See https://github.com/llvm/llvm-project/issues/49705. /// TODO Correctly handle DBG_VALUE_LISTs /// //===----------------------------------------------------------------------===// @@ -48,22 +48,17 @@ bool WebAssemblyNullifyDebugValueLists::runOnMachineFunction( LLVM_DEBUG(dbgs() << "********** Nullify DBG_VALUE_LISTs **********\n" "********** Function: " << MF.getName() << '\n'); - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - SmallVector<MachineInstr *, 2> DbgValueLists; - for (auto &MBB : MF) - for (auto &MI : MBB) - if (MI.getOpcode() == TargetOpcode::DBG_VALUE_LIST) - DbgValueLists.push_back(&MI); - + bool Changed = false; // Our backend, including WebAssemblyDebugValueManager, currently cannot - // handle DBG_VALUE_LISTs correctly. So this converts DBG_VALUE_LISTs to - // "DBG_VALUE $noreg", which will appear as "optimized out". - for (auto *DVL : DbgValueLists) { - BuildMI(*DVL->getParent(), DVL, DVL->getDebugLoc(), - TII.get(TargetOpcode::DBG_VALUE), false, Register(), - DVL->getOperand(0).getMetadata(), DVL->getOperand(1).getMetadata()); - DVL->eraseFromParent(); + // handle DBG_VALUE_LISTs correctly. So this makes them undefined, which will + // appear as "optimized out". + for (auto &MBB : MF) { + for (auto &MI : MBB) { + if (MI.getOpcode() == TargetOpcode::DBG_VALUE_LIST) { + MI.setDebugValueUndef(); + Changed = true; + } + } } - - return !DbgValueLists.empty(); + return Changed; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp index 5252db4858b9..4a6d37d7052e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp @@ -72,6 +72,152 @@ static float computeWeight(const MachineRegisterInfo *MRI, return Weight; } +// Create a map of "Register -> vector of <SlotIndex, DBG_VALUE>". +// The SlotIndex is the slot index of the next non-debug instruction or the end +// of a BB, because DBG_VALUE's don't have slot index themselves. +// Adapted from RegisterCoalescer::buildVRegToDbgValueMap. +static DenseMap<Register, std::vector<std::pair<SlotIndex, MachineInstr *>>> +buildVRegToDbgValueMap(MachineFunction &MF, const LiveIntervals *Liveness) { + DenseMap<Register, std::vector<std::pair<SlotIndex, MachineInstr *>>> + DbgVRegToValues; + const SlotIndexes *Slots = Liveness->getSlotIndexes(); + SmallVector<MachineInstr *, 8> ToInsert; + + // After collecting a block of DBG_VALUEs into ToInsert, enter them into the + // map. + auto CloseNewDVRange = [&DbgVRegToValues, &ToInsert](SlotIndex Slot) { + for (auto *X : ToInsert) { + for (const auto &Op : X->debug_operands()) { + if (Op.isReg() && Op.getReg().isVirtual()) + DbgVRegToValues[Op.getReg()].push_back({Slot, X}); + } + } + + ToInsert.clear(); + }; + + // Iterate over all instructions, collecting them into the ToInsert vector. + // Once a non-debug instruction is found, record the slot index of the + // collected DBG_VALUEs. + for (auto &MBB : MF) { + SlotIndex CurrentSlot = Slots->getMBBStartIdx(&MBB); + + for (auto &MI : MBB) { + if (MI.isDebugValue()) { + if (any_of(MI.debug_operands(), [](const MachineOperand &MO) { + return MO.isReg() && MO.getReg().isVirtual(); + })) + ToInsert.push_back(&MI); + } else if (!MI.isDebugOrPseudoInstr()) { + CurrentSlot = Slots->getInstructionIndex(MI); + CloseNewDVRange(CurrentSlot); + } + } + + // Close range of DBG_VALUEs at the end of blocks. + CloseNewDVRange(Slots->getMBBEndIdx(&MBB)); + } + + // Sort all DBG_VALUEs we've seen by slot number. + for (auto &Pair : DbgVRegToValues) + llvm::sort(Pair.second); + return DbgVRegToValues; +} + +// After register coalescing, some DBG_VALUEs will be invalid. Set them undef. +// This function has to run before the actual coalescing, i.e., the register +// changes. +static void undefInvalidDbgValues( + const LiveIntervals *Liveness, + const ArrayRef<SmallVector<LiveInterval *, 4>> &Assignments, + DenseMap<Register, std::vector<std::pair<SlotIndex, MachineInstr *>>> + &DbgVRegToValues) { +#ifndef NDEBUG + DenseSet<Register> SeenRegs; +#endif + for (size_t I = 0, E = Assignments.size(); I < E; ++I) { + const auto &CoalescedIntervals = Assignments[I]; + if (CoalescedIntervals.empty()) + continue; + for (LiveInterval *LI : CoalescedIntervals) { + Register Reg = LI->reg(); +#ifndef NDEBUG + // Ensure we don't process the same register twice + assert(SeenRegs.insert(Reg).second); +#endif + auto RegMapIt = DbgVRegToValues.find(Reg); + if (RegMapIt == DbgVRegToValues.end()) + continue; + SlotIndex LastSlot; + bool LastUndefResult = false; + for (auto [Slot, DbgValue] : RegMapIt->second) { + // All consecutive DBG_VALUEs have the same slot because the slot + // indices they have is the one for the first non-debug instruction + // after it, because DBG_VALUEs don't have slot index themselves. Before + // doing live range queries, quickly check if the current DBG_VALUE has + // the same slot index as the previous one, in which case we should do + // the same. Note that RegMapIt->second, the vector of {SlotIndex, + // DBG_VALUE}, is sorted by SlotIndex, which is necessary for this + // check. + if (Slot == LastSlot) { + if (LastUndefResult) { + LLVM_DEBUG(dbgs() << "Undefed: " << *DbgValue << "\n"); + DbgValue->setDebugValueUndef(); + } + continue; + } + LastSlot = Slot; + LastUndefResult = false; + for (LiveInterval *OtherLI : CoalescedIntervals) { + if (LI == OtherLI) + continue; + + // This DBG_VALUE has 'Reg' (the current LiveInterval's register) as + // its operand. If this DBG_VALUE's slot index is within other + // registers' live ranges, this DBG_VALUE should be undefed. For + // example, suppose %0 and %1 are to be coalesced into %0. + // ; %0's live range starts + // %0 = value_0 + // DBG_VALUE %0, !"a", ... (a) + // DBG_VALUE %1, !"b", ... (b) + // use %0 + // ; %0's live range ends + // ... + // ; %1's live range starts + // %1 = value_1 + // DBG_VALUE %0, !"c", ... (c) + // DBG_VALUE %1, !"d", ... (d) + // use %1 + // ; %1's live range ends + // + // In this code, (b) and (c) should be set to undef. After the two + // registers are coalesced, (b) will incorrectly say the variable + // "b"'s value is 'value_0', and (c) will also incorrectly say the + // variable "c"'s value is value_1. Note it doesn't actually matter + // which register they are coalesced into (%0 or %1); (b) and (c) + // should be set to undef as well if they are coalesced into %1. + // + // This happens DBG_VALUEs are not included when computing live + // ranges. + // + // Note that it is not possible for this DBG_VALUE to be + // simultaneously within 'Reg''s live range and one of other coalesced + // registers' live ranges because if their live ranges overlapped they + // would have not been selected as a coalescing candidate in the first + // place. + auto *SegmentIt = OtherLI->find(Slot); + if (SegmentIt != OtherLI->end() && SegmentIt->contains(Slot)) { + LLVM_DEBUG(dbgs() << "Undefed: " << *DbgValue << "\n"); + DbgValue->setDebugValueUndef(); + LastUndefResult = true; + break; + } + } + } + } + } +} + bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) { LLVM_DEBUG({ dbgs() << "********** Register Coloring **********\n" @@ -91,11 +237,17 @@ bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) { &getAnalysis<MachineBlockFrequencyInfo>(); WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); + // We don't preserve SSA form. + MRI->leaveSSA(); + // Gather all register intervals into a list and sort them. unsigned NumVRegs = MRI->getNumVirtRegs(); SmallVector<LiveInterval *, 0> SortedIntervals; SortedIntervals.reserve(NumVRegs); + // Record DBG_VALUEs and their SlotIndexes. + auto DbgVRegToValues = buildVRegToDbgValueMap(MF, Liveness); + LLVM_DEBUG(dbgs() << "Interesting register intervals:\n"); for (unsigned I = 0; I < NumVRegs; ++I) { Register VReg = Register::index2VirtReg(I); @@ -166,6 +318,9 @@ bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) { if (!Changed) return false; + // Set DBG_VALUEs that will be invalid after coalescing to undef. + undefInvalidDbgValues(Liveness, Assignments, DbgVRegToValues); + // Rewrite register operands. for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { Register Old = SortedIntervals[I]->reg(); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index 4b24f7fdb118..2e0df3c47841 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -278,12 +278,13 @@ static MachineInstr *getVRegDef(unsigned Reg, const MachineInstr *Insert, } // Test whether Reg, as defined at Def, has exactly one use. This is a -// generalization of MachineRegisterInfo::hasOneUse that uses LiveIntervals -// to handle complex cases. -static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI, - MachineDominatorTree &MDT, LiveIntervals &LIS) { +// generalization of MachineRegisterInfo::hasOneNonDBGUse that uses +// LiveIntervals to handle complex cases. +static bool hasOneNonDBGUse(unsigned Reg, MachineInstr *Def, + MachineRegisterInfo &MRI, MachineDominatorTree &MDT, + LiveIntervals &LIS) { // Most registers are in SSA form here so we try a quick MRI query first. - if (MRI.hasOneUse(Reg)) + if (MRI.hasOneNonDBGUse(Reg)) return true; bool HasOne = false; @@ -525,11 +526,10 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump()); WebAssemblyDebugValueManager DefDIs(Def); - MBB.splice(Insert, &MBB, Def); - DefDIs.move(Insert); + DefDIs.sink(Insert); LIS.handleMove(*Def); - if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) { + if (MRI.hasOneDef(Reg) && MRI.hasOneNonDBGUse(Reg)) { // No one else is using this register for anything so we can just stackify // it in place. MFI.stackifyVReg(MRI, Reg); @@ -537,8 +537,8 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, // The register may have unrelated uses or defs; create a new register for // just our one def and use so that we can stackify it. Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); - Def->getOperand(0).setReg(NewReg); Op.setReg(NewReg); + DefDIs.updateReg(NewReg); // Tell LiveIntervals about the new register. LIS.createAndComputeVirtRegInterval(NewReg); @@ -551,8 +551,6 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, MFI.stackifyVReg(MRI, NewReg); - DefDIs.updateReg(NewReg); - LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); } @@ -560,6 +558,13 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, return Def; } +static MachineInstr *getPrevNonDebugInst(MachineInstr *MI) { + for (auto *I = MI->getPrevNode(); I; I = I->getPrevNode()) + if (!I->isDebugInstr()) + return I; + return nullptr; +} + /// A trivially cloneable instruction; clone it and nest the new copy with the /// current instruction. static MachineInstr *rematerializeCheapDef( @@ -573,9 +578,10 @@ static MachineInstr *rematerializeCheapDef( WebAssemblyDebugValueManager DefDIs(&Def); Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); - TII->reMaterialize(MBB, Insert, NewReg, 0, Def, *TRI); + DefDIs.cloneSink(&*Insert, NewReg); Op.setReg(NewReg); - MachineInstr *Clone = &*std::prev(Insert); + MachineInstr *Clone = getPrevNonDebugInst(&*Insert); + assert(Clone); LIS.InsertMachineInstrInMaps(*Clone); LIS.createAndComputeVirtRegInterval(NewReg); MFI.stackifyVReg(MRI, NewReg); @@ -592,19 +598,13 @@ static MachineInstr *rematerializeCheapDef( } // If that was the last use of the original, delete the original. - // Move or clone corresponding DBG_VALUEs to the 'Insert' location. if (IsDead) { LLVM_DEBUG(dbgs() << " - Deleting original\n"); SlotIndex Idx = LIS.getInstructionIndex(Def).getRegSlot(); LIS.removePhysRegDefAt(MCRegister::from(WebAssembly::ARGUMENTS), Idx); LIS.removeInterval(Reg); LIS.RemoveMachineInstrFromMaps(Def); - Def.eraseFromParent(); - - DefDIs.move(&*Insert); - DefDIs.updateReg(NewReg); - } else { - DefDIs.clone(&*Insert, NewReg); + DefDIs.removeDef(); } return Clone; @@ -636,28 +636,26 @@ static MachineInstr *moveAndTeeForMultiUse( MachineRegisterInfo &MRI, const WebAssemblyInstrInfo *TII) { LLVM_DEBUG(dbgs() << "Move and tee for multi-use:"; Def->dump()); - WebAssemblyDebugValueManager DefDIs(Def); + const auto *RegClass = MRI.getRegClass(Reg); + Register TeeReg = MRI.createVirtualRegister(RegClass); + Register DefReg = MRI.createVirtualRegister(RegClass); // Move Def into place. - MBB.splice(Insert, &MBB, Def); + WebAssemblyDebugValueManager DefDIs(Def); + DefDIs.sink(Insert); LIS.handleMove(*Def); // Create the Tee and attach the registers. - const auto *RegClass = MRI.getRegClass(Reg); - Register TeeReg = MRI.createVirtualRegister(RegClass); - Register DefReg = MRI.createVirtualRegister(RegClass); MachineOperand &DefMO = Def->getOperand(0); MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(), TII->get(getTeeOpcode(RegClass)), TeeReg) .addReg(Reg, RegState::Define) .addReg(DefReg, getUndefRegState(DefMO.isDead())); Op.setReg(TeeReg); - DefMO.setReg(DefReg); + DefDIs.updateReg(DefReg); SlotIndex TeeIdx = LIS.InsertMachineInstrInMaps(*Tee).getRegSlot(); SlotIndex DefIdx = LIS.getInstructionIndex(*Def).getRegSlot(); - DefDIs.move(Insert); - // Tell LiveIntervals we moved the original vreg def from Def to Tee. LiveInterval &LI = LIS.getInterval(Reg); LiveInterval::iterator I = LI.FindSegmentContaining(DefIdx); @@ -674,8 +672,11 @@ static MachineInstr *moveAndTeeForMultiUse( imposeStackOrdering(Def); imposeStackOrdering(Tee); - DefDIs.clone(Tee, DefReg); - DefDIs.clone(Insert, TeeReg); + // Even though 'TeeReg, Reg = TEE ...', has two defs, we don't need to clone + // DBG_VALUEs for both of them, given that the latter will cancel the former + // anyway. Here we only clone DBG_VALUEs for TeeReg, which will be converted + // to a local index in ExplicitLocals pass. + DefDIs.cloneSink(Insert, TeeReg, /* CloneDef */ false); LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); LLVM_DEBUG(dbgs() << " - Tee instruction: "; Tee->dump()); @@ -876,7 +877,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) { bool SameBlock = DefI->getParent() == &MBB; bool CanMove = SameBlock && isSafeToMove(Def, &Use, Insert, MFI, MRI) && !TreeWalker.isOnStack(Reg); - if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) { + if (CanMove && hasOneNonDBGUse(Reg, DefI, MRI, MDT, LIS)) { Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); // If we are removing the frame base reg completely, remove the debug @@ -913,7 +914,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) { Register DefReg = SubsequentDef->getReg(); Register UseReg = SubsequentUse->getReg(); // TODO: This single-use restriction could be relaxed by using tees - if (DefReg != UseReg || !MRI.hasOneUse(DefReg)) + if (DefReg != UseReg || !MRI.hasOneNonDBGUse(DefReg)) break; MFI.stackifyVReg(MRI, DefReg); ++SubsequentDef; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp index 4fe339ce5293..2995b8816d1f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp @@ -62,6 +62,8 @@ enum RuntimeLibcallSignature { i32_func_i32_i32_iPTR, i64_func_i64_i64, i64_func_i64_i64_iPTR, + i64_i64_func_i32, + i64_i64_func_i64, i64_i64_func_f32, i64_i64_func_f64, i16_i16_func_i16_i16, @@ -71,20 +73,13 @@ enum RuntimeLibcallSignature { i64_i64_func_i64_i64_i64_i64_iPTR, i64_i64_i64_i64_func_i64_i64_i64_i64, i64_i64_func_i64_i64_i32, + i64_i64_func_i64_i64_i64_i64_i64_i64, iPTR_func_i32, iPTR_func_iPTR_i32_iPTR, iPTR_func_iPTR_iPTR_iPTR, f32_func_f32_f32_f32, f64_func_f64_f64_f64, func_i64_i64_iPTR_iPTR, - func_iPTR_f32, - func_iPTR_f64, - func_iPTR_i32, - func_iPTR_i64, - func_iPTR_i64_i64, - func_iPTR_i64_i64_i32, - func_iPTR_i64_i64_i64_i64, - func_iPTR_i64_i64_i64_i64_i64_i64, i32_func_i64_i64, i32_func_i64_i64_i64_i64, iPTR_func_f32, @@ -156,73 +151,76 @@ struct RuntimeLibcallSignatureTable { // All F80 and PPCF128 routines are unsupported. Table[RTLIB::ADD_F32] = f32_func_f32_f32; Table[RTLIB::ADD_F64] = f64_func_f64_f64; - Table[RTLIB::ADD_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::ADD_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::SUB_F32] = f32_func_f32_f32; Table[RTLIB::SUB_F64] = f64_func_f64_f64; - Table[RTLIB::SUB_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::SUB_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::MUL_F32] = f32_func_f32_f32; Table[RTLIB::MUL_F64] = f64_func_f64_f64; - Table[RTLIB::MUL_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::MUL_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::DIV_F32] = f32_func_f32_f32; Table[RTLIB::DIV_F64] = f64_func_f64_f64; - Table[RTLIB::DIV_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::DIV_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::REM_F32] = f32_func_f32_f32; Table[RTLIB::REM_F64] = f64_func_f64_f64; - Table[RTLIB::REM_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::REM_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::FMA_F32] = f32_func_f32_f32_f32; Table[RTLIB::FMA_F64] = f64_func_f64_f64_f64; - Table[RTLIB::FMA_F128] = func_iPTR_i64_i64_i64_i64_i64_i64; + Table[RTLIB::FMA_F128] = i64_i64_func_i64_i64_i64_i64_i64_i64; Table[RTLIB::POWI_F32] = f32_func_f32_i32; Table[RTLIB::POWI_F64] = f64_func_f64_i32; - Table[RTLIB::POWI_F128] = func_iPTR_i64_i64_i32; + Table[RTLIB::POWI_F128] = i64_i64_func_i64_i64_i32; Table[RTLIB::SQRT_F32] = f32_func_f32; Table[RTLIB::SQRT_F64] = f64_func_f64; - Table[RTLIB::SQRT_F128] = func_iPTR_i64_i64; + Table[RTLIB::SQRT_F128] = i64_i64_func_i64_i64; Table[RTLIB::CBRT_F32] = f32_func_f32; Table[RTLIB::CBRT_F64] = f64_func_f64; - Table[RTLIB::CBRT_F128] = func_iPTR_i64_i64; + Table[RTLIB::CBRT_F128] = i64_i64_func_i64_i64; Table[RTLIB::LOG_F32] = f32_func_f32; Table[RTLIB::LOG_F64] = f64_func_f64; - Table[RTLIB::LOG_F128] = func_iPTR_i64_i64; + Table[RTLIB::LOG_F128] = i64_i64_func_i64_i64; Table[RTLIB::LOG2_F32] = f32_func_f32; Table[RTLIB::LOG2_F64] = f64_func_f64; - Table[RTLIB::LOG2_F128] = func_iPTR_i64_i64; + Table[RTLIB::LOG2_F128] = i64_i64_func_i64_i64; Table[RTLIB::LOG10_F32] = f32_func_f32; Table[RTLIB::LOG10_F64] = f64_func_f64; - Table[RTLIB::LOG10_F128] = func_iPTR_i64_i64; + Table[RTLIB::LOG10_F128] = i64_i64_func_i64_i64; Table[RTLIB::EXP_F32] = f32_func_f32; Table[RTLIB::EXP_F64] = f64_func_f64; - Table[RTLIB::EXP_F128] = func_iPTR_i64_i64; + Table[RTLIB::EXP_F128] = i64_i64_func_i64_i64; Table[RTLIB::EXP2_F32] = f32_func_f32; Table[RTLIB::EXP2_F64] = f64_func_f64; - Table[RTLIB::EXP2_F128] = func_iPTR_i64_i64; + Table[RTLIB::EXP2_F128] = i64_i64_func_i64_i64; Table[RTLIB::SIN_F32] = f32_func_f32; Table[RTLIB::SIN_F64] = f64_func_f64; - Table[RTLIB::SIN_F128] = func_iPTR_i64_i64; + Table[RTLIB::SIN_F128] = i64_i64_func_i64_i64; Table[RTLIB::COS_F32] = f32_func_f32; Table[RTLIB::COS_F64] = f64_func_f64; - Table[RTLIB::COS_F128] = func_iPTR_i64_i64; + Table[RTLIB::COS_F128] = i64_i64_func_i64_i64; Table[RTLIB::SINCOS_F32] = func_f32_iPTR_iPTR; Table[RTLIB::SINCOS_F64] = func_f64_iPTR_iPTR; Table[RTLIB::SINCOS_F128] = func_i64_i64_iPTR_iPTR; Table[RTLIB::POW_F32] = f32_func_f32_f32; Table[RTLIB::POW_F64] = f64_func_f64_f64; - Table[RTLIB::POW_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::POW_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::CEIL_F32] = f32_func_f32; Table[RTLIB::CEIL_F64] = f64_func_f64; - Table[RTLIB::CEIL_F128] = func_iPTR_i64_i64; + Table[RTLIB::CEIL_F128] = i64_i64_func_i64_i64; Table[RTLIB::TRUNC_F32] = f32_func_f32; Table[RTLIB::TRUNC_F64] = f64_func_f64; - Table[RTLIB::TRUNC_F128] = func_iPTR_i64_i64; + Table[RTLIB::TRUNC_F128] = i64_i64_func_i64_i64; Table[RTLIB::RINT_F32] = f32_func_f32; Table[RTLIB::RINT_F64] = f64_func_f64; - Table[RTLIB::RINT_F128] = func_iPTR_i64_i64; + Table[RTLIB::RINT_F128] = i64_i64_func_i64_i64; Table[RTLIB::NEARBYINT_F32] = f32_func_f32; Table[RTLIB::NEARBYINT_F64] = f64_func_f64; - Table[RTLIB::NEARBYINT_F128] = func_iPTR_i64_i64; + Table[RTLIB::NEARBYINT_F128] = i64_i64_func_i64_i64; Table[RTLIB::ROUND_F32] = f32_func_f32; Table[RTLIB::ROUND_F64] = f64_func_f64; - Table[RTLIB::ROUND_F128] = func_iPTR_i64_i64; + Table[RTLIB::ROUND_F128] = i64_i64_func_i64_i64; + Table[RTLIB::ROUNDEVEN_F32] = f32_func_f32; + Table[RTLIB::ROUNDEVEN_F64] = f64_func_f64; + Table[RTLIB::ROUNDEVEN_F128] = i64_i64_func_i64_i64; Table[RTLIB::LROUND_F32] = iPTR_func_f32; Table[RTLIB::LROUND_F64] = iPTR_func_f64; Table[RTLIB::LROUND_F128] = iPTR_func_i64_i64; @@ -237,21 +235,27 @@ struct RuntimeLibcallSignatureTable { Table[RTLIB::LLRINT_F128] = i64_func_i64_i64; Table[RTLIB::FLOOR_F32] = f32_func_f32; Table[RTLIB::FLOOR_F64] = f64_func_f64; - Table[RTLIB::FLOOR_F128] = func_iPTR_i64_i64; + Table[RTLIB::FLOOR_F128] = i64_i64_func_i64_i64; Table[RTLIB::COPYSIGN_F32] = f32_func_f32_f32; Table[RTLIB::COPYSIGN_F64] = f64_func_f64_f64; - Table[RTLIB::COPYSIGN_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::COPYSIGN_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::FMIN_F32] = f32_func_f32_f32; Table[RTLIB::FMIN_F64] = f64_func_f64_f64; - Table[RTLIB::FMIN_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::FMIN_F128] = i64_i64_func_i64_i64_i64_i64; Table[RTLIB::FMAX_F32] = f32_func_f32_f32; Table[RTLIB::FMAX_F64] = f64_func_f64_f64; - Table[RTLIB::FMAX_F128] = func_iPTR_i64_i64_i64_i64; + Table[RTLIB::FMAX_F128] = i64_i64_func_i64_i64_i64_i64; + Table[RTLIB::LDEXP_F32] = f32_func_f32_i32; + Table[RTLIB::LDEXP_F64] = f64_func_f64_i32; + Table[RTLIB::LDEXP_F128] = i64_i64_func_i64_i64_i32; + Table[RTLIB::FREXP_F32] = f32_func_f32_i32; + Table[RTLIB::FREXP_F64] = f64_func_f64_i32; + Table[RTLIB::FREXP_F128] = i64_i64_func_i64_i64_i32; // Conversion // All F80 and PPCF128 routines are unsupported. - Table[RTLIB::FPEXT_F64_F128] = func_iPTR_f64; - Table[RTLIB::FPEXT_F32_F128] = func_iPTR_f32; + Table[RTLIB::FPEXT_F64_F128] = i64_i64_func_f64; + Table[RTLIB::FPEXT_F32_F128] = i64_i64_func_f32; Table[RTLIB::FPEXT_F32_F64] = f64_func_f32; Table[RTLIB::FPEXT_F16_F32] = f32_func_i16; Table[RTLIB::FPROUND_F32_F16] = i16_func_f32; @@ -280,22 +284,22 @@ struct RuntimeLibcallSignatureTable { Table[RTLIB::FPTOUINT_F128_I128] = i64_i64_func_i64_i64; Table[RTLIB::SINTTOFP_I32_F32] = f32_func_i32; Table[RTLIB::SINTTOFP_I32_F64] = f64_func_i32; - Table[RTLIB::SINTTOFP_I32_F128] = func_iPTR_i32; + Table[RTLIB::SINTTOFP_I32_F128] = i64_i64_func_i32; Table[RTLIB::SINTTOFP_I64_F32] = f32_func_i64; Table[RTLIB::SINTTOFP_I64_F64] = f64_func_i64; - Table[RTLIB::SINTTOFP_I64_F128] = func_iPTR_i64; + Table[RTLIB::SINTTOFP_I64_F128] = i64_i64_func_i64; Table[RTLIB::SINTTOFP_I128_F32] = f32_func_i64_i64; Table[RTLIB::SINTTOFP_I128_F64] = f64_func_i64_i64; - Table[RTLIB::SINTTOFP_I128_F128] = func_iPTR_i64_i64; + Table[RTLIB::SINTTOFP_I128_F128] = i64_i64_func_i64_i64; Table[RTLIB::UINTTOFP_I32_F32] = f32_func_i32; Table[RTLIB::UINTTOFP_I32_F64] = f64_func_i64; - Table[RTLIB::UINTTOFP_I32_F128] = func_iPTR_i32; + Table[RTLIB::UINTTOFP_I32_F128] = i64_i64_func_i32; Table[RTLIB::UINTTOFP_I64_F32] = f32_func_i64; Table[RTLIB::UINTTOFP_I64_F64] = f64_func_i64; - Table[RTLIB::UINTTOFP_I64_F128] = func_iPTR_i64; + Table[RTLIB::UINTTOFP_I64_F128] = i64_i64_func_i64; Table[RTLIB::UINTTOFP_I128_F32] = f32_func_i64_i64; Table[RTLIB::UINTTOFP_I128_F64] = f64_func_i64_i64; - Table[RTLIB::UINTTOFP_I128_F128] = func_iPTR_i64_i64; + Table[RTLIB::UINTTOFP_I128_F128] = i64_i64_func_i64_i64; // Comparison // ALl F80 and PPCF128 routines are unsupported. @@ -501,7 +505,7 @@ struct StaticLibcallNameMap { if (NameLibcall.first != nullptr && getRuntimeLibcallSignatures().Table[NameLibcall.second] != unsupported) { - assert(Map.find(NameLibcall.first) == Map.end() && + assert(!Map.contains(NameLibcall.first) && "duplicate libcall names in name map"); Map[NameLibcall.first] = NameLibcall.second; } @@ -687,72 +691,72 @@ void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget, Params.push_back(PtrTy); break; case i64_i64_func_f32: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::F32); break; case i64_i64_func_f64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::F64); break; case i16_i16_func_i16_i16: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I32); - Rets.push_back(wasm::ValType::I32); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I32); + Rets.push_back(wasm::ValType::I32); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I32); Params.push_back(wasm::ValType::I32); break; case i32_i32_func_i32_i32: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I32); - Rets.push_back(wasm::ValType::I32); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I32); + Rets.push_back(wasm::ValType::I32); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I32); Params.push_back(wasm::ValType::I32); break; case i64_i64_func_i64_i64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); break; case i64_i64_func_i64_i64_i64_i64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); break; case i64_i64_func_i64_i64_i64_i64_iPTR: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); @@ -760,28 +764,26 @@ void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget, Params.push_back(PtrTy); break; case i64_i64_i64_i64_func_i64_i64_i64_i64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); break; case i64_i64_func_i64_i64_i32: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I32); @@ -820,49 +822,6 @@ void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget, Params.push_back(PtrTy); Params.push_back(PtrTy); break; - case func_iPTR_f32: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::F32); - break; - case func_iPTR_f64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::F64); - break; - case func_iPTR_i32: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I32); - break; - case func_iPTR_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - break; - case func_iPTR_i64_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case func_iPTR_i64_i64_i32: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I32); - break; - case func_iPTR_i64_i64_i64_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case func_iPTR_i64_i64_i64_i64_i64_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; case i32_func_i64_i64: Rets.push_back(wasm::ValType::I32); Params.push_back(wasm::ValType::I64); @@ -888,12 +847,44 @@ void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget, Params.push_back(wasm::ValType::I64); Params.push_back(wasm::ValType::I64); break; + case i64_i64_func_i64_i64_i64_i64_i64_i64: + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } + Params.push_back(wasm::ValType::I64); + Params.push_back(wasm::ValType::I64); + Params.push_back(wasm::ValType::I64); + Params.push_back(wasm::ValType::I64); + Params.push_back(wasm::ValType::I64); + Params.push_back(wasm::ValType::I64); + break; + case i64_i64_func_i32: + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } + Params.push_back(wasm::ValType::I32); + break; + case i64_i64_func_i64: + if (Subtarget.hasMultivalue()) { + Rets.push_back(wasm::ValType::I64); + Rets.push_back(wasm::ValType::I64); + } else { + Params.push_back(PtrTy); + } + Params.push_back(wasm::ValType::I64); + break; case unsupported: llvm_unreachable("unsupported runtime library signature"); } } -// TODO: If the RTLIB::Libcall-taking flavor of GetSignature remains unsed +// TODO: If the RTLIB::Libcall-taking flavor of GetSignature remains unused // other than here, just roll its logic into this version. void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget, StringRef Name, @@ -904,9 +895,9 @@ void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget, auto Val = Map.find(Name); #ifndef NDEBUG if (Val == Map.end()) { - auto message = std::string("unexpected runtime library name: ") + - std::string(Name); - llvm_unreachable(message.c_str()); + auto Message = + std::string("unexpected runtime library name: ") + std::string(Name); + llvm_unreachable(Message.c_str()); } #endif return getLibcallSignature(Subtarget, Val->second, Rets, Params); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 630c786a3dc7..6ef219f216a3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -16,6 +16,7 @@ #include "TargetInfo/WebAssemblyTargetInfo.h" #include "Utils/WebAssemblyUtilities.h" #include "WebAssembly.h" +#include "WebAssemblyISelLowering.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyTargetObjectFile.h" #include "WebAssemblyTargetTransformInfo.h" @@ -98,13 +99,6 @@ static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM, return Reloc::Static; } - if (!TT.isOSEmscripten()) { - // Relocation modes other than static are currently implemented in a way - // that only works for Emscripten, so disable them if we aren't targeting - // Emscripten. - return Reloc::Static; - } - return *RM; } @@ -464,6 +458,15 @@ void WebAssemblyPassConfig::addIRPasses() { } void WebAssemblyPassConfig::addISelPrepare() { + WebAssemblyTargetMachine *WasmTM = + static_cast<WebAssemblyTargetMachine *>(TM); + const WebAssemblySubtarget *Subtarget = + WasmTM->getSubtargetImpl(std::string(WasmTM->getTargetCPU()), + std::string(WasmTM->getTargetFeatureString())); + if (Subtarget->hasReferenceTypes()) { + // We need to remove allocas for reference types + addPass(createPromoteMemoryToRegisterPass(true)); + } // Lower atomics and TLS if necessary addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine())); |
