diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
| commit | 145449b1e420787bb99721a429341fa6be3adfb6 (patch) | |
| tree | 1d56ae694a6de602e348dd80165cf881a36600ed /llvm/lib/Target/WebAssembly | |
| parent | ecbca9f5fb7d7613d2b94982c4825eb0d33d6842 (diff) | |
Diffstat (limited to 'llvm/lib/Target/WebAssembly')
42 files changed, 525 insertions, 651 deletions
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp index 56689d3ee06b..7bafa53af2af 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -24,6 +24,7 @@ #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCSectionWasm.h" @@ -374,7 +375,7 @@ public: auto Type = WebAssembly::parseType(Lexer.getTok().getString()); if (!Type) return error("unknown type: ", Lexer.getTok()); - Types.push_back(Type.getValue()); + Types.push_back(*Type); Parser.Lex(); if (!isNext(AsmToken::Comma)) break; @@ -670,11 +671,12 @@ public: } else { // Assume this identifier is a label. const MCExpr *Val; + SMLoc Start = Id.getLoc(); SMLoc End; if (Parser.parseExpression(Val, End)) return error("Cannot parse symbol: ", Lexer.getTok()); Operands.push_back(std::make_unique<WebAssemblyOperand>( - WebAssemblyOperand::Symbol, Id.getLoc(), Id.getEndLoc(), + WebAssemblyOperand::Symbol, Start, End, WebAssemblyOperand::SymOp{Val})); if (checkForP2AlignIfLoadStore(Operands, Name)) return true; @@ -815,8 +817,7 @@ public: // Now set this symbol with the correct type. auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); - WasmSym->setGlobalType( - wasm::WasmGlobalType{uint8_t(Type.getValue()), Mutable}); + WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(*Type), Mutable}); // And emit the directive again. TOut.emitGlobalType(WasmSym); return expect(AsmToken::EndOfStatement, "EOL"); @@ -846,7 +847,7 @@ public: // symbol auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName)); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); - wasm::WasmTableType Type = {uint8_t(ElemType.getValue()), Limits}; + wasm::WasmTableType Type = {uint8_t(*ElemType), Limits}; WasmSym->setTableType(Type); TOut.emitTableType(WasmSym); return expect(AsmToken::EndOfStatement, "EOL"); @@ -1016,7 +1017,7 @@ public: Inst.setOpcode(Opc64); } } - if (!SkipTypeCheck && TC.typeCheck(IDLoc, Inst)) + if (!SkipTypeCheck && TC.typeCheck(IDLoc, Inst, Operands)) return true; Out.emitInstruction(Inst, getSTI()); if (CurrentState == EndFunction) { @@ -1094,14 +1095,15 @@ public: auto *WS = getContext().getWasmSection(SecName, SectionKind::getText(), 0, Group, MCContext::GenericSectionID, nullptr); - getStreamer().SwitchSection(WS); + getStreamer().switchSection(WS); // Also generate DWARF for this section if requested. if (getContext().getGenDwarfForAssembly()) getContext().addGenDwarfSection(WS); } void onEndOfFunction(SMLoc ErrorLoc) { - TC.endOfFunction(ErrorLoc); + if (!SkipTypeCheck) + TC.endOfFunction(ErrorLoc); // Reset the type checker state. TC.Clear(); diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp index 128ce5c4fec0..ec72c1de0503 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp @@ -86,14 +86,12 @@ bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, Optional<wasm::ValType> EVT) { if (Stack.empty()) { return typeError(ErrorLoc, - EVT.hasValue() - ? StringRef("empty stack while popping ") + - WebAssembly::typeToString(EVT.getValue()) - : StringRef( - "empty stack while popping value")); + EVT ? StringRef("empty stack while popping ") + + WebAssembly::typeToString(EVT.getValue()) + : StringRef("empty stack while popping value")); } auto PVT = Stack.pop_back_val(); - if (EVT.hasValue() && EVT.getValue() != PVT) { + if (EVT && EVT.getValue() != PVT) { return typeError( ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) + ", expected " + @@ -102,6 +100,19 @@ bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, return false; } +bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) { + if (Stack.empty()) { + return typeError(ErrorLoc, StringRef("empty stack while popping reftype")); + } + auto PVT = Stack.pop_back_val(); + if (!WebAssembly::isRefType(PVT)) { + return typeError(ErrorLoc, StringRef("popped ") + + WebAssembly::typeToString(PVT) + + ", expected reftype"); + } + return false; +} + bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type) { auto Local = static_cast<size_t>(Inst.getOperand(0).getImm()); @@ -160,7 +171,7 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst, if (getSymRef(ErrorLoc, Inst, SymRef)) return true; auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); - switch (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA)) { + switch (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA)) { case wasm::WASM_SYMBOL_TYPE_GLOBAL: Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type); break; @@ -182,6 +193,20 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst, return false; } +bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCInst &Inst, + wasm::ValType &Type) { + const MCSymbolRefExpr *SymRef; + if (getSymRef(ErrorLoc, Inst, SymRef)) + return true; + auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); + if (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA) != + wasm::WASM_SYMBOL_TYPE_TABLE) + return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + + " missing .tabletype"); + Type = static_cast<wasm::ValType>(WasmSym->getTableType().ElemType); + return false; +} + bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) { // Check the return types. for (auto RVT : llvm::reverse(ReturnTypes)) { @@ -196,35 +221,58 @@ bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) { return false; } -bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) { +bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst, + OperandVector &Operands) { auto Opc = Inst.getOpcode(); auto Name = GetMnemonic(Opc); dumpTypeStack("typechecking " + Name + ": "); wasm::ValType Type; if (Name == "local.get") { - if (getLocal(ErrorLoc, Inst, Type)) + if (getLocal(Operands[1]->getStartLoc(), Inst, Type)) return true; Stack.push_back(Type); } else if (Name == "local.set") { - if (getLocal(ErrorLoc, Inst, Type)) + if (getLocal(Operands[1]->getStartLoc(), Inst, Type)) return true; if (popType(ErrorLoc, Type)) return true; } else if (Name == "local.tee") { - if (getLocal(ErrorLoc, Inst, Type)) + if (getLocal(Operands[1]->getStartLoc(), Inst, Type)) return true; if (popType(ErrorLoc, Type)) return true; Stack.push_back(Type); } else if (Name == "global.get") { - if (getGlobal(ErrorLoc, Inst, Type)) + if (getGlobal(Operands[1]->getStartLoc(), Inst, Type)) return true; Stack.push_back(Type); } else if (Name == "global.set") { - if (getGlobal(ErrorLoc, Inst, Type)) + if (getGlobal(Operands[1]->getStartLoc(), Inst, Type)) + return true; + if (popType(ErrorLoc, Type)) + return true; + } else if (Name == "table.get") { + if (getTable(Operands[1]->getStartLoc(), Inst, Type)) + return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + Stack.push_back(Type); + } else if (Name == "table.set") { + if (getTable(Operands[1]->getStartLoc(), Inst, Type)) return true; if (popType(ErrorLoc, Type)) return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + } else if (Name == "table.fill") { + if (getTable(Operands[1]->getStartLoc(), Inst, Type)) + return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; + if (popType(ErrorLoc, Type)) + return true; + if (popType(ErrorLoc, wasm::ValType::I32)) + return true; } else if (Name == "drop") { if (popType(ErrorLoc, {})) return true; @@ -245,33 +293,36 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) { return true; } else if (Name == "call" || Name == "return_call") { const MCSymbolRefExpr *SymRef; - if (getSymRef(ErrorLoc, Inst, SymRef)) + if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef)) return true; auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); auto Sig = WasmSym->getSignature(); if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION) - return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + - " missing .functype"); + return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") + + WasmSym->getName() + + " missing .functype"); if (checkSig(ErrorLoc, *Sig)) return true; if (Name == "return_call" && endOfFunction(ErrorLoc)) return true; } else if (Name == "catch") { const MCSymbolRefExpr *SymRef; - if (getSymRef(ErrorLoc, Inst, 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(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + - " missing .tagtype"); + 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 == "ref.null") { - auto VT = static_cast<wasm::ValType>(Inst.getOperand(0).getImm()); - Stack.push_back(VT); } else if (Name == "unreachable") { Unreachable = true; + } else if (Name == "ref.is_null") { + if (popRefType(ErrorLoc)) + return true; + Stack.push_back(wasm::ValType::I32); } else { // The current instruction is a stack instruction which doesn't have // explicit operands that indicate push/pop types, so we get those from diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h index 2b07faf67a18..3be966b5739c 100644 --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h @@ -16,9 +16,10 @@ #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H #define LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H -#include "llvm/MC/MCParser/MCAsmParser.h" -#include "llvm/MC/MCInstrInfo.h" #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCSymbol.h" namespace llvm { @@ -38,12 +39,14 @@ class WebAssemblyAsmTypeCheck final { void dumpTypeStack(Twine Msg); bool typeError(SMLoc ErrorLoc, const Twine &Msg); bool popType(SMLoc ErrorLoc, Optional<wasm::ValType> EVT); + bool popRefType(SMLoc ErrorLoc); bool getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type); bool checkEnd(SMLoc ErrorLoc, bool PopVals = false); bool checkSig(SMLoc ErrorLoc, const wasm::WasmSignature &Sig); bool getSymRef(SMLoc ErrorLoc, const MCInst &Inst, const MCSymbolRefExpr *&SymRef); bool getGlobal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type); + bool getTable(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type); public: WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, bool is64); @@ -52,7 +55,7 @@ public: void localDecl(const SmallVector<wasm::ValType, 4> &Locals); void setLastSig(const wasm::WasmSignature &Sig) { LastSig = Sig; } bool endOfFunction(SMLoc ErrorLoc); - bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst); + bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst, OperandVector &Operands); void Clear() { Stack.clear(); diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp index 5d38145559da..ae65a9dc2a4e 100644 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -17,8 +17,8 @@ #include "TargetInfo/WebAssemblyTargetInfo.h" #include "Utils/WebAssemblyTypeUtilities.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDecoderOps.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" -#include "llvm/MC/MCFixedLenDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp index d8122950e061..5727708a84ad 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp @@ -52,6 +52,4 @@ WebAssemblyMCAsmInfo::WebAssemblyMCAsmInfo(const Triple &T, // we make sure this info is set correctly. if (WebAssembly::WasmEnableEH || WebAssembly::WasmEnableSjLj) ExceptionsType = ExceptionHandling::Wasm; - - // TODO: UseIntegratedAssembler? } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp index 8f670ec88897..f52545a65dbb 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp @@ -62,7 +62,6 @@ static MCInstPrinter *createMCInstPrinter(const Triple & /*T*/, } static MCCodeEmitter *createCodeEmitter(const MCInstrInfo &MCII, - const MCRegisterInfo & /*MRI*/, MCContext &Ctx) { return createWebAssemblyMCCodeEmitter(MCII); } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp index 397b9b0ee9da..2da219d54c73 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -58,8 +58,6 @@ void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<wasm::ValType> Types) { } } -void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; } - void WebAssemblyTargetAsmStreamer::emitFunctionType(const MCSymbolWasm *Sym) { assert(Sym->isFunction()); OS << "\t.functype\t" << Sym->getName() << " "; @@ -136,10 +134,6 @@ void WebAssemblyTargetWasmStreamer::emitLocal(ArrayRef<wasm::ValType> Types) { } } -void WebAssemblyTargetWasmStreamer::emitEndFunc() { - llvm_unreachable(".end_func is not needed for direct wasm output"); -} - void WebAssemblyTargetWasmStreamer::emitIndIdx(const MCExpr *Value) { llvm_unreachable(".indidx encoding not yet implemented"); } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h index c0ad63c8dd50..522f6356c28b 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -32,8 +32,6 @@ public: /// .local virtual void emitLocal(ArrayRef<wasm::ValType> Types) = 0; - /// .endfunc - virtual void emitEndFunc() = 0; /// .functype virtual void emitFunctionType(const MCSymbolWasm *Sym) = 0; /// .indidx @@ -66,7 +64,6 @@ public: WebAssemblyTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); void emitLocal(ArrayRef<wasm::ValType> Types) override; - void emitEndFunc() override; void emitFunctionType(const MCSymbolWasm *Sym) override; void emitIndIdx(const MCExpr *Value) override; void emitGlobalType(const MCSymbolWasm *Sym) override; @@ -83,7 +80,6 @@ public: explicit WebAssemblyTargetWasmStreamer(MCStreamer &S); void emitLocal(ArrayRef<wasm::ValType> Types) override; - void emitEndFunc() override; void emitFunctionType(const MCSymbolWasm *Sym) override {} void emitIndIdx(const MCExpr *Value) override; void emitGlobalType(const MCSymbolWasm *Sym) override {} @@ -104,7 +100,6 @@ public: : WebAssemblyTargetStreamer(S) {} void emitLocal(ArrayRef<wasm::ValType>) override {} - void emitEndFunc() override {} void emitFunctionType(const MCSymbolWasm *) override {} void emitIndIdx(const MCExpr *) override {} void emitGlobalType(const MCSymbolWasm *) override {} diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h index cdb95d48398d..8fc67d37925c 100644 --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h @@ -80,6 +80,10 @@ inline bool isRefType(const Type *Ty) { return isFuncrefType(Ty) || isExternrefType(Ty); } +inline bool isRefType(wasm::ValType Type) { + return Type == wasm::ValType::EXTERNREF || Type == wasm::ValType::FUNCREF; +} + // Convert StringRef to ValType / HealType / BlockType Optional<wasm::ValType> parseType(StringRef Type); diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index 803786e0c9c2..aee8f160f38d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -26,7 +26,6 @@ class FunctionPass; // LLVM IR passes. ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(); -ModulePass *createWebAssemblyLowerGlobalDtors(); ModulePass *createWebAssemblyAddMissingPrototypes(); ModulePass *createWebAssemblyFixFunctionBitcasts(); FunctionPass *createWebAssemblyOptimizeReturned(); @@ -41,7 +40,6 @@ FunctionPass *createWebAssemblySetP2AlignOperands(); // Late passes. FunctionPass *createWebAssemblyReplacePhysRegs(); FunctionPass *createWebAssemblyNullifyDebugValueLists(); -FunctionPass *createWebAssemblyPrepareForLiveIntervals(); FunctionPass *createWebAssemblyOptimizeLiveIntervals(); FunctionPass *createWebAssemblyMemIntrinsicResults(); FunctionPass *createWebAssemblyRegStackify(); @@ -61,14 +59,12 @@ ModulePass *createWebAssemblyMCLowerPrePass(); // PassRegistry initialization declarations. void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &); void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &); -void initializeLowerGlobalDtorsPass(PassRegistry &); void initializeFixFunctionBitcastsPass(PassRegistry &); void initializeOptimizeReturnedPass(PassRegistry &); void initializeWebAssemblyArgumentMovePass(PassRegistry &); void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &); void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &); void initializeWebAssemblyNullifyDebugValueListsPass(PassRegistry &); -void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &); void initializeWebAssemblyRegStackifyPass(PassRegistry &); diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td b/llvm/lib/Target/WebAssembly/WebAssembly.td index a529c6217189..b83dcf3a8e65 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.td +++ b/llvm/lib/Target/WebAssembly/WebAssembly.td @@ -67,6 +67,10 @@ def FeatureReferenceTypes : SubtargetFeature<"reference-types", "HasReferenceTypes", "true", "Enable reference types">; +def FeatureExtendedConst : + SubtargetFeature<"extended-const", "HasExtendedConst", "true", + "Enable extended const expressions">; + //===----------------------------------------------------------------------===// // Architectures. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index bf326e5106be..57d51634e849 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -180,30 +180,30 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { MCSymbolWasm *Sym = cast<MCSymbolWasm>(getSymbol(GV)); if (!Sym->getType()) { - const WebAssemblyTargetLowering &TLI = *Subtarget->getTargetLowering(); SmallVector<MVT, 1> VTs; Type *GlobalVT = GV->getValueType(); - computeLegalValueVTs(TLI, GV->getParent()->getContext(), - GV->getParent()->getDataLayout(), GlobalVT, VTs); + if (Subtarget) { + // Subtarget is only set when a function is defined, because + // each function can declare a different subtarget. For example, + // on ARM a compilation unit might have a function on ARM and + // another on Thumb. Therefore only if Subtarget is non-null we + // can actually calculate the legal VTs. + const WebAssemblyTargetLowering &TLI = *Subtarget->getTargetLowering(); + computeLegalValueVTs(TLI, GV->getParent()->getContext(), + GV->getParent()->getDataLayout(), GlobalVT, VTs); + } WebAssembly::wasmSymbolSetType(Sym, GlobalVT, VTs); } - // If the GlobalVariable refers to a table, we handle it here instead of - // in emitExternalDecls - if (Sym->isTable()) { - getTargetStreamer()->emitTableType(Sym); - return; - } - emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); + emitSymbolType(Sym); if (GV->hasInitializer()) { assert(getSymbolPreferLocal(*GV) == Sym); emitLinkage(GV, Sym); - getTargetStreamer()->emitGlobalType(Sym); OutStreamer->emitLabel(Sym); // TODO: Actually emit the initializer value. Otherwise the global has the // default value for its type (0, ref.null, etc). - OutStreamer->AddBlankLine(); + OutStreamer->addBlankLine(); } } @@ -211,7 +211,7 @@ MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) { auto *WasmSym = cast<MCSymbolWasm>(GetExternalSymbolSymbol(Name)); // May be called multiple times, so early out. - if (WasmSym->getType().hasValue()) + if (WasmSym->getType()) return WasmSym; const WebAssemblySubtarget &Subtarget = getSubtarget(); @@ -271,31 +271,52 @@ MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) { return WasmSym; } -void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) { +void WebAssemblyAsmPrinter::emitSymbolType(const MCSymbolWasm *Sym) { + Optional<wasm::WasmSymbolType> WasmTy = Sym->getType(); + if (!WasmTy) + return; + + switch (*WasmTy) { + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + getTargetStreamer()->emitGlobalType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_TAG: + getTargetStreamer()->emitTagType(Sym); + break; + case wasm::WASM_SYMBOL_TYPE_TABLE: + getTargetStreamer()->emitTableType(Sym); + break; + default: + break; // We only handle globals, tags and tables here + } +} + +void WebAssemblyAsmPrinter::emitDecls(const Module &M) { if (signaturesEmitted) return; signaturesEmitted = true; // Normally symbols for globals get discovered as the MI gets lowered, - // but we need to know about them ahead of time. + // but we need to know about them ahead of time. This will however, + // 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) { - getOrCreateWasmSymbol(Name.getKey()); + auto *WasmSym = cast<MCSymbolWasm>(getOrCreateWasmSymbol(Name.getKey())); + if (WasmSym->isFunction()) { + // TODO(wvo): is there any case where this overlaps with the call to + // emitFunctionType in the loop below? + getTargetStreamer()->emitFunctionType(WasmSym); + } } for (auto &It : OutContext.getSymbols()) { - // Emit .globaltype, .tagtype, or .tabletype declarations. + // Emit .globaltype, .tagtype, or .tabletype declarations for extern + // declarations, i.e. those that have only been declared (but not defined) + // in the current module auto Sym = cast<MCSymbolWasm>(It.getValue()); - if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL) { - // .globaltype already handled by emitGlobalVariable for defined - // variables; here we make sure the types of external wasm globals get - // written to the file. - if (Sym->isUndefined()) - getTargetStreamer()->emitGlobalType(Sym); - } else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TAG) - getTargetStreamer()->emitTagType(Sym); - else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TABLE) - getTargetStreamer()->emitTableType(Sym); + if (!Sym->isDefined()) + emitSymbolType(Sym); } DenseSet<MCSymbol *> InvokeSymbols; @@ -303,55 +324,56 @@ void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) { if (F.isIntrinsic()) continue; - // Emit function type info for all undefined functions - if (F.isDeclarationForLinker()) { - SmallVector<MVT, 4> Results; - SmallVector<MVT, 4> Params; - computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); - // At this point these MCSymbols may or may not have been created already - // and thus also contain a signature, but we need to get the signature - // anyway here in case it is an invoke that has not yet been created. We - // will discard it later if it turns out not to be necessary. - auto Signature = signatureFromMVTs(Results, Params); - bool InvokeDetected = false; - auto *Sym = getMCSymbolForFunction( - &F, WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj, - Signature.get(), InvokeDetected); + // Emit function type info for all functions. This will emit duplicate + // information for defined functions (which already have function type + // info emitted alongside their definition), but this is necessary in + // order to enable the single-pass WebAssemblyAsmTypeCheck to succeed. + SmallVector<MVT, 4> Results; + SmallVector<MVT, 4> Params; + computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); + // At this point these MCSymbols may or may not have been created already + // and thus also contain a signature, but we need to get the signature + // anyway here in case it is an invoke that has not yet been created. We + // will discard it later if it turns out not to be necessary. + auto Signature = signatureFromMVTs(Results, Params); + bool InvokeDetected = false; + auto *Sym = getMCSymbolForFunction( + &F, WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj, + Signature.get(), InvokeDetected); - // Multiple functions can be mapped to the same invoke symbol. For - // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' - // are both mapped to '__invoke_vi'. We keep them in a set once we emit an - // Emscripten EH symbol so we don't emit the same symbol twice. - if (InvokeDetected && !InvokeSymbols.insert(Sym).second) - continue; + // Multiple functions can be mapped to the same invoke symbol. For + // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' + // are both mapped to '__invoke_vi'. We keep them in a set once we emit an + // Emscripten EH symbol so we don't emit the same symbol twice. + if (InvokeDetected && !InvokeSymbols.insert(Sym).second) + continue; - Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); - if (!Sym->getSignature()) { - Sym->setSignature(Signature.get()); - addSignature(std::move(Signature)); - } else { - // This symbol has already been created and had a signature. Discard it. - Signature.reset(); - } + Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); + if (!Sym->getSignature()) { + Sym->setSignature(Signature.get()); + addSignature(std::move(Signature)); + } else { + // This symbol has already been created and had a signature. Discard it. + Signature.reset(); + } - getTargetStreamer()->emitFunctionType(Sym); + getTargetStreamer()->emitFunctionType(Sym); - if (F.hasFnAttribute("wasm-import-module")) { - StringRef Name = - F.getFnAttribute("wasm-import-module").getValueAsString(); - Sym->setImportModule(storeName(Name)); - getTargetStreamer()->emitImportModule(Sym, Name); - } - if (F.hasFnAttribute("wasm-import-name")) { - // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use - // the original function name but the converted symbol name. - StringRef Name = - InvokeDetected - ? Sym->getName() - : F.getFnAttribute("wasm-import-name").getValueAsString(); - Sym->setImportName(storeName(Name)); - getTargetStreamer()->emitImportName(Sym, Name); - } + if (F.hasFnAttribute("wasm-import-module")) { + StringRef Name = + F.getFnAttribute("wasm-import-module").getValueAsString(); + Sym->setImportModule(storeName(Name)); + getTargetStreamer()->emitImportModule(Sym, Name); + } + if (F.hasFnAttribute("wasm-import-name")) { + // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use + // the original function name but the converted symbol name. + StringRef Name = + InvokeDetected + ? Sym->getName() + : F.getFnAttribute("wasm-import-name").getValueAsString(); + Sym->setImportName(storeName(Name)); + getTargetStreamer()->emitImportName(Sym, Name); } if (F.hasFnAttribute("wasm-export-name")) { @@ -362,9 +384,12 @@ void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) { } } } - + void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { - emitExternalDecls(M); + // This is required to emit external declarations (like .functypes) when + // no functions are defined in the compilation unit and therefore, + // emitDecls() is not called until now. + emitDecls(M); // When a function's address is taken, a TABLE_INDEX relocation is emitted // against the function symbol at the use site. However the relocation @@ -401,13 +426,13 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { if (!Name || !Contents) continue; - OutStreamer->PushSection(); + OutStreamer->pushSection(); std::string SectionName = (".custom_section." + Name->getString()).str(); MCSectionWasm *MySection = OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); - OutStreamer->SwitchSection(MySection); + OutStreamer->switchSection(MySection); OutStreamer->emitBytes(Contents->getString()); - OutStreamer->PopSection(); + OutStreamer->popSection(); } } @@ -445,8 +470,8 @@ void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { if (FieldCount != 0) { MCSectionWasm *Producers = OutContext.getWasmSection( ".custom_section.producers", SectionKind::getMetadata()); - OutStreamer->PushSection(); - OutStreamer->SwitchSection(Producers); + OutStreamer->pushSection(); + OutStreamer->switchSection(Producers); OutStreamer->emitULEB128IntValue(FieldCount); for (auto &Producers : {std::make_pair("language", &Languages), std::make_pair("processed-by", &Tools)}) { @@ -462,7 +487,7 @@ void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { OutStreamer->emitBytes(Producer.second); } } - OutStreamer->PopSection(); + OutStreamer->popSection(); } } @@ -518,8 +543,8 @@ void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { // Emit features and linkage policies into the "target_features" section MCSectionWasm *FeaturesSection = OutContext.getWasmSection( ".custom_section.target_features", SectionKind::getMetadata()); - OutStreamer->PushSection(); - OutStreamer->SwitchSection(FeaturesSection); + OutStreamer->pushSection(); + OutStreamer->switchSection(FeaturesSection); OutStreamer->emitULEB128IntValue(EmittedFeatures.size()); for (auto &F : EmittedFeatures) { @@ -528,10 +553,11 @@ void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { OutStreamer->emitBytes(F.Name); } - OutStreamer->PopSection(); + OutStreamer->popSection(); } void WebAssemblyAsmPrinter::emitConstantPool() { + emitDecls(*MMI->getModule()); assert(MF->getConstantPool()->getConstants().empty() && "WebAssembly disables constant pools"); } @@ -540,17 +566,6 @@ void WebAssemblyAsmPrinter::emitJumpTableInfo() { // Nothing to do; jump tables are incorporated into the instruction stream. } -void WebAssemblyAsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *Sym) - const { - AsmPrinter::emitLinkage(GV, Sym); - // This gets called before the function label and type are emitted. - // We use it to emit signatures of external functions. - // FIXME casts! - const_cast<WebAssemblyAsmPrinter *>(this) - ->emitExternalDecls(*MMI->getModule()); -} - - void WebAssemblyAsmPrinter::emitFunctionBodyStart() { const Function &F = MF->getFunction(); SmallVector<MVT, 1> ResultVTs; @@ -612,7 +627,7 @@ void WebAssemblyAsmPrinter::emitInstruction(const MachineInstr *MI) { // function body. if (isVerbose()) { OutStreamer->AddComment("fallthrough-return"); - OutStreamer->AddBlankLine(); + OutStreamer->addBlankLine(); } break; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h index 6b2f2000a0bd..65d6ee415180 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -66,10 +66,10 @@ public: void emitEndOfAsmFile(Module &M) override; void EmitProducerInfo(Module &M); void EmitTargetFeatures(Module &M); + void emitSymbolType(const MCSymbolWasm *Sym); void emitGlobalVariable(const GlobalVariable *GV) override; void emitJumpTableInfo() override; void emitConstantPool() override; - void emitLinkage(const GlobalValue *, MCSymbol *) const override; void emitFunctionBodyStart() override; void emitInstruction(const MachineInstr *MI) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, @@ -84,7 +84,7 @@ public: wasm::WasmSignature *Sig, bool &InvokeDetected); MCSymbol *getOrCreateWasmSymbol(StringRef Name); - void emitExternalDecls(const Module &M); + void emitDecls(const Module &M); }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 17e867e4c7d8..02e873a0f9a6 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -1716,7 +1716,7 @@ void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) { // Rewrite MBB operands to be depth immediates. SmallVector<MachineOperand, 4> Ops(MI.operands()); while (MI.getNumOperands() > 0) - MI.RemoveOperand(MI.getNumOperands() - 1); + MI.removeOperand(MI.getNumOperands() - 1); for (auto MO : Ops) { if (MO.isMBB()) { if (MI.getOpcode() == WebAssembly::DELEGATE) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp index b94981245f8b..81fe5395a6de 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp @@ -14,6 +14,7 @@ #include "WebAssemblyExceptionInfo.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "Utils/WebAssemblyUtilities.h" +#include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/CodeGen/MachineDominanceFrontier.h" #include "llvm/CodeGen/MachineDominators.h" diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFixBrTableDefaults.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFixBrTableDefaults.cpp index 5bdec89f1125..fa5b4a508fa5 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFixBrTableDefaults.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFixBrTableDefaults.cpp @@ -130,7 +130,7 @@ MachineBasicBlock *fixBrTableDefault(MachineInstr &MI, MachineBasicBlock *MBB, return nullptr; // Remove the dummy default target and install the real one. - MI.RemoveOperand(MI.getNumExplicitOperands() - 1); + MI.removeOperand(MI.getNumExplicitOperands() - 1); MI.addOperand(MF, MachineOperand::CreateMBB(TBB)); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp index 1ceae59dc993..83e71d731bfa 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp @@ -55,6 +55,7 @@ #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssembly.h" #include "WebAssemblySubtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/Support/Debug.h" using namespace llvm; @@ -221,10 +222,8 @@ private: assert(!Enterers.count(MBB)); if (Blocks.insert(MBB).second) { for (auto *Pred : MBB->predecessors()) { - if (!AddedToWorkList.count(Pred)) { + if (AddedToWorkList.insert(Pred).second) WorkList.push_back(Pred); - AddedToWorkList.insert(Pred); - } } } } @@ -491,6 +490,46 @@ FunctionPass *llvm::createWebAssemblyFixIrreducibleControlFlow() { return new WebAssemblyFixIrreducibleControlFlow(); } +// Test whether the given register has an ARGUMENT def. +static bool hasArgumentDef(unsigned Reg, const MachineRegisterInfo &MRI) { + for (const auto &Def : MRI.def_instructions(Reg)) + if (WebAssembly::isArgument(Def.getOpcode())) + return true; + return false; +} + +// Add a register definition with IMPLICIT_DEFs for every register to cover for +// register uses that don't have defs in every possible path. +// TODO: This is fairly heavy-handed; find a better approach. +static void addImplicitDefs(MachineFunction &MF) { + const MachineRegisterInfo &MRI = MF.getRegInfo(); + const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); + MachineBasicBlock &Entry = *MF.begin(); + for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) { + Register Reg = Register::index2VirtReg(I); + + // Skip unused registers. + if (MRI.use_nodbg_empty(Reg)) + continue; + + // Skip registers that have an ARGUMENT definition. + if (hasArgumentDef(Reg, MRI)) + continue; + + BuildMI(Entry, Entry.begin(), DebugLoc(), + TII.get(WebAssembly::IMPLICIT_DEF), Reg); + } + + // Move ARGUMENT_* instructions to the top of the entry block, so that their + // liveness reflects the fact that these really are live-in values. + for (MachineInstr &MI : llvm::make_early_inc_range(Entry)) { + if (WebAssembly::isArgument(MI.getOpcode())) { + MI.removeFromParent(); + Entry.insert(Entry.begin(), &MI); + } + } +} + bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction( MachineFunction &MF) { LLVM_DEBUG(dbgs() << "********** Fixing Irreducible Control Flow **********\n" @@ -505,8 +544,15 @@ bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction( if (LLVM_UNLIKELY(processRegion(&*MF.begin(), AllBlocks, MF))) { // We rewrote part of the function; recompute relevant things. - MF.getRegInfo().invalidateLiveness(); MF.RenumberBlocks(); + // Now we've inserted dispatch blocks, some register uses can have incoming + // paths without a def. For example, before this pass register %a was + // defined in BB1 and used in BB2, and there was only one path from BB1 and + // BB2. But if this pass inserts a dispatch block having multiple + // predecessors between the two BBs, now there are paths to BB2 without + // visiting BB1, and %a's use in BB2 is not dominated by its def. Adding + // IMPLICIT_DEFs to all regs is one simple way to fix it. + addImplicitDefs(MF); return true; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index a221f37cfd94..2636acaf1604 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -19,6 +19,8 @@ #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" #include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -159,22 +161,17 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( setTargetDAGCombine(ISD::VECTOR_SHUFFLE); // Combine extends of extract_subvectors into widening ops - setTargetDAGCombine(ISD::SIGN_EXTEND); - setTargetDAGCombine(ISD::ZERO_EXTEND); + setTargetDAGCombine({ISD::SIGN_EXTEND, ISD::ZERO_EXTEND}); // Combine int_to_fp or fp_extend of extract_vectors and vice versa into // conversions ops - setTargetDAGCombine(ISD::SINT_TO_FP); - setTargetDAGCombine(ISD::UINT_TO_FP); - setTargetDAGCombine(ISD::FP_EXTEND); - setTargetDAGCombine(ISD::EXTRACT_SUBVECTOR); + setTargetDAGCombine({ISD::SINT_TO_FP, ISD::UINT_TO_FP, ISD::FP_EXTEND, + ISD::EXTRACT_SUBVECTOR}); // Combine fp_to_{s,u}int_sat or fp_round of concat_vectors or vice versa // into conversion ops - setTargetDAGCombine(ISD::FP_TO_SINT_SAT); - setTargetDAGCombine(ISD::FP_TO_UINT_SAT); - setTargetDAGCombine(ISD::FP_ROUND); - setTargetDAGCombine(ISD::CONCAT_VECTORS); + setTargetDAGCombine({ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT, + ISD::FP_ROUND, ISD::CONCAT_VECTORS}); setTargetDAGCombine(ISD::TRUNCATE); @@ -577,7 +574,7 @@ LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB, // Move the function pointer to the end of the arguments for indirect calls if (IsIndirect) { auto FnPtr = CallParams.getOperand(0); - CallParams.RemoveOperand(0); + CallParams.removeOperand(0); // For funcrefs, call_indirect is done through __funcref_call_table and the // funcref is always installed in slot 0 of the table, therefore instead of having @@ -909,6 +906,30 @@ WebAssemblyTargetLowering::getPreferredVectorAction(MVT VT) const { return TargetLoweringBase::getPreferredVectorAction(VT); } +bool WebAssemblyTargetLowering::shouldSimplifyDemandedVectorElts( + SDValue Op, const TargetLoweringOpt &TLO) const { + // ISel process runs DAGCombiner after legalization; this step is called + // SelectionDAG optimization phase. This post-legalization combining process + // runs DAGCombiner on each node, and if there was a change to be made, + // re-runs legalization again on it and its user nodes to make sure + // everythiing is in a legalized state. + // + // The legalization calls lowering routines, and we do our custom lowering for + // build_vectors (LowerBUILD_VECTOR), which converts undef vector elements + // into zeros. But there is a set of routines in DAGCombiner that turns unused + // (= not demanded) nodes into undef, among which SimplifyDemandedVectorElts + // turns unused vector elements into undefs. But this routine does not work + // with our custom LowerBUILD_VECTOR, which turns undefs into zeros. This + // combination can result in a infinite loop, in which undefs are converted to + // zeros in legalization and back to undefs in combining. + // + // So after DAG is legalized, we prevent SimplifyDemandedVectorElts from + // running for build_vectors. + if (Op.getOpcode() == ISD::BUILD_VECTOR && TLO.LegalOps && TLO.LegalTys) + return false; + return true; +} + //===----------------------------------------------------------------------===// // WebAssembly Lowering private implementation. //===----------------------------------------------------------------------===// @@ -2110,8 +2131,7 @@ SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, auto GetMostCommon = [](auto &Counts) { auto CommonIt = - std::max_element(Counts.begin(), Counts.end(), - [](auto A, auto B) { return A.second < B.second; }); + std::max_element(Counts.begin(), Counts.end(), llvm::less_second()); assert(CommonIt != Counts.end() && "Unexpected all-undef build_vector"); return *CommonIt; }; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h index f7b460f61dbb..d86f2e59e3d2 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -113,6 +113,10 @@ private: report_fatal_error("llvm.clear_cache is not supported on wasm"); } + bool + shouldSimplifyDemandedVectorElts(SDValue Op, + const TargetLoweringOpt &TLO) const override; + // Custom lowering hooks. SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; SDValue LowerFrameIndex(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td index 42183d1645e1..ed80ed39f09c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td @@ -15,7 +15,7 @@ let UseNamedOperandTable = 1 in multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s, list<dag> pattern_r, string asmstr_r, string asmstr_s, bits<32> atomic_op, - string is64 = "false"> { + bit is64 = false> { defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s, !or(0xfe00, !and(0xff, atomic_op)), is64>, Requires<[HasAtomics]>; @@ -38,13 +38,13 @@ defm MEMORY_ATOMIC_NOTIFY_A32 : (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$count), (outs), (ins P2Align:$p2align, offset32_op:$off), [], "memory.atomic.notify \t$dst, ${off}(${addr})${p2align}, $count", - "memory.atomic.notify \t${off}${p2align}", 0x00, "false">; + "memory.atomic.notify \t${off}${p2align}", 0x00, false>; defm MEMORY_ATOMIC_NOTIFY_A64 : ATOMIC_I<(outs I32:$dst), (ins P2Align:$p2align, offset64_op:$off, I64:$addr, I32:$count), (outs), (ins P2Align:$p2align, offset64_op:$off), [], "memory.atomic.notify \t$dst, ${off}(${addr})${p2align}, $count", - "memory.atomic.notify \t${off}${p2align}", 0x00, "true">; + "memory.atomic.notify \t${off}${p2align}", 0x00, true>; let mayLoad = 1 in { defm MEMORY_ATOMIC_WAIT32_A32 : ATOMIC_I<(outs I32:$dst), @@ -52,28 +52,28 @@ defm MEMORY_ATOMIC_WAIT32_A32 : I64:$timeout), (outs), (ins P2Align:$p2align, offset32_op:$off), [], "memory.atomic.wait32 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout", - "memory.atomic.wait32 \t${off}${p2align}", 0x01, "false">; + "memory.atomic.wait32 \t${off}${p2align}", 0x01, false>; defm MEMORY_ATOMIC_WAIT32_A64 : ATOMIC_I<(outs I32:$dst), (ins P2Align:$p2align, offset64_op:$off, I64:$addr, I32:$exp, I64:$timeout), (outs), (ins P2Align:$p2align, offset64_op:$off), [], "memory.atomic.wait32 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout", - "memory.atomic.wait32 \t${off}${p2align}", 0x01, "true">; + "memory.atomic.wait32 \t${off}${p2align}", 0x01, true>; defm MEMORY_ATOMIC_WAIT64_A32 : ATOMIC_I<(outs I32:$dst), (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I64:$exp, I64:$timeout), (outs), (ins P2Align:$p2align, offset32_op:$off), [], "memory.atomic.wait64 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout", - "memory.atomic.wait64 \t${off}${p2align}", 0x02, "false">; + "memory.atomic.wait64 \t${off}${p2align}", 0x02, false>; defm MEMORY_ATOMIC_WAIT64_A64 : ATOMIC_I<(outs I32:$dst), (ins P2Align:$p2align, offset64_op:$off, I64:$addr, I64:$exp, I64:$timeout), (outs), (ins P2Align:$p2align, offset64_op:$off), [], "memory.atomic.wait64 \t$dst, ${off}(${addr})${p2align}, $exp, $timeout", - "memory.atomic.wait64 \t${off}${p2align}", 0x02, "true">; + "memory.atomic.wait64 \t${off}${p2align}", 0x02, true>; } // mayLoad = 1 } // hasSideEffects = 1 @@ -469,13 +469,13 @@ multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string name, (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val), (outs), (ins P2Align:$p2align, offset32_op:$off), [], !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"), - !strconcat(name, "\t${off}${p2align}"), atomic_op, "false">; + !strconcat(name, "\t${off}${p2align}"), atomic_op, false>; defm "_A64" : ATOMIC_I<(outs rc:$dst), (ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$val), (outs), (ins P2Align:$p2align, offset64_op:$off), [], !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"), - !strconcat(name, "\t${off}${p2align}"), atomic_op, "true">; + !strconcat(name, "\t${off}${p2align}"), atomic_op, true>; } defm ATOMIC_RMW_ADD_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.add", 0x1e>; @@ -767,14 +767,14 @@ multiclass WebAssemblyTerRMW<WebAssemblyRegClass rc, string name, rc:$new_), (outs), (ins P2Align:$p2align, offset32_op:$off), [], !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new_"), - !strconcat(name, "\t${off}${p2align}"), atomic_op, "false">; + !strconcat(name, "\t${off}${p2align}"), atomic_op, false>; defm "_A64" : ATOMIC_I<(outs rc:$dst), (ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$exp, rc:$new_), (outs), (ins P2Align:$p2align, offset64_op:$off), [], !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new_"), - !strconcat(name, "\t${off}${p2align}"), atomic_op, "true">; + !strconcat(name, "\t${off}${p2align}"), atomic_op, true>; } defm ATOMIC_RMW_CMPXCHG_I32 : diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td index 4dc0c9a46c38..f2e73dd19d6b 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td @@ -14,12 +14,12 @@ // WebAssembly Instruction Format. // We instantiate 2 of these for every actual instruction (register based // and stack based), see below. -class WebAssemblyInst<bits<32> inst, string asmstr, string stack, string is64> +class WebAssemblyInst<bits<32> inst, string asmstr, bit stack, bit is64> : StackRel, RegisterRel, Wasm64Rel, Instruction { bits<32> Inst = inst; // Instruction encoding. - string StackBased = stack; + bit StackBased = stack; string BaseName = NAME; - string IsWasm64 = is64; + bit IsWasm64 = is64; string Wasm32Name = !subst("_A64", "_A32", NAME); let Namespace = "WebAssembly"; let Pattern = []; @@ -30,8 +30,8 @@ class WebAssemblyInst<bits<32> inst, string asmstr, string stack, string is64> } // Normal instructions. Default instantiation of a WebAssemblyInst. -class NI<dag oops, dag iops, list<dag> pattern, string stack, - string asmstr = "", bits<32> inst = -1, string is64 = "false"> +class NI<dag oops, dag iops, list<dag> pattern, bit stack, + string asmstr = "", bits<32> inst = -1, bit is64 = false> : WebAssemblyInst<inst, asmstr, stack, is64> { dag OutOperandList = oops; dag InOperandList = iops; @@ -54,11 +54,11 @@ class NI<dag oops, dag iops, list<dag> pattern, string stack, // there is always an equivalent pair of instructions. multiclass I<dag oops_r, dag iops_r, dag oops_s, dag iops_s, list<dag> pattern_r, string asmstr_r = "", string asmstr_s = "", - bits<32> inst = -1, string is64 = "false"> { + bits<32> inst = -1, bit is64 = false> { let isCodeGenOnly = 1 in - def "" : NI<oops_r, iops_r, pattern_r, "false", asmstr_r, inst, is64>; + def "" : NI<oops_r, iops_r, pattern_r, false, asmstr_r, inst, is64>; let BaseName = NAME in - def _S : NI<oops_s, iops_s, [], "true", asmstr_s, inst, is64>; + def _S : NI<oops_s, iops_s, [], true, asmstr_s, inst, is64>; } // For instructions that have no register ops, so both sets are the same. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index 3fb0af1d47a0..134a0efc6822 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -66,6 +66,10 @@ def HasReferenceTypes : Predicate<"Subtarget->hasReferenceTypes()">, AssemblerPredicate<(all_of FeatureReferenceTypes), "reference-types">; +def HasExtendedConst : + Predicate<"Subtarget->hasExtendedConst()">, + AssemblerPredicate<(all_of FeatureExtendedConst), "extended-const">; + //===----------------------------------------------------------------------===// // WebAssembly-specific DAG Node Types. //===----------------------------------------------------------------------===// @@ -221,8 +225,8 @@ def getStackOpcode : InstrMapping { let FilterClass = "StackRel"; let RowFields = ["BaseName"]; let ColFields = ["StackBased"]; - let KeyCol = ["false"]; - let ValueCols = [["true"]]; + let KeyCol = ["0"]; + let ValueCols = [["1"]]; } //===----------------------------------------------------------------------===// @@ -234,8 +238,8 @@ def getRegisterOpcode : InstrMapping { let FilterClass = "RegisterRel"; let RowFields = ["BaseName"]; let ColFields = ["StackBased"]; - let KeyCol = ["true"]; - let ValueCols = [["false"]]; + let KeyCol = ["1"]; + let ValueCols = [["0"]]; } //===----------------------------------------------------------------------===// @@ -247,8 +251,8 @@ def getWasm64Opcode : InstrMapping { let FilterClass = "Wasm64Rel"; let RowFields = ["Wasm32Name"]; let ColFields = ["IsWasm64"]; - let KeyCol = ["false"]; - let ValueCols = [["true"]]; + let KeyCol = ["0"]; + let ValueCols = [["1"]]; } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td index a70f62dde845..d5bb9e9e48b4 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -47,13 +47,13 @@ multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode, (ins P2Align:$p2align, offset32_op:$off, I32:$addr), (outs), (ins P2Align:$p2align, offset32_op:$off), [], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"), - !strconcat(Name, "\t${off}${p2align}"), Opcode, "false">, + !strconcat(Name, "\t${off}${p2align}"), Opcode, false>, Requires<reqs>; defm "_A64": I<(outs rc:$dst), (ins P2Align:$p2align, offset64_op:$off, I64:$addr), (outs), (ins P2Align:$p2align, offset64_op:$off), [], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"), - !strconcat(Name, "\t${off}${p2align}"), Opcode, "true">, + !strconcat(Name, "\t${off}${p2align}"), Opcode, true>, Requires<reqs>; } } @@ -244,7 +244,7 @@ multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode, (outs), (ins P2Align:$p2align, offset32_op:$off), [], !strconcat(Name, "\t${off}(${addr})${p2align}, $val"), - !strconcat(Name, "\t${off}${p2align}"), Opcode, "false">, + !strconcat(Name, "\t${off}${p2align}"), Opcode, false>, Requires<reqs>; let mayStore = 1, UseNamedOperandTable = 1 in defm "_A64" : I<(outs), @@ -252,7 +252,7 @@ multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode, (outs), (ins P2Align:$p2align, offset64_op:$off), [], !strconcat(Name, "\t${off}(${addr})${p2align}, $val"), - !strconcat(Name, "\t${off}${p2align}"), Opcode, "true">, + !strconcat(Name, "\t${off}${p2align}"), Opcode, true>, Requires<reqs>; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td index 76a88caafc47..608963d58863 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td @@ -27,6 +27,12 @@ multiclass REF_I<WebAssemblyRegClass rc, ValueType vt, string ht> { vt#".select\t$dst, $lhs, $rhs, $cond", vt#".select", 0x1b>, Requires<[HasReferenceTypes]>; + defm REF_IS_NULL_#rc + : I<(outs I32:$dst), (ins rc:$ref), (outs), (ins), + [(set I32:$dst, (!cast<Intrinsic>("int_wasm_ref_is_null_" # ht) rc:$ref))], + "ref.is_null\t$ref", + "ref.is_null", 0xd1>, + Requires<[HasReferenceTypes]>; } defm "" : REF_I<FUNCREF, funcref, "func">; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td index 5bb12c7fbdc7..ed3cc7ed1c53 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -1229,9 +1229,9 @@ def trunc_sat_zero_s : SDNode<"WebAssemblyISD::TRUNC_SAT_ZERO_S", trunc_sat_zero_t>; def trunc_sat_zero_u : SDNode<"WebAssemblyISD::TRUNC_SAT_ZERO_U", trunc_sat_zero_t>; -defm "" : SIMDConvert<I32x4, F64x2, trunc_sat_zero_s, "trunc_sat_zero_f64x2_s", +defm "" : SIMDConvert<I32x4, F64x2, trunc_sat_zero_s, "trunc_sat_f64x2_s_zero", 0xfc>; -defm "" : SIMDConvert<I32x4, F64x2, trunc_sat_zero_u, "trunc_sat_zero_f64x2_u", +defm "" : SIMDConvert<I32x4, F64x2, trunc_sat_zero_u, "trunc_sat_f64x2_u_zero", 0xfd>; // Integer to floating point: convert @@ -1307,7 +1307,7 @@ defm "" : SIMDConvert<I32x4, I16x8, int_wasm_extadd_pairwise_unsigned, def demote_t : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisVec<1>]>; def demote_zero : SDNode<"WebAssemblyISD::DEMOTE_ZERO", demote_t>; defm "" : SIMDConvert<F32x4, F64x2, demote_zero, - "demote_zero_f64x2", 0x5e>; + "demote_f64x2_zero", 0x5e>; def promote_t : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisVec<1>]>; def promote_low : SDNode<"WebAssemblyISD::PROMOTE_LOW", promote_t>; @@ -1334,7 +1334,37 @@ defm Q15MULR_SAT_S : SIMDBinary<I16x8, int_wasm_q15mulr_sat_signed, "q15mulr_sat_s", 0x82>; //===----------------------------------------------------------------------===// -// Fused Multiply- Add and Subtract (FMA/FMS) +// Relaxed swizzle +//===----------------------------------------------------------------------===// + +defm RELAXED_SWIZZLE : + RELAXED_I<(outs V128:$dst), (ins V128:$src, V128:$mask), (outs), (ins), + [(set (v16i8 V128:$dst), + (int_wasm_relaxed_swizzle (v16i8 V128:$src), (v16i8 V128:$mask)))], + "i8x16.relaxed_swizzle\t$dst, $src, $mask", "i8x16.relaxed_swizzle", 0x100>; + +//===----------------------------------------------------------------------===// +// Relaxed floating-point to int conversions +//===----------------------------------------------------------------------===// + +multiclass RelaxedConvert<Vec vec, Vec arg, SDPatternOperator op, string name, bits<32> simdop> { + defm op#_#vec : + RELAXED_I<(outs V128:$dst), (ins V128:$vec), (outs), (ins), + [(set (vec.vt V128:$dst), (vec.vt (op (arg.vt V128:$vec))))], + vec.prefix#"."#name#"\t$dst, $vec", vec.prefix#"."#name, simdop>; +} + +defm "" : RelaxedConvert<I32x4, F32x4, int_wasm_relaxed_trunc_signed, + "relaxed_trunc_f32x4_s", 0x101>; +defm "" : RelaxedConvert<I32x4, F32x4, int_wasm_relaxed_trunc_unsigned, + "relaxed_trunc_f32x4_u", 0x102>; +defm "" : RelaxedConvert<I32x4, F64x2, int_wasm_relaxed_trunc_signed_zero, + "relaxed_trunc_f64x2_s_zero", 0x103>; +defm "" : RelaxedConvert<I32x4, F64x2, int_wasm_relaxed_trunc_unsigned_zero, + "relaxed_trunc_f64x2_u_zero", 0x104>; + +//===----------------------------------------------------------------------===// +// Relaxed Fused Multiply- Add and Subtract (FMA/FMS) //===----------------------------------------------------------------------===// multiclass SIMDFM<Vec vec, bits<32> simdopA, bits<32> simdopS> { @@ -1342,16 +1372,18 @@ multiclass SIMDFM<Vec vec, bits<32> simdopA, bits<32> simdopS> { RELAXED_I<(outs V128:$dst), (ins V128:$a, V128:$b, V128:$c), (outs), (ins), [(set (vec.vt V128:$dst), (int_wasm_fma (vec.vt V128:$a), (vec.vt V128:$b), (vec.vt V128:$c)))], - vec.prefix#".fma\t$dst, $a, $b, $c", vec.prefix#".fma", simdopA>; + vec.prefix#".relaxed_fma\t$dst, $a, $b, $c", + vec.prefix#".relaxed_fma", simdopA>; defm FMS_#vec : RELAXED_I<(outs V128:$dst), (ins V128:$a, V128:$b, V128:$c), (outs), (ins), [(set (vec.vt V128:$dst), (int_wasm_fms (vec.vt V128:$a), (vec.vt V128:$b), (vec.vt V128:$c)))], - vec.prefix#".fms\t$dst, $a, $b, $c", vec.prefix#".fms", simdopS>; + vec.prefix#".relaxed_fms\t$dst, $a, $b, $c", + vec.prefix#".relaxed_fms", simdopS>; } -defm "" : SIMDFM<F32x4, 0xaf, 0xb0>; -defm "" : SIMDFM<F64x2, 0xcf, 0xd0>; +defm "" : SIMDFM<F32x4, 0x105, 0x106>; +defm "" : SIMDFM<F64x2, 0x107, 0x108>; //===----------------------------------------------------------------------===// // Laneselect @@ -1362,58 +1394,61 @@ multiclass SIMDLANESELECT<Vec vec, bits<32> op> { RELAXED_I<(outs V128:$dst), (ins V128:$a, V128:$b, V128:$c), (outs), (ins), [(set (vec.vt V128:$dst), (int_wasm_laneselect (vec.vt V128:$a), (vec.vt V128:$b), (vec.vt V128:$c)))], - vec.prefix#".laneselect\t$dst, $a, $b, $c", vec.prefix#".laneselect", op>; + vec.prefix#".relaxed_laneselect\t$dst, $a, $b, $c", + vec.prefix#".relaxed_laneselect", op>; } -defm "" : SIMDLANESELECT<I8x16, 0xb2>; -defm "" : SIMDLANESELECT<I16x8, 0xb3>; -defm "" : SIMDLANESELECT<I32x4, 0xd2>; -defm "" : SIMDLANESELECT<I64x2, 0xd3>; - - -//===----------------------------------------------------------------------===// -// Relaxed swizzle -//===----------------------------------------------------------------------===// - -defm RELAXED_SWIZZLE : - RELAXED_I<(outs V128:$dst), (ins V128:$src, V128:$mask), (outs), (ins), - [(set (v16i8 V128:$dst), - (int_wasm_relaxed_swizzle (v16i8 V128:$src), (v16i8 V128:$mask)))], - "i8x16.relaxed_swizzle\t$dst, $src, $mask", "i8x16.relaxed_swizzle", 162>; +defm "" : SIMDLANESELECT<I8x16, 0x109>; +defm "" : SIMDLANESELECT<I16x8, 0x10a>; +defm "" : SIMDLANESELECT<I32x4, 0x10b>; +defm "" : SIMDLANESELECT<I64x2, 0x10c>; //===----------------------------------------------------------------------===// // Relaxed floating-point min and max. //===----------------------------------------------------------------------===// -multiclass SIMD_RELAXED_FMINMAX<Vec vec, bits<32> simdopMin, bits<32> simdopMax> { - defm RELAXED_FMIN_#vec : - RELAXED_I<(outs V128:$dst), (ins V128:$a, V128:$b), (outs), (ins), - [(set (vec.vt V128:$dst), (int_wasm_relaxed_min - (vec.vt V128:$a), (vec.vt V128:$b)))], - vec.prefix#".relaxed_min\t$dst, $a, $b", vec.prefix#".relaxed_min", simdopMin>; - defm RELAXED_FMAX_#vec : - RELAXED_I<(outs V128:$dst), (ins V128:$a, V128:$b), (outs), (ins), - [(set (vec.vt V128:$dst), (int_wasm_relaxed_max - (vec.vt V128:$a), (vec.vt V128:$b)))], - vec.prefix#".relaxed_max\t$dst, $a, $b", vec.prefix#".relaxed_max", simdopMax>; +multiclass RelaxedBinary<Vec vec, SDPatternOperator node, string name, + bits<32> simdop> { + defm _#vec : RELAXED_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs), + (outs), (ins), + [(set (vec.vt V128:$dst), + (node (vec.vt V128:$lhs), (vec.vt V128:$rhs)))], + vec.prefix#"."#name#"\t$dst, $lhs, $rhs", + vec.prefix#"."#name, simdop>; } -defm "" : SIMD_RELAXED_FMINMAX<F32x4, 0xb4, 0xe2>; -defm "" : SIMD_RELAXED_FMINMAX<F64x2, 0xd4, 0xee>; +defm SIMD_RELAXED_FMIN : + RelaxedBinary<F32x4, int_wasm_relaxed_min, "relaxed_min", 0x10d>; +defm SIMD_RELAXED_FMAX : + RelaxedBinary<F32x4, int_wasm_relaxed_max, "relaxed_max", 0x10e>; +defm SIMD_RELAXED_FMIN : + RelaxedBinary<F64x2, int_wasm_relaxed_min, "relaxed_min", 0x10f>; +defm SIMD_RELAXED_FMAX : + RelaxedBinary<F64x2, int_wasm_relaxed_max, "relaxed_max", 0x110>; //===----------------------------------------------------------------------===// -// Relaxed floating-point to int conversions +// Relaxed rounding q15 multiplication //===----------------------------------------------------------------------===// -multiclass SIMD_RELAXED_CONVERT<Vec vec, Vec arg, SDPatternOperator op, string name, bits<32> simdop> { - defm op#_#vec : - RELAXED_I<(outs V128:$dst), (ins V128:$vec), (outs), (ins), - [(set (vec.vt V128:$dst), (vec.vt (op (arg.vt V128:$vec))))], - vec.prefix#"."#name#"\t$dst, $vec", vec.prefix#"."#name, simdop>; -} +defm RELAXED_Q15MULR_S : + RelaxedBinary<I16x8, int_wasm_relaxed_q15mulr_signed, "relaxed_q15mulr_s", + 0x111>; + +//===----------------------------------------------------------------------===// +// Relaxed integer dot product +//===----------------------------------------------------------------------===// -defm "" : SIMD_RELAXED_CONVERT<I32x4, F32x4, int_wasm_relaxed_trunc_signed, "relaxed_trunc_f32x4_s", 0xa5>; -defm "" : SIMD_RELAXED_CONVERT<I32x4, F32x4, int_wasm_relaxed_trunc_unsigned, "relaxed_trunc_f32x4_u", 0xa6>; +defm RELAXED_DOT : + RELAXED_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs), (outs), (ins), + [(set (v8i16 V128:$dst), (int_wasm_dot_i8x16_i7x16_signed + (v16i8 V128:$lhs), (v16i8 V128:$rhs)))], + "i16x8.dot_i8x16_i7x16_s\t$dst, $lhs, $rhs", + "i16x8.dot_i8x16_i7x16_s", 0x112>; -defm "" : SIMD_RELAXED_CONVERT<I32x4, F64x2, int_wasm_relaxed_trunc_zero_signed, "relaxed_trunc_f64x2_s_zero", 0xc5>; -defm "" : SIMD_RELAXED_CONVERT<I32x4, F64x2, int_wasm_relaxed_trunc_zero_unsigned, "relaxed_trunc_f64x2_u_zero", 0xc6>; +defm RELAXED_DOT_ADD : + RELAXED_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs, V128:$acc), + (outs), (ins), + [(set (v4i32 V128:$dst), (int_wasm_dot_i8x16_i7x16_add_signed + (v16i8 V128:$lhs), (v16i8 V128:$rhs), (v4i32 V128:$acc)))], + "i32x4.dot_i8x16_i7x16_add_s\t$dst, $lhs, $rhs, $acc", + "i32x4.dot_i8x16_i7x16_add_s", 0x113>; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp index 309fcaf340eb..d16bb6b6648a 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp @@ -16,6 +16,7 @@ #include "WebAssembly.h" #include "WebAssemblySubtarget.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/WasmEHFuncInfo.h" #include "llvm/MC/MCAsmInfo.h" @@ -72,9 +73,8 @@ WebAssemblyLateEHPrepare::getMatchingEHPad(MachineInstr *MI) { MachineBasicBlock *EHPad = nullptr; while (!WL.empty()) { MachineBasicBlock *MBB = WL.pop_back_val(); - if (Visited.count(MBB)) + if (!Visited.insert(MBB).second) continue; - Visited.insert(MBB); if (MBB->isEHPad()) { if (EHPad && EHPad != MBB) return nullptr; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp index b6c43be03aba..2db4bd822349 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -406,8 +406,9 @@ static bool canThrow(const Value *V) { return true; } -// Get a global variable with the given name. If it doesn't exist declare it, -// which will generate an import and assume that it will exist at link time. +// Get a thread-local global variable with the given name. If it doesn't exist +// declare it, which will generate an import and assume that it will exist at +// link time. static GlobalVariable *getGlobalVariable(Module &M, Type *Ty, WebAssemblyTargetMachine &TM, const char *Name) { @@ -415,16 +416,11 @@ static GlobalVariable *getGlobalVariable(Module &M, Type *Ty, if (!GV) report_fatal_error(Twine("unable to create global: ") + Name); - // If the target supports TLS, make this variable thread-local. We can't just - // unconditionally make it thread-local and depend on - // CoalesceFeaturesAndStripAtomics to downgrade it, because stripping TLS has - // the side effect of disallowing the object from being linked into a - // shared-memory module, which we don't want to be responsible for. - auto *Subtarget = TM.getSubtargetImpl(); - auto TLS = Subtarget->hasAtomics() && Subtarget->hasBulkMemory() - ? GlobalValue::LocalExecTLSModel - : GlobalValue::NotThreadLocal; - GV->setThreadLocalMode(TLS); + // Variables created by this function are thread local. If the target does not + // support TLS, we depend on CoalesceFeaturesAndStripAtomics to downgrade it + // to non-thread-local ones, in which case we don't allow this object to be + // linked with other objects using shared memory. + GV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel); return GV; } @@ -556,7 +552,7 @@ Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallBase *CI) { Optional<unsigned> NEltArg; std::tie(SizeArg, NEltArg) = FnAttrs.getAllocSizeArgs(); SizeArg += 1; - if (NEltArg.hasValue()) + if (NEltArg) NEltArg = NEltArg.getValue() + 1; FnAttrs.addAllocSizeAttr(SizeArg, NEltArg); } @@ -1064,22 +1060,16 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { nullifySetjmp(F); } - if (!Changed) { - // Delete unused global variables and functions - if (ResumeF) - ResumeF->eraseFromParent(); - if (EHTypeIDF) - EHTypeIDF->eraseFromParent(); - if (EmLongjmpF) - EmLongjmpF->eraseFromParent(); - if (SaveSetjmpF) - SaveSetjmpF->eraseFromParent(); - if (TestSetjmpF) - TestSetjmpF->eraseFromParent(); - return false; - } + // Delete unused global variables and functions + for (auto *V : {ThrewGV, ThrewValueGV}) + if (V && V->use_empty()) + V->eraseFromParent(); + for (auto *V : {GetTempRet0F, SetTempRet0F, ResumeF, EHTypeIDF, EmLongjmpF, + SaveSetjmpF, TestSetjmpF, WasmLongjmpF, CatchF}) + if (V && V->use_empty()) + V->eraseFromParent(); - return true; + return Changed; } bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { @@ -1324,9 +1314,14 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { BasicBlock *BB = CB->getParent(); if (BB->getParent() != &F) // in other function continue; - if (CB->getOperandBundle(LLVMContext::OB_funclet)) - report_fatal_error( - "setjmp within a catch clause is not supported in Wasm EH"); + if (CB->getOperandBundle(LLVMContext::OB_funclet)) { + std::string S; + raw_string_ostream SS(S); + SS << "In function " + F.getName() + + ": setjmp within a catch clause is not supported in Wasm EH:\n"; + SS << *CB; + report_fatal_error(StringRef(SS.str())); + } CallInst *CI = nullptr; // setjmp cannot throw. So if it is an invoke, lower it to a call @@ -1502,10 +1497,16 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForEmscriptenSjLj( for (unsigned I = 0; I < BBs.size(); I++) { BasicBlock *BB = BBs[I]; for (Instruction &I : *BB) { - if (isa<InvokeInst>(&I)) - report_fatal_error("When using Wasm EH with Emscripten SjLj, there is " - "a restriction that `setjmp` function call and " - "exception cannot be used within the same function"); + if (isa<InvokeInst>(&I)) { + std::string S; + raw_string_ostream SS(S); + SS << "In function " << F.getName() + << ": When using Wasm EH with Emscripten SjLj, there is a " + "restriction that `setjmp` function call and exception cannot be " + "used within the same function:\n"; + SS << I; + report_fatal_error(StringRef(SS.str())); + } auto *CI = dyn_cast<CallInst>(&I); if (!CI) continue; @@ -1829,7 +1830,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::handleLongjmpableCallsForWasmSjLj( if (auto *CPI = dyn_cast<CatchPadInst>(FromPad)) { UnwindDest = CPI->getCatchSwitch()->getUnwindDest(); break; - } else if (auto *CPI = dyn_cast<CleanupPadInst>(FromPad)) { + } + if (auto *CPI = dyn_cast<CleanupPadInst>(FromPad)) { // getCleanupRetUnwindDest() can return nullptr when // 1. This cleanuppad's matching cleanupret uwninds to caller // 2. There is no matching cleanupret because it ends with diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp deleted file mode 100644 index ca6f3f194645..000000000000 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp +++ /dev/null @@ -1,210 +0,0 @@ -//===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===// -// -// 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 -/// Lower @llvm.global_dtors. -/// -/// WebAssembly doesn't have a builtin way to invoke static destructors. -/// Implement @llvm.global_dtors by creating wrapper functions that are -/// registered in @llvm.global_ctors and which contain a call to -/// `__cxa_atexit` to register their destructor functions. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Utils/ModuleUtils.h" -#include <map> - -using namespace llvm; - -#define DEBUG_TYPE "wasm-lower-global-dtors" - -namespace { -class LowerGlobalDtors final : public ModulePass { - StringRef getPassName() const override { - return "WebAssembly Lower @llvm.global_dtors"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - ModulePass::getAnalysisUsage(AU); - } - - bool runOnModule(Module &M) override; - -public: - static char ID; - LowerGlobalDtors() : ModulePass(ID) {} -}; -} // End anonymous namespace - -char LowerGlobalDtors::ID = 0; -INITIALIZE_PASS(LowerGlobalDtors, DEBUG_TYPE, - "Lower @llvm.global_dtors for WebAssembly", false, false) - -ModulePass *llvm::createWebAssemblyLowerGlobalDtors() { - return new LowerGlobalDtors(); -} - -bool LowerGlobalDtors::runOnModule(Module &M) { - LLVM_DEBUG(dbgs() << "********** Lower Global Destructors **********\n"); - - GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors"); - if (!GV || !GV->hasInitializer()) - return false; - - const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); - if (!InitList) - return false; - - // Validate @llvm.global_dtor's type. - auto *ETy = dyn_cast<StructType>(InitList->getType()->getElementType()); - if (!ETy || ETy->getNumElements() != 3 || - !ETy->getTypeAtIndex(0U)->isIntegerTy() || - !ETy->getTypeAtIndex(1U)->isPointerTy() || - !ETy->getTypeAtIndex(2U)->isPointerTy()) - return false; // Not (int, ptr, ptr). - - // Collect the contents of @llvm.global_dtors, ordered by priority. Within a - // priority, sequences of destructors with the same associated object are - // recorded so that we can register them as a group. - std::map< - uint16_t, - std::vector<std::pair<Constant *, std::vector<Constant *>>> - > DtorFuncs; - for (Value *O : InitList->operands()) { - auto *CS = dyn_cast<ConstantStruct>(O); - if (!CS) - continue; // Malformed. - - auto *Priority = dyn_cast<ConstantInt>(CS->getOperand(0)); - if (!Priority) - continue; // Malformed. - uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX); - - Constant *DtorFunc = CS->getOperand(1); - if (DtorFunc->isNullValue()) - break; // Found a null terminator, skip the rest. - - Constant *Associated = CS->getOperand(2); - Associated = cast<Constant>(Associated->stripPointerCasts()); - - auto &AtThisPriority = DtorFuncs[PriorityValue]; - if (AtThisPriority.empty() || AtThisPriority.back().first != Associated) { - std::vector<Constant *> NewList; - NewList.push_back(DtorFunc); - AtThisPriority.push_back(std::make_pair(Associated, NewList)); - } else { - AtThisPriority.back().second.push_back(DtorFunc); - } - } - if (DtorFuncs.empty()) - return false; - - // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); - LLVMContext &C = M.getContext(); - PointerType *VoidStar = Type::getInt8PtrTy(C); - Type *AtExitFuncArgs[] = {VoidStar}; - FunctionType *AtExitFuncTy = - FunctionType::get(Type::getVoidTy(C), AtExitFuncArgs, - /*isVarArg=*/false); - - FunctionCallee AtExit = M.getOrInsertFunction( - "__cxa_atexit", - FunctionType::get(Type::getInt32Ty(C), - {PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar}, - /*isVarArg=*/false)); - - // Declare __dso_local. - Constant *DsoHandle = M.getNamedValue("__dso_handle"); - if (!DsoHandle) { - Type *DsoHandleTy = Type::getInt8Ty(C); - GlobalVariable *Handle = new GlobalVariable( - M, DsoHandleTy, /*isConstant=*/true, - GlobalVariable::ExternalWeakLinkage, nullptr, "__dso_handle"); - Handle->setVisibility(GlobalVariable::HiddenVisibility); - DsoHandle = Handle; - } - - // For each unique priority level and associated symbol, generate a function - // to call all the destructors at that level, and a function to register the - // first function with __cxa_atexit. - for (auto &PriorityAndMore : DtorFuncs) { - uint16_t Priority = PriorityAndMore.first; - uint64_t Id = 0; - auto &AtThisPriority = PriorityAndMore.second; - for (auto &AssociatedAndMore : AtThisPriority) { - Constant *Associated = AssociatedAndMore.first; - auto ThisId = Id++; - - Function *CallDtors = Function::Create( - AtExitFuncTy, Function::PrivateLinkage, - "call_dtors" + - (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) - : Twine()) + - (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) - : Twine()) + - (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) - : Twine()), - &M); - BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors); - FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C), - /*isVarArg=*/false); - - for (auto Dtor : reverse(AssociatedAndMore.second)) - CallInst::Create(VoidVoid, Dtor, "", BB); - ReturnInst::Create(C, BB); - - Function *RegisterCallDtors = Function::Create( - VoidVoid, Function::PrivateLinkage, - "register_call_dtors" + - (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) - : Twine()) + - (AtThisPriority.size() > 1 ? Twine("$") + Twine(ThisId) - : Twine()) + - (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) - : Twine()), - &M); - BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors); - BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors); - BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors); - - Value *Null = ConstantPointerNull::get(VoidStar); - Value *Args[] = {CallDtors, Null, DsoHandle}; - Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB); - Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res, - Constant::getNullValue(Res->getType())); - BranchInst::Create(FailBB, RetBB, Cmp, EntryBB); - - // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave. - // This should be very rare, because if the process is running out of - // memory before main has even started, something is wrong. - CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), "", - FailBB); - new UnreachableInst(C, FailBB); - - ReturnInst::Create(C, RetBB); - - // Now register the registration function with @llvm.global_ctors. - appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated); - } - } - - // Now that we've lowered everything, remove @llvm.global_dtors. - GV->eraseFromParent(); - - return true; -} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp index 37ac8e75f4b7..21f6fd37d402 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCLowerPrePass.cpp @@ -65,6 +65,9 @@ ModulePass *llvm::createWebAssemblyMCLowerPrePass() { // for all functions before AsmPrinter. If this way of doing things is ever // suboptimal, we could opt to make it a MachineFunctionPass and instead use // something like createBarrierNoopPass() to enforce ordering. +// +// The information stored here is essential for emitExternalDecls in the Wasm +// AsmPrinter bool WebAssemblyMCLowerPrePass::runOnModule(Module &M) { auto *MMIWP = getAnalysisIfAvailable<MachineModuleInfoWrapperPass>(); if (!MMIWP) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp index ea80e96d50de..96284687971c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp @@ -24,6 +24,16 @@ using namespace llvm; WebAssemblyFunctionInfo::~WebAssemblyFunctionInfo() = default; // anchor. +MachineFunctionInfo *WebAssemblyFunctionInfo::clone( + BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB) + const { + WebAssemblyFunctionInfo *Clone = + DestMF.cloneInfo<WebAssemblyFunctionInfo>(*this); + Clone->MF = &DestMF; + return Clone; +} + void WebAssemblyFunctionInfo::initWARegs(MachineRegisterInfo &MRI) { assert(WARegs.empty()); unsigned Reg = UnusedReg; @@ -153,7 +163,7 @@ void WebAssemblyFunctionInfo::initializeBaseYamlFields( addResult(WebAssembly::parseMVT(VT.Value)); if (WasmEHInfo) { for (auto KV : YamlMFI.SrcToUnwindDest) - WasmEHInfo->setUnwindDest(MF.getBlockNumbered(KV.first), - MF.getBlockNumbered(KV.second)); + WasmEHInfo->setUnwindDest(MF->getBlockNumbered(KV.first), + MF->getBlockNumbered(KV.second)); } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h index 413d0d1dc554..619617049bb2 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h @@ -31,7 +31,7 @@ struct WebAssemblyFunctionInfo; /// This class is derived from MachineFunctionInfo and contains private /// WebAssembly-specific information for each MachineFunction. class WebAssemblyFunctionInfo final : public MachineFunctionInfo { - const MachineFunction &MF; + const MachineFunction *MF; std::vector<MVT> Params; std::vector<MVT> Results; @@ -70,11 +70,16 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo { WasmEHFuncInfo *WasmEHInfo = nullptr; public: - explicit WebAssemblyFunctionInfo(MachineFunction &MF) - : MF(MF), WasmEHInfo(MF.getWasmEHFuncInfo()) {} + explicit WebAssemblyFunctionInfo(MachineFunction &MF_) + : MF(&MF_), WasmEHInfo(MF_.getWasmEHFuncInfo()) {} ~WebAssemblyFunctionInfo() override; - const MachineFunction &getMachineFunction() const { return MF; } + MachineFunctionInfo * + clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB) + const override; + + const MachineFunction &getMachineFunction() const { return *MF; } void initializeBaseYamlFields(const yaml::WebAssemblyFunctionInfo &YamlMFI); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp index 62fa089a94d4..5d8c58dcc334 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyNullifyDebugValueLists.cpp @@ -16,6 +16,7 @@ #include "WebAssembly.h" #include "WebAssemblySubtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" using namespace llvm; #define DEBUG_TYPE "wasm-nullify-dbg-value-lists" diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp index 6a6cac6d956f..d542ddb45c2e 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp @@ -49,6 +49,11 @@ class WebAssemblyOptimizeLiveIntervals final : public MachineFunctionPass { MachineFunctionPass::getAnalysisUsage(AU); } + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::TracksLiveness); + } + bool runOnMachineFunction(MachineFunction &MF) override; public: @@ -102,7 +107,7 @@ bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction( SplitLIs.clear(); } - // In PrepareForLiveIntervals, we conservatively inserted IMPLICIT_DEF + // In FixIrreducibleControlFlow, we conservatively inserted IMPLICIT_DEF // instructions to satisfy LiveIntervals' requirement that all uses be // dominated by defs. Now that LiveIntervals has computed which of these // defs are actually needed and which are dead, remove the dead ones. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp deleted file mode 100644 index 5682cadc1a64..000000000000 --- a/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp +++ /dev/null @@ -1,126 +0,0 @@ -//===- WebAssemblyPrepareForLiveIntervals.cpp - Prepare for LiveIntervals -===// -// -// 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 -/// Fix up code to meet LiveInterval's requirements. -/// -/// Some CodeGen passes don't preserve LiveInterval's requirements, because -/// they run after register allocation and it isn't important. However, -/// WebAssembly runs LiveIntervals in a late pass. This pass transforms code -/// to meet LiveIntervals' requirements; primarily, it ensures that all -/// virtual register uses have definitions (IMPLICIT_DEF definitions if -/// nothing else). -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "Utils/WebAssemblyUtilities.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-prepare-for-live-intervals" - -namespace { -class WebAssemblyPrepareForLiveIntervals final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyPrepareForLiveIntervals() : MachineFunctionPass(ID) {} - -private: - StringRef getPassName() const override { - return "WebAssembly Prepare For LiveIntervals"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; -}; -} // end anonymous namespace - -char WebAssemblyPrepareForLiveIntervals::ID = 0; -INITIALIZE_PASS(WebAssemblyPrepareForLiveIntervals, DEBUG_TYPE, - "Fix up code for LiveIntervals", false, false) - -FunctionPass *llvm::createWebAssemblyPrepareForLiveIntervals() { - return new WebAssemblyPrepareForLiveIntervals(); -} - -// Test whether the given register has an ARGUMENT def. -static bool hasArgumentDef(unsigned Reg, const MachineRegisterInfo &MRI) { - for (const auto &Def : MRI.def_instructions(Reg)) - if (WebAssembly::isArgument(Def.getOpcode())) - return true; - return false; -} - -bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction( - MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Prepare For LiveIntervals **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - bool Changed = false; - MachineRegisterInfo &MRI = MF.getRegInfo(); - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - MachineBasicBlock &Entry = *MF.begin(); - - assert(!mustPreserveAnalysisID(LiveIntervalsID) && - "LiveIntervals shouldn't be active yet!"); - - // We don't preserve SSA form. - MRI.leaveSSA(); - - // BranchFolding and perhaps other passes don't preserve IMPLICIT_DEF - // instructions. LiveIntervals requires that all paths to virtual register - // uses provide a definition. Insert IMPLICIT_DEFs in the entry block to - // conservatively satisfy this. - // - // TODO: This is fairly heavy-handed; find a better approach. - // - for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) { - Register Reg = Register::index2VirtReg(I); - - // Skip unused registers. - if (MRI.use_nodbg_empty(Reg)) - continue; - - // Skip registers that have an ARGUMENT definition. - if (hasArgumentDef(Reg, MRI)) - continue; - - BuildMI(Entry, Entry.begin(), DebugLoc(), - TII.get(WebAssembly::IMPLICIT_DEF), Reg); - Changed = true; - } - - // Move ARGUMENT_* instructions to the top of the entry block, so that their - // liveness reflects the fact that these really are live-in values. - for (MachineInstr &MI : llvm::make_early_inc_range(Entry)) { - if (WebAssembly::isArgument(MI.getOpcode())) { - MI.removeFromParent(); - Entry.insert(Entry.begin(), &MI); - } - } - - // Ok, we're now ready to run the LiveIntervals analysis again. - MF.getProperties().set(MachineFunctionProperties::Property::TracksLiveness); - - return Changed; -} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp index 71f0bd28e1be..1e2bee7a5c73 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp @@ -72,9 +72,6 @@ bool WebAssemblyReplacePhysRegs::runOnMachineFunction(MachineFunction &MF) { assert(!mustPreserveAnalysisID(LiveIntervalsID) && "LiveIntervals shouldn't be active yet!"); - // We don't preserve SSA or liveness. - MRI.leaveSSA(); - MRI.invalidateLiveness(); for (unsigned PReg = WebAssembly::NoRegister + 1; PReg < WebAssembly::NUM_TARGET_REGS; ++PReg) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp index 16e05150c64e..74af4c8873f7 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp @@ -44,7 +44,7 @@ SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemmove( SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemset( SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Dst, SDValue Val, - SDValue Size, Align Alignment, bool IsVolatile, + SDValue Size, Align Alignment, bool IsVolatile, bool AlwaysInline, MachinePointerInfo DstPtrInfo) const { auto &ST = DAG.getMachineFunction().getSubtarget<WebAssemblySubtarget>(); if (!ST.hasBulkMemory()) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h index f4d2132fd3af..fd517b238715 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h @@ -37,6 +37,7 @@ public: SDValue EmitTargetCodeForMemset(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Op1, SDValue Op2, SDValue Op3, Align Alignment, bool IsVolatile, + bool AlwaysInline, MachinePointerInfo DstPtrInfo) const override; }; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h index b553c8150652..780694980523 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h @@ -48,6 +48,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo { bool HasMutableGlobals = false; bool HasTailCall = false; bool HasReferenceTypes = false; + bool HasExtendedConst = false; /// What processor and OS we're targeting. Triple TargetTriple; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 482837178f3d..76f036358ae8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -25,11 +25,12 @@ #include "llvm/CodeGen/RegAllocRegistry.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/Function.h" +#include "llvm/InitializePasses.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Transforms/Scalar.h" -#include "llvm/Transforms/Scalar/LowerAtomic.h" +#include "llvm/Transforms/Scalar/LowerAtomicPass.h" #include "llvm/Transforms/Utils.h" using namespace llvm; @@ -56,13 +57,12 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() { auto &PR = *PassRegistry::getPassRegistry(); initializeWebAssemblyAddMissingPrototypesPass(PR); initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR); - initializeLowerGlobalDtorsPass(PR); + initializeLowerGlobalDtorsLegacyPassPass(PR); initializeFixFunctionBitcastsPass(PR); initializeOptimizeReturnedPass(PR); initializeWebAssemblyArgumentMovePass(PR); initializeWebAssemblySetP2AlignOperandsPass(PR); initializeWebAssemblyReplacePhysRegsPass(PR); - initializeWebAssemblyPrepareForLiveIntervalsPass(PR); initializeWebAssemblyOptimizeLiveIntervalsPass(PR); initializeWebAssemblyMemIntrinsicResultsPass(PR); initializeWebAssemblyRegStackifyPass(PR); @@ -87,7 +87,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() { static Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM, const Triple &TT) { - if (!RM.hasValue()) { + if (!RM) { // Default to static relocation model. This should always be more optimial // than PIC since the static linker can determine all global addresses and // assume direct function calls. @@ -203,11 +203,12 @@ public: bool StrippedAtomics = false; bool StrippedTLS = false; - if (!Features[WebAssembly::FeatureAtomics]) + if (!Features[WebAssembly::FeatureAtomics]) { StrippedAtomics = stripAtomics(M); - - if (!Features[WebAssembly::FeatureBulkMemory]) StrippedTLS = stripThreadLocals(M); + } else if (!Features[WebAssembly::FeatureBulkMemory]) { + StrippedTLS |= stripThreadLocals(M); + } if (StrippedAtomics && !StrippedTLS) stripThreadLocals(M); @@ -320,6 +321,7 @@ public: FunctionPass *createTargetRegisterAllocator(bool) override; void addIRPasses() override; + void addISelPrepare() override; bool addInstSelector() override; void addPostRegAlloc() override; bool addGCPasses() override { return false; } @@ -335,7 +337,7 @@ public: } // end anonymous namespace TargetTransformInfo -WebAssemblyTargetMachine::getTargetTransformInfo(const Function &F) { +WebAssemblyTargetMachine::getTargetTransformInfo(const Function &F) const { return TargetTransformInfo(WebAssemblyTTIImpl(this, F)); } @@ -407,17 +409,11 @@ static void basicCheckForEHAndSjLj(TargetMachine *TM) { //===----------------------------------------------------------------------===// void WebAssemblyPassConfig::addIRPasses() { - // Lower atomics and TLS if necessary - addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine())); - - // This is a no-op if atomics are not used in the module - addPass(createAtomicExpandPass()); - // Add signatures to prototype-less function declarations addPass(createWebAssemblyAddMissingPrototypes()); // Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls. - addPass(createWebAssemblyLowerGlobalDtors()); + addPass(createLowerGlobalDtorsLegacyPass()); // Fix function bitcasts, as WebAssembly requires caller and callee signatures // to match. @@ -455,6 +451,16 @@ void WebAssemblyPassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); } +void WebAssemblyPassConfig::addISelPrepare() { + // Lower atomics and TLS if necessary + addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine())); + + // This is a no-op if atomics are not used in the module + addPass(createAtomicExpandPass()); + + TargetPassConfig::addISelPrepare(); +} + bool WebAssemblyPassConfig::addInstSelector() { (void)TargetPassConfig::addInstSelector(); addPass( @@ -517,9 +523,6 @@ void WebAssemblyPassConfig::addPreEmitPass() { // Preparations and optimizations related to register stackification. if (getOptLevel() != CodeGenOpt::None) { - // LiveIntervals isn't commonly run this late. Re-establish preconditions. - addPass(createWebAssemblyPrepareForLiveIntervals()); - // Depend on LiveIntervals and perform some optimizations on it. addPass(createWebAssemblyOptimizeLiveIntervals()); @@ -588,8 +591,7 @@ yaml::MachineFunctionInfo *WebAssemblyTargetMachine::convertFuncInfoToYAML( bool WebAssemblyTargetMachine::parseMachineFunctionInfo( const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS, SMDiagnostic &Error, SMRange &SourceRange) const { - const auto &YamlMFI = - reinterpret_cast<const yaml::WebAssemblyFunctionInfo &>(MFI); + const auto &YamlMFI = static_cast<const yaml::WebAssemblyFunctionInfo &>(MFI); MachineFunction &MF = PFS.MF; MF.getInfo<WebAssemblyFunctionInfo>()->initializeBaseYamlFields(YamlMFI); return false; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h index 29e968bfe8eb..5d5378f76567 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h @@ -46,7 +46,7 @@ public: return TLOF.get(); } - TargetTransformInfo getTargetTransformInfo(const Function &F) override; + TargetTransformInfo getTargetTransformInfo(const Function &F) const override; bool usesPhysRegsForValues() const override { return false; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp index f1ebcbc6fc51..62f7155e794a 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp @@ -139,3 +139,7 @@ void WebAssemblyTTIImpl::getUnrollingPreferences( // becomes "fall through" to default value of 2. UP.BEInsns = 2; } + +bool WebAssemblyTTIImpl::supportsTailCalls() const { + return getST()->hasTailCall(); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h index 50036f7f7e98..fde58a9587b6 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h @@ -74,6 +74,8 @@ public: bool areInlineCompatible(const Function *Caller, const Function *Callee) const; + + bool supportsTailCalls() const; }; } // end namespace llvm |
