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