summaryrefslogtreecommitdiff
path: root/lib/Target/WebAssembly
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
commitd8e91e46262bc44006913e6796843909f1ac7bcd (patch)
tree7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/Target/WebAssembly
parentb7eb8e35e481a74962664b63dfb09483b200209a (diff)
Notes
Diffstat (limited to 'lib/Target/WebAssembly')
-rw-r--r--lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp778
-rw-r--r--lib/Target/WebAssembly/CMakeLists.txt4
-rw-r--r--lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp104
-rw-r--r--lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp243
-rw-r--r--lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h14
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp14
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h8
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp78
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp31
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h95
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp143
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h64
-rw-r--r--lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp21
-rw-r--r--lib/Target/WebAssembly/README.txt18
-rw-r--r--lib/Target/WebAssembly/WebAssembly.h6
-rw-r--r--lib/Target/WebAssembly/WebAssembly.td12
-rw-r--r--lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp87
-rw-r--r--lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp112
-rw-r--r--lib/Target/WebAssembly/WebAssemblyAsmPrinter.h9
-rw-r--r--lib/Target/WebAssembly/WebAssemblyCFGSort.cpp238
-rw-r--r--lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp693
-rw-r--r--lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp43
-rw-r--r--lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp46
-rw-r--r--lib/Target/WebAssembly/WebAssemblyDebugValueManager.h38
-rw-r--r--lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp87
-rw-r--r--lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp7
-rw-r--r--lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp116
-rw-r--r--lib/Target/WebAssembly/WebAssemblyFastISel.cpp218
-rw-r--r--lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp159
-rw-r--r--lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp428
-rw-r--r--lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp94
-rw-r--r--lib/Target/WebAssembly/WebAssemblyFrameLowering.h29
-rw-r--r--lib/Target/WebAssembly/WebAssemblyISD.def5
-rw-r--r--lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp4
-rw-r--r--lib/Target/WebAssembly/WebAssemblyISelLowering.cpp611
-rw-r--r--lib/Target/WebAssembly/WebAssemblyISelLowering.h25
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrAtomics.td448
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrCall.td71
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrControl.td104
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrConv.td116
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrExceptRef.td4
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrFloat.td54
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrFormats.td131
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp17
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrInfo.td119
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrInteger.td46
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrMemory.td67
-rw-r--r--lib/Target/WebAssembly/WebAssemblyInstrSIMD.td794
-rw-r--r--lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp79
-rw-r--r--lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp123
-rw-r--r--lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp285
-rw-r--r--lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp82
-rw-r--r--lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp164
-rw-r--r--lib/Target/WebAssembly/WebAssemblyMCInstLower.h4
-rw-r--r--lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp30
-rw-r--r--lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h26
-rw-r--r--lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp (renamed from lib/Target/WebAssembly/WebAssemblyStoreResults.cpp)71
-rw-r--r--lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp10
-rw-r--r--lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp4
-rw-r--r--lib/Target/WebAssembly/WebAssemblyPeephole.cpp10
-rw-r--r--lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp5
-rw-r--r--lib/Target/WebAssembly/WebAssemblyRegColoring.cpp19
-rw-r--r--lib/Target/WebAssembly/WebAssemblyRegStackify.cpp157
-rw-r--r--lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp2
-rw-r--r--lib/Target/WebAssembly/WebAssemblyRegisterInfo.td4
-rw-r--r--lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp6
-rw-r--r--lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp23
-rw-r--r--lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h15
-rw-r--r--lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp29
-rw-r--r--lib/Target/WebAssembly/WebAssemblySubtarget.cpp7
-rw-r--r--lib/Target/WebAssembly/WebAssemblySubtarget.h20
-rw-r--r--lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp43
-rw-r--r--lib/Target/WebAssembly/WebAssemblyUtilities.cpp119
73 files changed, 5608 insertions, 2382 deletions
diff --git a/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 2d92b93ca704..0a5908f43790 100644
--- a/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -18,13 +18,14 @@
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
#include "WebAssembly.h"
#include "llvm/MC/MCContext.h"
-#include "llvm/MC/MCParser/MCTargetAsmParser.h"
-#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
-#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSymbolWasm.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/TargetRegistry.h"
@@ -34,27 +35,10 @@ using namespace llvm;
namespace {
-// We store register types as SimpleValueType to retain SIMD layout
-// information, but must also be able to supply them as the (unnamed)
-// register enum from WebAssemblyRegisterInfo.td/.inc.
-static unsigned MVTToWasmReg(MVT::SimpleValueType Type) {
- switch(Type) {
- case MVT::i32: return WebAssembly::I32_0;
- case MVT::i64: return WebAssembly::I64_0;
- case MVT::f32: return WebAssembly::F32_0;
- case MVT::f64: return WebAssembly::F64_0;
- case MVT::v16i8: return WebAssembly::V128_0;
- case MVT::v8i16: return WebAssembly::V128_0;
- case MVT::v4i32: return WebAssembly::V128_0;
- case MVT::v4f32: return WebAssembly::V128_0;
- default: return MVT::INVALID_SIMPLE_VALUE_TYPE;
- }
-}
-
/// WebAssemblyOperand - Instances of this class represent the operands in a
/// parsed WASM machine instruction.
struct WebAssemblyOperand : public MCParsedAsmOperand {
- enum KindTy { Token, Local, Stack, Integer, Float, Symbol } Kind;
+ enum KindTy { Token, Integer, Float, Symbol, BrList } Kind;
SMLoc StartLoc, EndLoc;
@@ -62,19 +46,6 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
StringRef Tok;
};
- struct RegOp {
- // This is a (virtual) local or stack register represented as 0..
- unsigned RegNo;
- // In most targets, the register number also encodes the type, but for
- // wasm we have to track that seperately since we have an unbounded
- // number of registers.
- // This has the unfortunate side effect that we supply a different value
- // to the table-gen matcher at different times in the process (when it
- // calls getReg() or addRegOperands().
- // TODO: While this works, it feels brittle. and would be nice to clean up.
- MVT::SimpleValueType Type;
- };
-
struct IntOp {
int64_t Val;
};
@@ -87,37 +58,45 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
const MCExpr *Exp;
};
+ struct BrLOp {
+ std::vector<unsigned> List;
+ };
+
union {
struct TokOp Tok;
- struct RegOp Reg;
struct IntOp Int;
struct FltOp Flt;
struct SymOp Sym;
+ struct BrLOp BrL;
};
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, TokOp T)
- : Kind(K), StartLoc(Start), EndLoc(End), Tok(T) {}
- WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, RegOp R)
- : Kind(K), StartLoc(Start), EndLoc(End), Reg(R) {}
+ : Kind(K), StartLoc(Start), EndLoc(End), Tok(T) {}
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, IntOp I)
- : Kind(K), StartLoc(Start), EndLoc(End), Int(I) {}
+ : Kind(K), StartLoc(Start), EndLoc(End), Int(I) {}
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, FltOp F)
- : Kind(K), StartLoc(Start), EndLoc(End), Flt(F) {}
+ : Kind(K), StartLoc(Start), EndLoc(End), Flt(F) {}
WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, SymOp S)
- : Kind(K), StartLoc(Start), EndLoc(End), Sym(S) {}
+ : Kind(K), StartLoc(Start), EndLoc(End), Sym(S) {}
+ WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End)
+ : Kind(K), StartLoc(Start), EndLoc(End), BrL() {}
+
+ ~WebAssemblyOperand() {
+ if (isBrList())
+ BrL.~BrLOp();
+ }
bool isToken() const override { return Kind == Token; }
- bool isImm() const override { return Kind == Integer ||
- Kind == Float ||
- Kind == Symbol; }
- bool isReg() const override { return Kind == Local || Kind == Stack; }
+ bool isImm() const override {
+ return Kind == Integer || Kind == Float || Kind == Symbol;
+ }
bool isMem() const override { return false; }
+ bool isReg() const override { return false; }
+ bool isBrList() const { return Kind == BrList; }
unsigned getReg() const override {
- assert(isReg());
- // This is called from the tablegen matcher (MatchInstructionImpl)
- // where it expects to match the type of register, see RegOp above.
- return MVTToWasmReg(Reg.Type);
+ llvm_unreachable("Assembly inspects a register operand");
+ return 0;
}
StringRef getToken() const {
@@ -128,19 +107,9 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
SMLoc getStartLoc() const override { return StartLoc; }
SMLoc getEndLoc() const override { return EndLoc; }
- void addRegOperands(MCInst &Inst, unsigned N) const {
- assert(N == 1 && "Invalid number of operands!");
- assert(isReg() && "Not a register operand!");
- // This is called from the tablegen matcher (MatchInstructionImpl)
- // where it expects to output the actual register index, see RegOp above.
- unsigned R = Reg.RegNo;
- if (Kind == Stack) {
- // A stack register is represented as a large negative number.
- // See WebAssemblyRegNumbering::runOnMachineFunction and
- // getWARegStackId for why this | is needed.
- R |= INT32_MIN;
- }
- Inst.addOperand(MCOperand::createReg(R));
+ void addRegOperands(MCInst &, unsigned) const {
+ // Required by the assembly matcher.
+ llvm_unreachable("Assembly matcher creates register operands");
}
void addImmOperands(MCInst &Inst, unsigned N) const {
@@ -155,17 +124,17 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
llvm_unreachable("Should be immediate or symbol!");
}
+ void addBrListOperands(MCInst &Inst, unsigned N) const {
+ assert(N == 1 && isBrList() && "Invalid BrList!");
+ for (auto Br : BrL.List)
+ Inst.addOperand(MCOperand::createImm(Br));
+ }
+
void print(raw_ostream &OS) const override {
switch (Kind) {
case Token:
OS << "Tok:" << Tok.Tok;
break;
- case Local:
- OS << "Loc:" << Reg.RegNo << ":" << static_cast<int>(Reg.Type);
- break;
- case Stack:
- OS << "Stk:" << Reg.RegNo << ":" << static_cast<int>(Reg.Type);
- break;
case Integer:
OS << "Int:" << Int.Val;
break;
@@ -175,6 +144,9 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
case Symbol:
OS << "Sym:" << Sym.Exp;
break;
+ case BrList:
+ OS << "BrList:" << BrL.List.size();
+ break;
}
}
};
@@ -182,352 +154,526 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
class WebAssemblyAsmParser final : public MCTargetAsmParser {
MCAsmParser &Parser;
MCAsmLexer &Lexer;
- // These are for the current function being parsed:
- // These are vectors since register assignments are so far non-sparse.
- // Replace by map if necessary.
- std::vector<MVT::SimpleValueType> LocalTypes;
- std::vector<MVT::SimpleValueType> StackTypes;
- MCSymbol *LastLabel;
+
+ // Much like WebAssemblyAsmPrinter in the backend, we have to own these.
+ std::vector<std::unique_ptr<wasm::WasmSignature>> Signatures;
+
+ // Order of labels, directives and instructions in a .s file have no
+ // syntactical enforcement. This class is a callback from the actual parser,
+ // and yet we have to be feeding data to the streamer in a very particular
+ // order to ensure a correct binary encoding that matches the regular backend
+ // (the streamer does not enforce this). This "state machine" enum helps
+ // guarantee that correct order.
+ enum ParserState {
+ FileStart,
+ Label,
+ FunctionStart,
+ FunctionLocals,
+ Instructions,
+ } CurrentState = FileStart;
+
+ // For ensuring blocks are properly nested.
+ enum NestingType {
+ Function,
+ Block,
+ Loop,
+ Try,
+ If,
+ Else,
+ Undefined,
+ };
+ std::vector<NestingType> NestingStack;
+
+ // We track this to see if a .functype following a label is the same,
+ // as this is how we recognize the start of a function.
+ MCSymbol *LastLabel = nullptr;
public:
- WebAssemblyAsmParser(const MCSubtargetInfo &sti, MCAsmParser &Parser,
- const MCInstrInfo &mii, const MCTargetOptions &Options)
- : MCTargetAsmParser(Options, sti, mii), Parser(Parser),
- Lexer(Parser.getLexer()), LastLabel(nullptr) {
+ WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
+ const MCInstrInfo &MII, const MCTargetOptions &Options)
+ : MCTargetAsmParser(Options, STI, MII), Parser(Parser),
+ Lexer(Parser.getLexer()) {
+ setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
}
#define GET_ASSEMBLER_HEADER
#include "WebAssemblyGenAsmMatcher.inc"
// TODO: This is required to be implemented, but appears unused.
- bool ParseRegister(unsigned &/*RegNo*/, SMLoc &/*StartLoc*/,
- SMLoc &/*EndLoc*/) override {
+ bool ParseRegister(unsigned & /*RegNo*/, SMLoc & /*StartLoc*/,
+ SMLoc & /*EndLoc*/) override {
llvm_unreachable("ParseRegister is not implemented.");
}
- bool Error(const StringRef &msg, const AsmToken &tok) {
- return Parser.Error(tok.getLoc(), msg + tok.getString());
+ bool error(const Twine &Msg, const AsmToken &Tok) {
+ return Parser.Error(Tok.getLoc(), Msg + Tok.getString());
+ }
+
+ bool error(const Twine &Msg) {
+ return Parser.Error(Lexer.getTok().getLoc(), Msg);
+ }
+
+ void addSignature(std::unique_ptr<wasm::WasmSignature> &&Sig) {
+ Signatures.push_back(std::move(Sig));
+ }
+
+ std::pair<StringRef, StringRef> nestingString(NestingType NT) {
+ switch (NT) {
+ case Function:
+ return {"function", "end_function"};
+ case Block:
+ return {"block", "end_block"};
+ case Loop:
+ return {"loop", "end_loop"};
+ case Try:
+ return {"try", "end_try"};
+ case If:
+ return {"if", "end_if"};
+ case Else:
+ return {"else", "end_if"};
+ default:
+ llvm_unreachable("unknown NestingType");
+ }
+ }
+
+ void push(NestingType NT) { NestingStack.push_back(NT); }
+
+ bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) {
+ if (NestingStack.empty())
+ return error(Twine("End of block construct with no start: ") + Ins);
+ auto Top = NestingStack.back();
+ if (Top != NT1 && Top != NT2)
+ return error(Twine("Block construct type mismatch, expected: ") +
+ nestingString(Top).second + ", instead got: " + Ins);
+ NestingStack.pop_back();
+ return false;
+ }
+
+ bool ensureEmptyNestingStack() {
+ auto err = !NestingStack.empty();
+ while (!NestingStack.empty()) {
+ error(Twine("Unmatched block construct(s) at function end: ") +
+ nestingString(NestingStack.back()).first);
+ NestingStack.pop_back();
+ }
+ return err;
}
- bool IsNext(AsmToken::TokenKind Kind) {
- auto ok = Lexer.is(Kind);
- if (ok) Parser.Lex();
- return ok;
+ bool isNext(AsmToken::TokenKind Kind) {
+ auto Ok = Lexer.is(Kind);
+ if (Ok)
+ Parser.Lex();
+ return Ok;
}
- bool Expect(AsmToken::TokenKind Kind, const char *KindName) {
- if (!IsNext(Kind))
- return Error(std::string("Expected ") + KindName + ", instead got: ",
+ bool expect(AsmToken::TokenKind Kind, const char *KindName) {
+ if (!isNext(Kind))
+ return error(std::string("Expected ") + KindName + ", instead got: ",
Lexer.getTok());
return false;
}
- MVT::SimpleValueType ParseRegType(const StringRef &RegType) {
- // Derive type from .param .local decls, or the instruction itself.
- return StringSwitch<MVT::SimpleValueType>(RegType)
- .Case("i32", MVT::i32)
- .Case("i64", MVT::i64)
- .Case("f32", MVT::f32)
- .Case("f64", MVT::f64)
- .Case("i8x16", MVT::v16i8)
- .Case("i16x8", MVT::v8i16)
- .Case("i32x4", MVT::v4i32)
- .Case("f32x4", MVT::v4f32)
- .Default(MVT::INVALID_SIMPLE_VALUE_TYPE);
+ StringRef expectIdent() {
+ if (!Lexer.is(AsmToken::Identifier)) {
+ error("Expected identifier, got: ", Lexer.getTok());
+ return StringRef();
+ }
+ auto Name = Lexer.getTok().getString();
+ Parser.Lex();
+ return Name;
}
- MVT::SimpleValueType &GetType(
- std::vector<MVT::SimpleValueType> &Types, size_t i) {
- Types.resize(std::max(i + 1, Types.size()), MVT::INVALID_SIMPLE_VALUE_TYPE);
- return Types[i];
+ Optional<wasm::ValType> parseType(const 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;
+ return Optional<wasm::ValType>();
}
- bool ParseReg(OperandVector &Operands, StringRef TypePrefix) {
- if (Lexer.is(AsmToken::Integer)) {
- auto &Local = Lexer.getTok();
- // This is a reference to a local, turn it into a virtual register.
- auto LocalNo = static_cast<unsigned>(Local.getIntVal());
- Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Local, Local.getLoc(),
- Local.getEndLoc(),
- WebAssemblyOperand::RegOp{LocalNo,
- GetType(LocalTypes, LocalNo)}));
- Parser.Lex();
- } else if (Lexer.is(AsmToken::Identifier)) {
- auto &StackRegTok = Lexer.getTok();
- // These are push/pop/drop pseudo stack registers, which we turn
- // into virtual registers also. The stackify pass will later turn them
- // back into implicit stack references if possible.
- auto StackReg = StackRegTok.getString();
- auto StackOp = StackReg.take_while([](char c) { return isalpha(c); });
- auto Reg = StackReg.drop_front(StackOp.size());
- unsigned long long ParsedRegNo = 0;
- if (!Reg.empty() && getAsUnsignedInteger(Reg, 10, ParsedRegNo))
- return Error("Cannot parse stack register index: ", StackRegTok);
- unsigned RegNo = static_cast<unsigned>(ParsedRegNo);
- if (StackOp == "push") {
- // This defines a result, record register type.
- auto RegType = ParseRegType(TypePrefix);
- GetType(StackTypes, RegNo) = RegType;
- Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Stack,
- StackRegTok.getLoc(),
- StackRegTok.getEndLoc(),
- WebAssemblyOperand::RegOp{RegNo, RegType}));
- } else if (StackOp == "pop") {
- // This uses a previously defined stack value.
- auto RegType = GetType(StackTypes, RegNo);
- Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Stack,
- StackRegTok.getLoc(),
- StackRegTok.getEndLoc(),
- WebAssemblyOperand::RegOp{RegNo, RegType}));
- } else if (StackOp == "drop") {
- // This operand will be dropped, since it is part of an instruction
- // whose result is void.
- } else {
- return Error("Unknown stack register prefix: ", StackRegTok);
- }
+ WebAssembly::ExprType parseBlockType(StringRef ID) {
+ return StringSwitch<WebAssembly::ExprType>(ID)
+ .Case("i32", WebAssembly::ExprType::I32)
+ .Case("i64", WebAssembly::ExprType::I64)
+ .Case("f32", WebAssembly::ExprType::F32)
+ .Case("f64", WebAssembly::ExprType::F64)
+ .Case("v128", WebAssembly::ExprType::V128)
+ .Case("except_ref", WebAssembly::ExprType::ExceptRef)
+ .Case("void", WebAssembly::ExprType::Void)
+ .Default(WebAssembly::ExprType::Invalid);
+ }
+
+ bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
+ while (Lexer.is(AsmToken::Identifier)) {
+ auto Type = parseType(Lexer.getTok().getString());
+ if (!Type)
+ return true;
+ Types.push_back(Type.getValue());
Parser.Lex();
- } else {
- return Error(
- "Expected identifier/integer following $, instead got: ",
- Lexer.getTok());
+ if (!isNext(AsmToken::Comma))
+ break;
}
- IsNext(AsmToken::Equal);
return false;
}
- void ParseSingleInteger(bool IsNegative, OperandVector &Operands) {
+ void parseSingleInteger(bool IsNegative, OperandVector &Operands) {
auto &Int = Lexer.getTok();
int64_t Val = Int.getIntVal();
- if (IsNegative) Val = -Val;
+ if (IsNegative)
+ Val = -Val;
Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Integer, Int.getLoc(),
- Int.getEndLoc(), WebAssemblyOperand::IntOp{Val}));
+ WebAssemblyOperand::Integer, Int.getLoc(), Int.getEndLoc(),
+ WebAssemblyOperand::IntOp{Val}));
Parser.Lex();
}
- bool ParseOperandStartingWithInteger(bool IsNegative,
- OperandVector &Operands,
- StringRef InstType) {
- ParseSingleInteger(IsNegative, Operands);
- if (Lexer.is(AsmToken::LParen)) {
- // Parse load/store operands of the form: offset($reg)align
- auto &LParen = Lexer.getTok();
- Operands.push_back(
- make_unique<WebAssemblyOperand>(WebAssemblyOperand::Token,
- LParen.getLoc(),
- LParen.getEndLoc(),
- WebAssemblyOperand::TokOp{
- LParen.getString()}));
- Parser.Lex();
- if (Expect(AsmToken::Dollar, "register")) return true;
- if (ParseReg(Operands, InstType)) return true;
- auto &RParen = Lexer.getTok();
- Operands.push_back(
- make_unique<WebAssemblyOperand>(WebAssemblyOperand::Token,
- RParen.getLoc(),
- RParen.getEndLoc(),
- WebAssemblyOperand::TokOp{
- RParen.getString()}));
- if (Expect(AsmToken::RParen, ")")) return true;
- if (Lexer.is(AsmToken::Integer)) {
- ParseSingleInteger(false, Operands);
+ bool parseOperandStartingWithInteger(bool IsNegative, OperandVector &Operands,
+ StringRef InstName) {
+ parseSingleInteger(IsNegative, Operands);
+ // FIXME: there is probably a cleaner way to do this.
+ auto IsLoadStore = InstName.startswith("load") ||
+ InstName.startswith("store") ||
+ InstName.startswith("atomic_load") ||
+ InstName.startswith("atomic_store");
+ if (IsLoadStore) {
+ // Parse load/store operands of the form: offset align
+ auto &Offset = Lexer.getTok();
+ if (Offset.is(AsmToken::Integer)) {
+ parseSingleInteger(false, Operands);
} else {
// Alignment not specified.
// FIXME: correctly derive a default from the instruction.
+ // We can't just call WebAssembly::GetDefaultP2Align since we don't have
+ // an opcode until after the assembly matcher.
Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Integer, RParen.getLoc(),
- RParen.getEndLoc(), WebAssemblyOperand::IntOp{0}));
+ WebAssemblyOperand::Integer, Offset.getLoc(), Offset.getEndLoc(),
+ WebAssemblyOperand::IntOp{0}));
}
}
return false;
}
- bool ParseInstruction(ParseInstructionInfo &/*Info*/, StringRef Name,
+ void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
+ WebAssembly::ExprType BT) {
+ Operands.push_back(make_unique<WebAssemblyOperand>(
+ WebAssemblyOperand::Integer, NameLoc, NameLoc,
+ WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
+ }
+
+ bool ParseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name,
SMLoc NameLoc, OperandVector &Operands) override {
- Operands.push_back(
- make_unique<WebAssemblyOperand>(WebAssemblyOperand::Token, NameLoc,
- SMLoc::getFromPointer(
- NameLoc.getPointer() + Name.size()),
- WebAssemblyOperand::TokOp{
- StringRef(NameLoc.getPointer(),
- Name.size())}));
+ // Note: Name does NOT point into the sourcecode, but to a local, so
+ // use NameLoc instead.
+ Name = StringRef(NameLoc.getPointer(), Name.size());
+
+ // WebAssembly has instructions with / in them, which AsmLexer parses
+ // as seperate tokens, so if we find such tokens immediately adjacent (no
+ // whitespace), expand the name to include them:
+ for (;;) {
+ auto &Sep = Lexer.getTok();
+ if (Sep.getLoc().getPointer() != Name.end() ||
+ Sep.getKind() != AsmToken::Slash)
+ break;
+ // Extend name with /
+ Name = StringRef(Name.begin(), Name.size() + Sep.getString().size());
+ Parser.Lex();
+ // We must now find another identifier, or error.
+ auto &Id = Lexer.getTok();
+ if (Id.getKind() != AsmToken::Identifier ||
+ Id.getLoc().getPointer() != Name.end())
+ return error("Incomplete instruction name: ", Id);
+ Name = StringRef(Name.begin(), Name.size() + Id.getString().size());
+ Parser.Lex();
+ }
+
+ // Now construct the name as first operand.
+ Operands.push_back(make_unique<WebAssemblyOperand>(
+ WebAssemblyOperand::Token, NameLoc, SMLoc::getFromPointer(Name.end()),
+ WebAssemblyOperand::TokOp{Name}));
auto NamePair = Name.split('.');
// If no '.', there is no type prefix.
- if (NamePair.second.empty()) std::swap(NamePair.first, NamePair.second);
+ auto BaseName = NamePair.second.empty() ? NamePair.first : NamePair.second;
+
+ // If this instruction is part of a control flow structure, ensure
+ // proper nesting.
+ bool ExpectBlockType = false;
+ if (BaseName == "block") {
+ push(Block);
+ ExpectBlockType = true;
+ } else if (BaseName == "loop") {
+ push(Loop);
+ ExpectBlockType = true;
+ } else if (BaseName == "try") {
+ push(Try);
+ ExpectBlockType = true;
+ } else if (BaseName == "if") {
+ push(If);
+ ExpectBlockType = true;
+ } else if (BaseName == "else") {
+ if (pop(BaseName, If))
+ return true;
+ push(Else);
+ } else if (BaseName == "catch") {
+ if (pop(BaseName, Try))
+ return true;
+ push(Try);
+ } else if (BaseName == "catch_all") {
+ if (pop(BaseName, Try))
+ return true;
+ push(Try);
+ } else if (BaseName == "end_if") {
+ if (pop(BaseName, If, Else))
+ return true;
+ } else if (BaseName == "end_try") {
+ if (pop(BaseName, Try))
+ return true;
+ } else if (BaseName == "end_loop") {
+ if (pop(BaseName, Loop))
+ return true;
+ } else if (BaseName == "end_block") {
+ if (pop(BaseName, Block))
+ return true;
+ } else if (BaseName == "end_function") {
+ if (pop(BaseName, Function) || ensureEmptyNestingStack())
+ return true;
+ }
+
while (Lexer.isNot(AsmToken::EndOfStatement)) {
auto &Tok = Lexer.getTok();
switch (Tok.getKind()) {
- case AsmToken::Dollar: {
- Parser.Lex();
- if (ParseReg(Operands, NamePair.first)) return true;
- break;
- }
case AsmToken::Identifier: {
auto &Id = Lexer.getTok();
- const MCExpr *Val;
- SMLoc End;
- if (Parser.parsePrimaryExpr(Val, End))
- return Error("Cannot parse symbol: ", Lexer.getTok());
- Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Symbol, Id.getLoc(),
- Id.getEndLoc(), WebAssemblyOperand::SymOp{Val}));
+ if (ExpectBlockType) {
+ // Assume this identifier is a block_type.
+ auto BT = parseBlockType(Id.getString());
+ if (BT == WebAssembly::ExprType::Invalid)
+ return error("Unknown block type: ", Id);
+ addBlockTypeOperand(Operands, NameLoc, BT);
+ Parser.Lex();
+ } else {
+ // Assume this identifier is a label.
+ const MCExpr *Val;
+ SMLoc End;
+ if (Parser.parsePrimaryExpr(Val, End))
+ return error("Cannot parse symbol: ", Lexer.getTok());
+ Operands.push_back(make_unique<WebAssemblyOperand>(
+ WebAssemblyOperand::Symbol, Id.getLoc(), Id.getEndLoc(),
+ WebAssemblyOperand::SymOp{Val}));
+ }
break;
}
case AsmToken::Minus:
Parser.Lex();
if (Lexer.isNot(AsmToken::Integer))
- return Error("Expected integer instead got: ", Lexer.getTok());
- if (ParseOperandStartingWithInteger(true, Operands, NamePair.first))
+ return error("Expected integer instead got: ", Lexer.getTok());
+ if (parseOperandStartingWithInteger(true, Operands, BaseName))
return true;
break;
case AsmToken::Integer:
- if (ParseOperandStartingWithInteger(false, Operands, NamePair.first))
+ if (parseOperandStartingWithInteger(false, Operands, BaseName))
return true;
break;
case AsmToken::Real: {
double Val;
if (Tok.getString().getAsDouble(Val, false))
- return Error("Cannot parse real: ", Tok);
+ return error("Cannot parse real: ", Tok);
Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Float, Tok.getLoc(),
- Tok.getEndLoc(), WebAssemblyOperand::FltOp{Val}));
+ WebAssemblyOperand::Float, Tok.getLoc(), Tok.getEndLoc(),
+ WebAssemblyOperand::FltOp{Val}));
+ Parser.Lex();
+ break;
+ }
+ case AsmToken::LCurly: {
Parser.Lex();
+ auto Op = make_unique<WebAssemblyOperand>(
+ WebAssemblyOperand::BrList, Tok.getLoc(), Tok.getEndLoc());
+ if (!Lexer.is(AsmToken::RCurly))
+ for (;;) {
+ Op->BrL.List.push_back(Lexer.getTok().getIntVal());
+ expect(AsmToken::Integer, "integer");
+ if (!isNext(AsmToken::Comma))
+ break;
+ }
+ expect(AsmToken::RCurly, "}");
+ Operands.push_back(std::move(Op));
break;
}
default:
- return Error("Unexpected token in operand: ", Tok);
+ return error("Unexpected token in operand: ", Tok);
}
if (Lexer.isNot(AsmToken::EndOfStatement)) {
- if (Expect(AsmToken::Comma, ",")) return true;
- }
- }
- Parser.Lex();
- // Call instructions are vararg, but the tablegen matcher doesn't seem to
- // support that, so for now we strip these extra operands.
- // This is problematic if these arguments are not simple $pop stack
- // registers, since e.g. a local register would get lost, so we check for
- // this. This can be the case when using -disable-wasm-explicit-locals
- // which currently s2wasm requires.
- // TODO: Instead, we can move this code to MatchAndEmitInstruction below and
- // actually generate get_local instructions on the fly.
- // Or even better, improve the matcher to support vararg?
- auto IsIndirect = NamePair.second == "call_indirect";
- if (IsIndirect || NamePair.second == "call") {
- // Figure out number of fixed operands from the instruction.
- size_t CallOperands = 1; // The name token.
- if (!IsIndirect) CallOperands++; // The function index.
- if (!NamePair.first.empty()) CallOperands++; // The result register.
- if (Operands.size() > CallOperands) {
- // Ensure operands we drop are all $pop.
- for (size_t I = CallOperands; I < Operands.size(); I++) {
- auto Operand =
- reinterpret_cast<WebAssemblyOperand *>(Operands[I].get());
- if (Operand->Kind != WebAssemblyOperand::Stack)
- Parser.Error(NameLoc,
- "Call instruction has non-stack arguments, if this code was "
- "generated with -disable-wasm-explicit-locals please remove it");
- }
- // Drop unneeded operands.
- Operands.resize(CallOperands);
+ if (expect(AsmToken::Comma, ","))
+ return true;
}
}
- // Block instructions require a signature index, but these are missing in
- // assembly, so we add a dummy one explicitly (since we have no control
- // over signature tables here, we assume these will be regenerated when
- // the wasm module is generated).
- if (NamePair.second == "block" || NamePair.second == "loop") {
- Operands.push_back(make_unique<WebAssemblyOperand>(
- WebAssemblyOperand::Integer, NameLoc,
- NameLoc, WebAssemblyOperand::IntOp{-1}));
- }
- // These don't specify the type, which has to derived from the local index.
- if (NamePair.second == "get_local" || NamePair.second == "tee_local") {
- if (Operands.size() >= 3 && Operands[1]->isReg() &&
- Operands[2]->isImm()) {
- auto Op1 = reinterpret_cast<WebAssemblyOperand *>(Operands[1].get());
- auto Op2 = reinterpret_cast<WebAssemblyOperand *>(Operands[2].get());
- auto Type = GetType(LocalTypes, static_cast<size_t>(Op2->Int.Val));
- Op1->Reg.Type = Type;
- GetType(StackTypes, Op1->Reg.RegNo) = Type;
- }
+ if (ExpectBlockType && Operands.size() == 1) {
+ // Support blocks with no operands as default to void.
+ addBlockTypeOperand(Operands, NameLoc, WebAssembly::ExprType::Void);
}
+ Parser.Lex();
return false;
}
void onLabelParsed(MCSymbol *Symbol) override {
LastLabel = Symbol;
+ CurrentState = Label;
}
+ bool parseSignature(wasm::WasmSignature *Signature) {
+ if (expect(AsmToken::LParen, "("))
+ return true;
+ if (parseRegTypeList(Signature->Params))
+ return true;
+ if (expect(AsmToken::RParen, ")"))
+ return true;
+ if (expect(AsmToken::MinusGreater, "->"))
+ return true;
+ if (expect(AsmToken::LParen, "("))
+ return true;
+ if (parseRegTypeList(Signature->Returns))
+ return true;
+ if (expect(AsmToken::RParen, ")"))
+ return true;
+ return false;
+ }
+
+ // 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.
assert(DirectiveID.getKind() == AsmToken::Identifier);
auto &Out = getStreamer();
- auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>(
- *Out.getTargetStreamer());
- // TODO: we're just parsing the subset of directives we're interested in,
- // and ignoring ones we don't recognise. We should ideally verify
- // all directives here.
- if (DirectiveID.getString() == ".type") {
- // This could be the start of a function, check if followed by
- // "label,@function"
- if (!(IsNext(AsmToken::Identifier) &&
- IsNext(AsmToken::Comma) &&
- IsNext(AsmToken::At) &&
- Lexer.is(AsmToken::Identifier)))
- return Error("Expected label,@type declaration, got: ", Lexer.getTok());
- if (Lexer.getTok().getString() == "function") {
- // Track locals from start of function.
- LocalTypes.clear();
- StackTypes.clear();
- }
- Parser.Lex();
- //Out.EmitSymbolAttribute(??, MCSA_ELF_TypeFunction);
- } else if (DirectiveID.getString() == ".param" ||
- DirectiveID.getString() == ".local") {
- // Track the number of locals, needed for correct virtual register
- // assignment elsewhere.
- // Also output a directive to the streamer.
- std::vector<MVT> Params;
- std::vector<MVT> Locals;
- while (Lexer.is(AsmToken::Identifier)) {
- auto RegType = ParseRegType(Lexer.getTok().getString());
- if (RegType == MVT::INVALID_SIMPLE_VALUE_TYPE) return true;
- LocalTypes.push_back(RegType);
- if (DirectiveID.getString() == ".param") {
- Params.push_back(RegType);
- } else {
- Locals.push_back(RegType);
- }
- Parser.Lex();
- if (!IsNext(AsmToken::Comma)) break;
+ auto &TOut =
+ reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer());
+
+ // 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;
+ if (expect(AsmToken::Comma, ","))
+ return true;
+ auto TypeTok = Lexer.getTok();
+ auto TypeName = expectIdent();
+ if (TypeName.empty())
+ return true;
+ auto Type = parseType(TypeName);
+ if (!Type)
+ return error("Unknown type in .globaltype directive: ", TypeTok);
+ // Now set this symbol with the correct type.
+ auto WasmSym = cast<MCSymbolWasm>(
+ TOut.getStreamer().getContext().getOrCreateSymbol(SymName));
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
+ WasmSym->setGlobalType(
+ wasm::WasmGlobalType{uint8_t(Type.getValue()), true});
+ // And emit the directive again.
+ TOut.emitGlobalType(WasmSym);
+ return expect(AsmToken::EndOfStatement, "EOL");
+ }
+
+ if (DirectiveID.getString() == ".functype") {
+ // This code has to send things to the streamer similar to
+ // WebAssemblyAsmPrinter::EmitFunctionBodyStart.
+ // TODO: would be good to factor this into a common function, but the
+ // assembler and backend really don't share any common code, and this code
+ // parses the locals seperately.
+ auto SymName = expectIdent();
+ if (SymName.empty())
+ return true;
+ auto WasmSym = cast<MCSymbolWasm>(
+ TOut.getStreamer().getContext().getOrCreateSymbol(SymName));
+ if (CurrentState == Label && WasmSym == LastLabel) {
+ // This .functype indicates a start of a function.
+ if (ensureEmptyNestingStack())
+ return true;
+ CurrentState = FunctionStart;
+ push(Function);
}
- assert(LastLabel);
- TOut.emitParam(LastLabel, Params);
+ auto Signature = make_unique<wasm::WasmSignature>();
+ if (parseSignature(Signature.get()))
+ return true;
+ WasmSym->setSignature(Signature.get());
+ addSignature(std::move(Signature));
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ TOut.emitFunctionType(WasmSym);
+ // TODO: backend also calls TOut.emitIndIdx, but that is not implemented.
+ return expect(AsmToken::EndOfStatement, "EOL");
+ }
+
+ if (DirectiveID.getString() == ".eventtype") {
+ auto SymName = expectIdent();
+ if (SymName.empty())
+ return true;
+ auto WasmSym = cast<MCSymbolWasm>(
+ TOut.getStreamer().getContext().getOrCreateSymbol(SymName));
+ auto Signature = make_unique<wasm::WasmSignature>();
+ if (parseRegTypeList(Signature->Params))
+ return true;
+ WasmSym->setSignature(Signature.get());
+ addSignature(std::move(Signature));
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_EVENT);
+ TOut.emitEventType(WasmSym);
+ // TODO: backend also calls TOut.emitIndIdx, but that is not implemented.
+ return expect(AsmToken::EndOfStatement, "EOL");
+ }
+
+ if (DirectiveID.getString() == ".local") {
+ if (CurrentState != FunctionStart)
+ return error(".local directive should follow the start of a function",
+ Lexer.getTok());
+ SmallVector<wasm::ValType, 4> Locals;
+ if (parseRegTypeList(Locals))
+ return true;
TOut.emitLocal(Locals);
- } else {
- // For now, ignore anydirective we don't recognize:
- while (Lexer.isNot(AsmToken::EndOfStatement)) Parser.Lex();
+ CurrentState = FunctionLocals;
+ return expect(AsmToken::EndOfStatement, "EOL");
}
- return Expect(AsmToken::EndOfStatement, "EOL");
+
+ return true; // We didn't process this directive.
}
- bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &/*Opcode*/,
- OperandVector &Operands,
- MCStreamer &Out, uint64_t &ErrorInfo,
+ bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/,
+ OperandVector &Operands, MCStreamer &Out,
+ uint64_t &ErrorInfo,
bool MatchingInlineAsm) override {
MCInst Inst;
unsigned MatchResult =
MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm);
switch (MatchResult) {
case Match_Success: {
+ if (CurrentState == FunctionStart) {
+ // This is the first instruction in a function, but we haven't seen
+ // a .local directive yet. The streamer requires locals to be encoded
+ // as a prelude to the instructions, so emit an empty list of locals
+ // here.
+ auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>(
+ *Out.getTargetStreamer());
+ TOut.emitLocal(SmallVector<wasm::ValType, 0>());
+ }
+ CurrentState = Instructions;
Out.EmitInstruction(Inst, getSTI());
return false;
}
case Match_MissingFeature:
- return Parser.Error(IDLoc,
- "instruction requires a WASM feature not currently enabled");
+ return Parser.Error(
+ IDLoc, "instruction requires a WASM feature not currently enabled");
case Match_MnemonicFail:
return Parser.Error(IDLoc, "invalid instruction");
case Match_NearMisses:
@@ -547,6 +693,8 @@ public:
}
llvm_unreachable("Implement any new match types added!");
}
+
+ void onEndOfFile() override { ensureEmptyNestingStack(); }
};
} // end anonymous namespace
diff --git a/lib/Target/WebAssembly/CMakeLists.txt b/lib/Target/WebAssembly/CMakeLists.txt
index a928f110efe0..1f3b7d942d47 100644
--- a/lib/Target/WebAssembly/CMakeLists.txt
+++ b/lib/Target/WebAssembly/CMakeLists.txt
@@ -19,7 +19,9 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyCallIndirectFixup.cpp
WebAssemblyCFGStackify.cpp
WebAssemblyCFGSort.cpp
+ WebAssemblyDebugValueManager.cpp
WebAssemblyLateEHPrepare.cpp
+ WebAssemblyEHRestoreStackPointer.cpp
WebAssemblyExceptionInfo.cpp
WebAssemblyExplicitLocals.cpp
WebAssemblyFastISel.cpp
@@ -46,7 +48,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyRuntimeLibcallSignatures.cpp
WebAssemblySelectionDAGInfo.cpp
WebAssemblySetP2AlignOperands.cpp
- WebAssemblyStoreResults.cpp
+ WebAssemblyMemIntrinsicResults.cpp
WebAssemblySubtarget.cpp
WebAssemblyTargetMachine.cpp
WebAssemblyTargetObjectFile.cpp
diff --git a/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp
index 2f0960271e30..6acc9b20eed2 100644
--- a/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp
+++ b/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp
@@ -16,7 +16,6 @@
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
-#include "WebAssembly.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCFixedLenDisassembler.h"
@@ -37,6 +36,8 @@ using DecodeStatus = MCDisassembler::DecodeStatus;
#include "WebAssemblyGenDisassemblerTables.inc"
namespace {
+static constexpr int WebAssemblyInstructionTableSize = 256;
+
class WebAssemblyDisassembler final : public MCDisassembler {
std::unique_ptr<const MCInstrInfo> MCII;
@@ -75,31 +76,43 @@ static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) {
return V;
}
-static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,
- ArrayRef<uint8_t> Bytes, bool Signed) {
+static bool nextLEB(int64_t &Val, ArrayRef<uint8_t> Bytes, uint64_t &Size,
+ bool Signed = false) {
unsigned N = 0;
const char *Error = nullptr;
- auto Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N,
- Bytes.data() + Bytes.size(), &Error)
- : static_cast<int64_t>(
- decodeULEB128(Bytes.data() + Size, &N,
- Bytes.data() + Bytes.size(), &Error));
+ Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N,
+ Bytes.data() + Bytes.size(), &Error)
+ : static_cast<int64_t>(decodeULEB128(Bytes.data() + Size, &N,
+ Bytes.data() + Bytes.size(),
+ &Error));
if (Error)
return false;
Size += N;
+ return true;
+}
+
+static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,
+ ArrayRef<uint8_t> Bytes, bool Signed) {
+ int64_t Val;
+ if (!nextLEB(Val, Bytes, Size, Signed))
+ return false;
MI.addOperand(MCOperand::createImm(Val));
return true;
}
template <typename T>
-bool parseFPImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
+bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
if (Size + sizeof(T) > Bytes.size())
return false;
T Val;
memcpy(&Val, Bytes.data() + Size, sizeof(T));
support::endian::byte_swap<T, support::endianness::little>(Val);
Size += sizeof(T);
- MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
+ if (std::is_floating_point<T>::value) {
+ MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
+ } else {
+ MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));
+ }
return true;
}
@@ -108,7 +121,7 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
raw_ostream & /*OS*/, raw_ostream &CS) const {
CommentStream = &CS;
Size = 0;
- auto Opc = nextByte(Bytes, Size);
+ int Opc = nextByte(Bytes, Size);
if (Opc < 0)
return MCDisassembler::Fail;
const auto *WasmInst = &InstructionTable0[Opc];
@@ -124,10 +137,12 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
}
if (!WasmInst)
return MCDisassembler::Fail;
- Opc = nextByte(Bytes, Size);
- if (Opc < 0)
+ int64_t PrefixedOpc;
+ if (!nextLEB(PrefixedOpc, Bytes, Size))
return MCDisassembler::Fail;
- WasmInst += Opc;
+ if (PrefixedOpc < 0 || PrefixedOpc >= WebAssemblyInstructionTableSize)
+ return MCDisassembler::Fail;
+ WasmInst += PrefixedOpc;
}
if (WasmInst->ET == ET_Unused)
return MCDisassembler::Fail;
@@ -136,7 +151,8 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
MI.setOpcode(WasmInst->Opcode);
// Parse any operands.
for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) {
- switch (WasmInst->Operands[OPI]) {
+ auto OT = OperandTable[WasmInst->OperandStart + OPI];
+ switch (OT) {
// ULEB operands:
case WebAssembly::OPERAND_BASIC_BLOCK:
case WebAssembly::OPERAND_LOCAL:
@@ -152,32 +168,68 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
}
// SLEB operands:
case WebAssembly::OPERAND_I32IMM:
- case WebAssembly::OPERAND_I64IMM:
- case WebAssembly::OPERAND_SIGNATURE: {
+ case WebAssembly::OPERAND_I64IMM: {
if (!parseLEBImmediate(MI, Size, Bytes, true))
return MCDisassembler::Fail;
break;
}
+ // block_type operands (uint8_t).
+ case WebAssembly::OPERAND_SIGNATURE: {
+ if (!parseImmediate<uint8_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
// FP operands.
case WebAssembly::OPERAND_F32IMM: {
- if (!parseFPImmediate<float>(MI, Size, Bytes))
+ if (!parseImmediate<float>(MI, Size, Bytes))
return MCDisassembler::Fail;
break;
}
case WebAssembly::OPERAND_F64IMM: {
- if (!parseFPImmediate<double>(MI, Size, Bytes))
+ if (!parseImmediate<double>(MI, Size, Bytes))
return MCDisassembler::Fail;
break;
}
- case MCOI::OPERAND_REGISTER: {
- // These are NOT actually in the instruction stream, but MC is going to
- // expect operands to be present for them!
- // FIXME: can MC re-generate register assignments or do we have to
- // do this? Since this function decodes a single instruction, we don't
- // have the proper context for tracking an operand stack here.
- MI.addOperand(MCOperand::createReg(0));
+ // Vector lane operands (not LEB encoded).
+ case WebAssembly::OPERAND_VEC_I8IMM: {
+ if (!parseImmediate<uint8_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_VEC_I16IMM: {
+ if (!parseImmediate<uint16_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_VEC_I32IMM: {
+ if (!parseImmediate<uint32_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_VEC_I64IMM: {
+ if (!parseImmediate<uint64_t>(MI, Size, Bytes))
+ return MCDisassembler::Fail;
+ break;
+ }
+ case WebAssembly::OPERAND_BRLIST: {
+ int64_t TargetTableLen;
+ if (!nextLEB(TargetTableLen, Bytes, Size, false))
+ return MCDisassembler::Fail;
+ for (int64_t I = 0; I < TargetTableLen; I++) {
+ if (!parseLEBImmediate(MI, Size, Bytes, false))
+ return MCDisassembler::Fail;
+ }
+ // Default case.
+ if (!parseLEBImmediate(MI, Size, Bytes, false))
+ return MCDisassembler::Fail;
break;
}
+ case MCOI::OPERAND_REGISTER:
+ // The tablegen header currently does not have any register operands since
+ // we use only the stack (_S) instructions.
+ // If you hit this that probably means a bad instruction definition in
+ // tablegen.
+ llvm_unreachable("Register operand in WebAssemblyDisassembler");
default:
llvm_unreachable("Unknown operand type in WebAssemblyDisassembler");
}
diff --git a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp
index 10fa798ac8d7..15532d7ff1a6 100644
--- a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp
+++ b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp
@@ -35,12 +35,12 @@ using namespace llvm;
WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI,
const MCInstrInfo &MII,
const MCRegisterInfo &MRI)
- : MCInstPrinter(MAI, MII, MRI), ControlFlowCounter(0) {}
+ : MCInstPrinter(MAI, MII, MRI) {}
void WebAssemblyInstPrinter::printRegName(raw_ostream &OS,
unsigned RegNo) const {
assert(RegNo != WebAssemblyFunctionInfo::UnusedReg);
- // Note that there's an implicit get_local/set_local here!
+ // Note that there's an implicit local.get/local.set here!
OS << "$" << RegNo;
}
@@ -57,9 +57,9 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
// FIXME: For CALL_INDIRECT_VOID, don't print a leading comma, because
// we have an extra flags operand which is not currently printed, for
// compatiblity reasons.
- if (i != 0 &&
- (MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID ||
- i != Desc.getNumOperands()))
+ if (i != 0 && ((MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID &&
+ MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID_S) ||
+ i != Desc.getNumOperands()))
OS << ", ";
printOperand(MI, i, OS);
}
@@ -70,25 +70,76 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
if (CommentStream) {
// Observe any effects on the control flow stack, for use in annotating
// control flow label references.
- switch (MI->getOpcode()) {
+ unsigned Opc = MI->getOpcode();
+ switch (Opc) {
default:
break;
- case WebAssembly::LOOP: {
+
+ case WebAssembly::LOOP:
+ case WebAssembly::LOOP_S:
printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
break;
- }
+
case WebAssembly::BLOCK:
+ case WebAssembly::BLOCK_S:
ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
break;
+
+ case WebAssembly::TRY:
+ case WebAssembly::TRY_S:
+ ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
+ EHPadStack.push_back(EHPadStackCounter++);
+ LastSeenEHInst = TRY;
+ break;
+
case WebAssembly::END_LOOP:
- // Have to guard against an empty stack, in case of mismatched pairs
- // in assembly parsing.
- if (!ControlFlowStack.empty()) ControlFlowStack.pop_back();
+ case WebAssembly::END_LOOP_S:
+ if (ControlFlowStack.empty()) {
+ printAnnotation(OS, "End marker mismatch!");
+ } else {
+ ControlFlowStack.pop_back();
+ }
break;
+
case WebAssembly::END_BLOCK:
- if (!ControlFlowStack.empty()) printAnnotation(
- OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
+ case WebAssembly::END_BLOCK_S:
+ if (ControlFlowStack.empty()) {
+ printAnnotation(OS, "End marker mismatch!");
+ } else {
+ printAnnotation(
+ OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
+ }
+ break;
+
+ case WebAssembly::END_TRY:
+ case WebAssembly::END_TRY_S:
+ if (ControlFlowStack.empty()) {
+ printAnnotation(OS, "End marker mismatch!");
+ } else {
+ printAnnotation(
+ OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
+ LastSeenEHInst = END_TRY;
+ }
+ break;
+
+ case WebAssembly::CATCH_I32:
+ case WebAssembly::CATCH_I32_S:
+ case WebAssembly::CATCH_I64:
+ case WebAssembly::CATCH_I64_S:
+ case WebAssembly::CATCH_ALL:
+ case WebAssembly::CATCH_ALL_S:
+ // There can be multiple catch instructions for one try instruction, so we
+ // print a label only for the first 'catch' label.
+ if (LastSeenEHInst != CATCH) {
+ if (EHPadStack.empty()) {
+ printAnnotation(OS, "try-catch mismatch!");
+ } else {
+ printAnnotation(OS,
+ "catch" + utostr(EHPadStack.pop_back_val()) + ':');
+ }
+ }
+ LastSeenEHInst = CATCH;
break;
}
@@ -96,34 +147,61 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
unsigned NumFixedOperands = Desc.NumOperands;
SmallSet<uint64_t, 8> Printed;
for (unsigned i = 0, e = MI->getNumOperands(); i < e; ++i) {
- if (!(i < NumFixedOperands
- ? (Desc.OpInfo[i].OperandType ==
- WebAssembly::OPERAND_BASIC_BLOCK)
- : (Desc.TSFlags & WebAssemblyII::VariableOpImmediateIsLabel)))
- continue;
+ // See if this operand denotes a basic block target.
+ if (i < NumFixedOperands) {
+ // A non-variable_ops operand, check its type.
+ if (Desc.OpInfo[i].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
+ continue;
+ } else {
+ // A variable_ops operand, which currently can be immediates (used in
+ // br_table) which are basic block targets, or for call instructions
+ // when using -wasm-keep-registers (in which case they are registers,
+ // and should not be processed).
+ if (!MI->getOperand(i).isImm())
+ continue;
+ }
uint64_t Depth = MI->getOperand(i).getImm();
if (!Printed.insert(Depth).second)
continue;
- const auto &Pair = ControlFlowStack.rbegin()[Depth];
- printAnnotation(OS, utostr(Depth) + ": " + (Pair.second ? "up" : "down") +
- " to label" + utostr(Pair.first));
+
+ if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) {
+ if (Depth > EHPadStack.size()) {
+ printAnnotation(OS, "Invalid depth argument!");
+ } else if (Depth == EHPadStack.size()) {
+ // This can happen when rethrow instruction breaks out of all nests
+ // and throws up to the current function's caller.
+ printAnnotation(OS, utostr(Depth) + ": " + "to caller");
+ } else {
+ uint64_t CatchNo = EHPadStack.rbegin()[Depth];
+ printAnnotation(OS, utostr(Depth) + ": " + "down to catch" +
+ utostr(CatchNo));
+ }
+
+ } else {
+ if (Depth >= ControlFlowStack.size()) {
+ printAnnotation(OS, "Invalid depth argument!");
+ } else {
+ const auto &Pair = ControlFlowStack.rbegin()[Depth];
+ printAnnotation(OS, utostr(Depth) + ": " +
+ (Pair.second ? "up" : "down") + " to label" +
+ utostr(Pair.first));
+ }
+ }
}
}
}
static std::string toString(const APFloat &FP) {
// Print NaNs with custom payloads specially.
- if (FP.isNaN() &&
- !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
+ if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
!FP.bitwiseIsEqual(
APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
APInt AI = FP.bitcastToAPInt();
- return
- std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
- utohexstr(AI.getZExtValue() &
- (AI.getBitWidth() == 32 ? INT64_C(0x007fffff) :
- INT64_C(0x000fffffffffffff)),
- /*LowerCase=*/true);
+ return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
+ utohexstr(AI.getZExtValue() &
+ (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
+ : INT64_C(0x000fffffffffffff)),
+ /*LowerCase=*/true);
}
// Use C99's hexadecimal floating-point representation.
@@ -141,9 +219,6 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O) {
const MCOperand &Op = MI->getOperand(OpNo);
if (Op.isReg()) {
- assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() ||
- MII.get(MI->getOpcode()).TSFlags == 0) &&
- "WebAssembly variable_ops register ops don't use TSFlags");
unsigned WAReg = Op.getReg();
if (int(WAReg) >= 0)
printRegName(O, WAReg);
@@ -157,23 +232,9 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
if (OpNo < MII.get(MI->getOpcode()).getNumDefs())
O << '=';
} else if (Op.isImm()) {
- const MCInstrDesc &Desc = MII.get(MI->getOpcode());
- assert((OpNo < Desc.getNumOperands() ||
- (Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate)) &&
- "WebAssemblyII::VariableOpIsImmediate should be set for "
- "variable_ops immediate ops");
- (void)Desc;
- // TODO: (MII.get(MI->getOpcode()).TSFlags &
- // WebAssemblyII::VariableOpImmediateIsLabel)
- // can tell us whether this is an immediate referencing a label in the
- // control flow stack, and it may be nice to pretty-print.
O << Op.getImm();
} else if (Op.isFPImm()) {
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
- assert(OpNo < Desc.getNumOperands() &&
- "Unexpected floating-point immediate as a non-fixed operand");
- assert(Desc.TSFlags == 0 &&
- "WebAssembly variable_ops floating point ops don't use TSFlags");
const MCOperandInfo &Info = Desc.OpInfo[OpNo];
if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
// TODO: MC converts all floating point immediate operands to double.
@@ -184,78 +245,66 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
O << ::toString(APFloat(Op.getFPImm()));
}
} else {
- assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() ||
- (MII.get(MI->getOpcode()).TSFlags &
- WebAssemblyII::VariableOpIsImmediate)) &&
- "WebAssemblyII::VariableOpIsImmediate should be set for "
- "variable_ops expr ops");
assert(Op.isExpr() && "unknown operand kind in printOperand");
Op.getExpr()->print(O, &MAI);
}
}
-void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(
- const MCInst *MI, unsigned OpNo, raw_ostream &O) {
+void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ O << "{";
+ for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
+ if (I != OpNo)
+ O << ", ";
+ O << MI->getOperand(I).getImm();
+ }
+ O << "}";
+}
+
+void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
+ unsigned OpNo,
+ raw_ostream &O) {
int64_t Imm = MI->getOperand(OpNo).getImm();
if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
return;
O << ":p2align=" << Imm;
}
-void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(
- const MCInst *MI, unsigned OpNo, raw_ostream &O) {
- int64_t Imm = MI->getOperand(OpNo).getImm();
- switch (WebAssembly::ExprType(Imm)) {
- case WebAssembly::ExprType::Void: break;
- case WebAssembly::ExprType::I32: O << "i32"; break;
- case WebAssembly::ExprType::I64: O << "i64"; break;
- case WebAssembly::ExprType::F32: O << "f32"; break;
- case WebAssembly::ExprType::F64: O << "f64"; break;
- case WebAssembly::ExprType::I8x16: O << "i8x16"; break;
- case WebAssembly::ExprType::I16x8: O << "i16x8"; break;
- case WebAssembly::ExprType::I32x4: O << "i32x4"; break;
- case WebAssembly::ExprType::F32x4: O << "f32x4"; break;
- case WebAssembly::ExprType::B8x16: O << "b8x16"; break;
- case WebAssembly::ExprType::B16x8: O << "b16x8"; break;
- case WebAssembly::ExprType::B32x4: O << "b32x4"; break;
- case WebAssembly::ExprType::ExceptRef: O << "except_ref"; break;
- }
+void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,
+ unsigned OpNo,
+ raw_ostream &O) {
+ auto Imm = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ if (Imm != wasm::WASM_TYPE_NORESULT)
+ O << WebAssembly::anyTypeToString(Imm);
}
-const char *llvm::WebAssembly::TypeToString(MVT Ty) {
- switch (Ty.SimpleTy) {
- case MVT::i32:
+// We have various enums representing a subset of these types, use this
+// function to convert any of them to text.
+const char *llvm::WebAssembly::anyTypeToString(unsigned Ty) {
+ switch (Ty) {
+ case wasm::WASM_TYPE_I32:
return "i32";
- case MVT::i64:
+ case wasm::WASM_TYPE_I64:
return "i64";
- case MVT::f32:
+ case wasm::WASM_TYPE_F32:
return "f32";
- case MVT::f64:
+ case wasm::WASM_TYPE_F64:
return "f64";
- case MVT::v16i8:
- case MVT::v8i16:
- case MVT::v4i32:
- case MVT::v4f32:
+ case wasm::WASM_TYPE_V128:
return "v128";
- case MVT::ExceptRef:
+ case wasm::WASM_TYPE_FUNCREF:
+ return "funcref";
+ case wasm::WASM_TYPE_FUNC:
+ return "func";
+ case wasm::WASM_TYPE_EXCEPT_REF:
return "except_ref";
+ case wasm::WASM_TYPE_NORESULT:
+ return "void";
default:
- llvm_unreachable("unsupported type");
+ return "invalid_type";
}
}
-const char *llvm::WebAssembly::TypeToString(wasm::ValType Type) {
- switch (Type) {
- case wasm::ValType::I32:
- return "i32";
- case wasm::ValType::I64:
- return "i64";
- case wasm::ValType::F32:
- return "f32";
- case wasm::ValType::F64:
- return "f64";
- case wasm::ValType::EXCEPT_REF:
- return "except_ref";
- }
- llvm_unreachable("unsupported type");
+const char *llvm::WebAssembly::typeToString(wasm::ValType Ty) {
+ return anyTypeToString(static_cast<unsigned>(Ty));
}
diff --git a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h
index f5b890a7615e..5ad45c7d5c7f 100644
--- a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h
+++ b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h
@@ -25,8 +25,13 @@ namespace llvm {
class MCSubtargetInfo;
class WebAssemblyInstPrinter final : public MCInstPrinter {
- uint64_t ControlFlowCounter;
- SmallVector<std::pair<uint64_t, bool>, 0> ControlFlowStack;
+ uint64_t ControlFlowCounter = 0;
+ uint64_t EHPadStackCounter = 0;
+ SmallVector<std::pair<uint64_t, bool>, 4> ControlFlowStack;
+ SmallVector<uint64_t, 4> EHPadStack;
+
+ enum EHInstKind { TRY, CATCH, END_TRY };
+ EHInstKind LastSeenEHInst = END_TRY;
public:
WebAssemblyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII,
@@ -38,6 +43,7 @@ public:
// Used by tblegen code.
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void printBrList(const MCInst *MI, unsigned OpNo, raw_ostream &O);
void printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O);
void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo,
@@ -50,8 +56,8 @@ public:
namespace WebAssembly {
-const char *TypeToString(MVT Ty);
-const char *TypeToString(wasm::ValType Type);
+const char *typeToString(wasm::ValType Ty);
+const char *anyTypeToString(unsigned Ty);
} // end namespace WebAssembly
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
index 244c2189b455..0726dd481174 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp
@@ -73,13 +73,13 @@ public:
const MCFixupKindInfo &
WebAssemblyAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
const static MCFixupKindInfo Infos[WebAssembly::NumTargetFixupKinds] = {
- // This table *must* be in the order that the fixup_* kinds are defined in
- // WebAssemblyFixupKinds.h.
- //
- // Name Offset (bits) Size (bits) Flags
- { "fixup_code_sleb128_i32", 0, 5*8, 0 },
- { "fixup_code_sleb128_i64", 0, 10*8, 0 },
- { "fixup_code_uleb128_i32", 0, 5*8, 0 },
+ // This table *must* be in the order that the fixup_* kinds are defined in
+ // WebAssemblyFixupKinds.h.
+ //
+ // Name Offset (bits) Size (bits) Flags
+ {"fixup_code_sleb128_i32", 0, 5 * 8, 0},
+ {"fixup_code_sleb128_i64", 0, 10 * 8, 0},
+ {"fixup_code_uleb128_i32", 0, 5 * 8, 0},
};
if (Kind < FirstTargetFixupKind)
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h
index b0af63c924bd..c2fac5f93a2f 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h
@@ -15,11 +15,9 @@
namespace llvm {
namespace WebAssembly {
enum Fixups {
- fixup_code_sleb128_i32 = FirstTargetFixupKind, // 32-bit signed
- fixup_code_sleb128_i64, // 64-bit signed
- fixup_code_uleb128_i32, // 32-bit unsigned
-
- fixup_code_global_index, // 32-bit unsigned
+ fixup_code_sleb128_i32 = FirstTargetFixupKind, // 32-bit signed
+ fixup_code_sleb128_i64, // 64-bit signed
+ fixup_code_uleb128_i32, // 32-bit unsigned
// Marker
LastTargetFixupKind,
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
index 94ca94e1e18c..065a4dc94ca6 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp
@@ -67,13 +67,16 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
OS << uint8_t(Binary);
} else {
assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet");
- OS << uint8_t(Binary >> 8)
- << uint8_t(Binary);
+ OS << uint8_t(Binary >> 8);
+ encodeULEB128(uint8_t(Binary), OS);
}
// For br_table instructions, encode the size of the table. In the MCInst,
- // there's an index operand, one operand for each table entry, and the
- // default operand.
+ // there's an index operand (if not a stack instruction), one operand for
+ // each table entry, and the default operand.
+ if (MI.getOpcode() == WebAssembly::BR_TABLE_I32_S ||
+ MI.getOpcode() == WebAssembly::BR_TABLE_I64_S)
+ encodeULEB128(MI.getNumOperands() - 1, OS);
if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 ||
MI.getOpcode() == WebAssembly::BR_TABLE_I64)
encodeULEB128(MI.getNumOperands() - 2, OS);
@@ -83,36 +86,47 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
const MCOperand &MO = MI.getOperand(i);
if (MO.isReg()) {
/* nothing to encode */
+
} else if (MO.isImm()) {
if (i < Desc.getNumOperands()) {
- assert(Desc.TSFlags == 0 &&
- "WebAssembly non-variable_ops don't use TSFlags");
const MCOperandInfo &Info = Desc.OpInfo[i];
LLVM_DEBUG(dbgs() << "Encoding immediate: type="
<< int(Info.OperandType) << "\n");
- if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
+ switch (Info.OperandType) {
+ case WebAssembly::OPERAND_I32IMM:
encodeSLEB128(int32_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_OFFSET32) {
+ break;
+ case WebAssembly::OPERAND_OFFSET32:
encodeULEB128(uint32_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
+ break;
+ case WebAssembly::OPERAND_I64IMM:
encodeSLEB128(int64_t(MO.getImm()), OS);
- } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
- llvm_unreachable("wasm globals should only be accessed symbolicly");
- } else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
+ break;
+ case WebAssembly::OPERAND_SIGNATURE:
OS << uint8_t(MO.getImm());
- } else {
+ break;
+ case WebAssembly::OPERAND_VEC_I8IMM:
+ support::endian::write<uint8_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_VEC_I16IMM:
+ support::endian::write<uint16_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_VEC_I32IMM:
+ support::endian::write<uint32_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_VEC_I64IMM:
+ support::endian::write<uint64_t>(OS, MO.getImm(), support::little);
+ break;
+ case WebAssembly::OPERAND_GLOBAL:
+ llvm_unreachable("wasm globals should only be accessed symbolicly");
+ default:
encodeULEB128(uint64_t(MO.getImm()), OS);
}
} else {
- assert(Desc.TSFlags == (WebAssemblyII::VariableOpIsImmediate |
- WebAssemblyII::VariableOpImmediateIsLabel));
encodeULEB128(uint64_t(MO.getImm()), OS);
}
+
} else if (MO.isFPImm()) {
- assert(i < Desc.getNumOperands() &&
- "Unexpected floating-point immediate as a non-fixed operand");
- assert(Desc.TSFlags == 0 &&
- "WebAssembly variable_ops floating point ops don't use TSFlags");
const MCOperandInfo &Info = Desc.OpInfo[i];
if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
// TODO: MC converts all floating point immediate operands to double.
@@ -124,27 +138,31 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
double d = MO.getFPImm();
support::endian::write<double>(OS, d, support::little);
}
+
} else if (MO.isExpr()) {
const MCOperandInfo &Info = Desc.OpInfo[i];
llvm::MCFixupKind FixupKind;
size_t PaddedSize = 5;
- if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
+ switch (Info.OperandType) {
+ case WebAssembly::OPERAND_I32IMM:
FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i32);
- } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
+ break;
+ case WebAssembly::OPERAND_I64IMM:
FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i64);
PaddedSize = 10;
- } else if (Info.OperandType == WebAssembly::OPERAND_FUNCTION32 ||
- Info.OperandType == WebAssembly::OPERAND_OFFSET32 ||
- Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
+ break;
+ case WebAssembly::OPERAND_FUNCTION32:
+ case WebAssembly::OPERAND_OFFSET32:
+ case WebAssembly::OPERAND_TYPEINDEX:
+ case WebAssembly::OPERAND_GLOBAL:
+ case WebAssembly::OPERAND_EVENT:
FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32);
- } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
- FixupKind = MCFixupKind(WebAssembly::fixup_code_global_index);
- } else {
+ break;
+ default:
llvm_unreachable("unexpected symbolic operand kind");
}
- Fixups.push_back(MCFixup::create(
- OS.tell() - Start, MO.getExpr(),
- FixupKind, MI.getLoc()));
+ Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(),
+ FixupKind, MI.getLoc()));
++MCNumFixups;
encodeULEB128(0, OS, PaddedSize);
} else {
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
index baf8a0c96c0a..390f367c2978 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp
@@ -90,6 +90,10 @@ static MCTargetStreamer *createAsmTargetStreamer(MCStreamer &S,
return new WebAssemblyTargetAsmStreamer(S, OS);
}
+static MCTargetStreamer *createNullTargetStreamer(MCStreamer &S) {
+ return new WebAssemblyTargetNullStreamer(S);
+}
+
// Force static initialization.
extern "C" void LLVMInitializeWebAssemblyTargetMC() {
for (Target *T :
@@ -120,16 +124,31 @@ extern "C" void LLVMInitializeWebAssemblyTargetMC() {
createObjectTargetStreamer);
// Register the asm target streamer.
TargetRegistry::RegisterAsmTargetStreamer(*T, createAsmTargetStreamer);
+ // Register the null target streamer.
+ TargetRegistry::RegisterNullTargetStreamer(*T, createNullTargetStreamer);
}
}
wasm::ValType WebAssembly::toValType(const MVT &Ty) {
switch (Ty.SimpleTy) {
- case MVT::i32: return wasm::ValType::I32;
- case MVT::i64: return wasm::ValType::I64;
- case MVT::f32: return wasm::ValType::F32;
- case MVT::f64: return wasm::ValType::F64;
- case MVT::ExceptRef: return wasm::ValType::EXCEPT_REF;
- default: llvm_unreachable("unexpected type");
+ case MVT::i32:
+ return wasm::ValType::I32;
+ case MVT::i64:
+ return wasm::ValType::I64;
+ case MVT::f32:
+ return wasm::ValType::F32;
+ case MVT::f64:
+ return wasm::ValType::F64;
+ case MVT::v16i8:
+ case MVT::v8i16:
+ case MVT::v4i32:
+ case MVT::v2i64:
+ case MVT::v4f32:
+ case MVT::v2f64:
+ return wasm::ValType::V128;
+ case MVT::ExceptRef:
+ return wasm::ValType::EXCEPT_REF;
+ default:
+ llvm_unreachable("unexpected type");
}
}
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index c1c8d243e920..a01517fb90c3 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -59,6 +59,14 @@ enum OperandType {
OPERAND_F32IMM,
/// 64-bit floating-point immediates.
OPERAND_F64IMM,
+ /// 8-bit vector lane immediate
+ OPERAND_VEC_I8IMM,
+ /// 16-bit vector lane immediate
+ OPERAND_VEC_I16IMM,
+ /// 32-bit vector lane immediate
+ OPERAND_VEC_I32IMM,
+ /// 64-bit vector lane immediate
+ OPERAND_VEC_I64IMM,
/// 32-bit unsigned function indices.
OPERAND_FUNCTION32,
/// 32-bit unsigned memory offsets.
@@ -69,17 +77,24 @@ enum OperandType {
OPERAND_SIGNATURE,
/// type signature immediate for call_indirect.
OPERAND_TYPEINDEX,
+ /// Event index.
+ OPERAND_EVENT,
+ /// A list of branch targets for br_list.
+ OPERAND_BRLIST,
};
} // end namespace WebAssembly
namespace WebAssemblyII {
-enum {
- // For variadic instructions, this flag indicates whether an operand
- // in the variable_ops range is an immediate value.
- VariableOpIsImmediate = (1 << 0),
- // For immediate values in the variable_ops range, this flag indicates
- // whether the value represents a control-flow label.
- VariableOpImmediateIsLabel = (1 << 1)
+
+/// Target Operand Flag enum.
+enum TOF {
+ MO_NO_FLAG = 0,
+
+ // Flags to indicate the type of the symbol being referenced
+ MO_SYMBOL_FUNCTION = 0x1,
+ MO_SYMBOL_GLOBAL = 0x2,
+ MO_SYMBOL_EVENT = 0x4,
+ MO_SYMBOL_MASK = 0x7,
};
} // end namespace WebAssemblyII
@@ -149,6 +164,10 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
case WebAssembly::ATOMIC_RMW8_U_XCHG_I32_S:
case WebAssembly::ATOMIC_RMW8_U_XCHG_I64:
case WebAssembly::ATOMIC_RMW8_U_XCHG_I64_S:
+ case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I32:
+ case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I32_S:
+ case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I64:
+ case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I64_S:
return 0;
case WebAssembly::LOAD16_S_I32:
case WebAssembly::LOAD16_S_I32_S:
@@ -194,6 +213,10 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
case WebAssembly::ATOMIC_RMW16_U_XCHG_I32_S:
case WebAssembly::ATOMIC_RMW16_U_XCHG_I64:
case WebAssembly::ATOMIC_RMW16_U_XCHG_I64_S:
+ case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I32:
+ case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I32_S:
+ case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I64:
+ case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I64_S:
return 1;
case WebAssembly::LOAD_I32:
case WebAssembly::LOAD_I32_S:
@@ -241,6 +264,14 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
case WebAssembly::ATOMIC_RMW_XCHG_I32_S:
case WebAssembly::ATOMIC_RMW32_U_XCHG_I64:
case WebAssembly::ATOMIC_RMW32_U_XCHG_I64_S:
+ case WebAssembly::ATOMIC_RMW_CMPXCHG_I32:
+ case WebAssembly::ATOMIC_RMW_CMPXCHG_I32_S:
+ case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64:
+ case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64_S:
+ case WebAssembly::ATOMIC_NOTIFY:
+ case WebAssembly::ATOMIC_NOTIFY_S:
+ case WebAssembly::ATOMIC_WAIT_I32:
+ case WebAssembly::ATOMIC_WAIT_I32_S:
return 2;
case WebAssembly::LOAD_I64:
case WebAssembly::LOAD_I64_S:
@@ -266,7 +297,36 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
case WebAssembly::ATOMIC_RMW_XOR_I64_S:
case WebAssembly::ATOMIC_RMW_XCHG_I64:
case WebAssembly::ATOMIC_RMW_XCHG_I64_S:
+ case WebAssembly::ATOMIC_RMW_CMPXCHG_I64:
+ case WebAssembly::ATOMIC_RMW_CMPXCHG_I64_S:
+ case WebAssembly::ATOMIC_WAIT_I64:
+ case WebAssembly::ATOMIC_WAIT_I64_S:
return 3;
+ case WebAssembly::LOAD_v16i8:
+ case WebAssembly::LOAD_v16i8_S:
+ case WebAssembly::LOAD_v8i16:
+ case WebAssembly::LOAD_v8i16_S:
+ case WebAssembly::LOAD_v4i32:
+ case WebAssembly::LOAD_v4i32_S:
+ case WebAssembly::LOAD_v2i64:
+ case WebAssembly::LOAD_v2i64_S:
+ case WebAssembly::LOAD_v4f32:
+ case WebAssembly::LOAD_v4f32_S:
+ case WebAssembly::LOAD_v2f64:
+ case WebAssembly::LOAD_v2f64_S:
+ case WebAssembly::STORE_v16i8:
+ case WebAssembly::STORE_v16i8_S:
+ case WebAssembly::STORE_v8i16:
+ case WebAssembly::STORE_v8i16_S:
+ case WebAssembly::STORE_v4i32:
+ case WebAssembly::STORE_v4i32_S:
+ case WebAssembly::STORE_v2i64:
+ case WebAssembly::STORE_v2i64_S:
+ case WebAssembly::STORE_v4f32:
+ case WebAssembly::STORE_v4f32_S:
+ case WebAssembly::STORE_v2f64:
+ case WebAssembly::STORE_v2f64_S:
+ return 4;
default:
llvm_unreachable("Only loads and stores have p2align values");
}
@@ -282,19 +342,14 @@ static const unsigned StoreP2AlignOperandNo = 0;
/// This is used to indicate block signatures.
enum class ExprType : unsigned {
- Void = 0x40,
- I32 = 0x7F,
- I64 = 0x7E,
- F32 = 0x7D,
- F64 = 0x7C,
- I8x16 = 0x7B,
- I16x8 = 0x7A,
- I32x4 = 0x79,
- F32x4 = 0x78,
- B8x16 = 0x77,
- B16x8 = 0x76,
- B32x4 = 0x75,
- ExceptRef = 0x68
+ Void = 0x40,
+ I32 = 0x7F,
+ I64 = 0x7E,
+ F32 = 0x7D,
+ F64 = 0x7C,
+ V128 = 0x7B,
+ ExceptRef = 0x68,
+ Invalid = 0x00
};
/// Instruction opcodes emitted via means other than CodeGen.
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index 5272e188e1d0..50143fb0ece3 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -39,70 +39,80 @@ WebAssemblyTargetAsmStreamer::WebAssemblyTargetAsmStreamer(
WebAssemblyTargetWasmStreamer::WebAssemblyTargetWasmStreamer(MCStreamer &S)
: WebAssemblyTargetStreamer(S) {}
-static void PrintTypes(formatted_raw_ostream &OS, ArrayRef<MVT> Types) {
+static void printTypes(formatted_raw_ostream &OS,
+ ArrayRef<wasm::ValType> Types) {
bool First = true;
- for (MVT Type : Types) {
+ for (auto Type : Types) {
if (First)
First = false;
else
OS << ", ";
- OS << WebAssembly::TypeToString(Type);
+ OS << WebAssembly::typeToString(Type);
}
OS << '\n';
}
-void WebAssemblyTargetAsmStreamer::emitParam(MCSymbol *Symbol,
- ArrayRef<MVT> Types) {
+void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<wasm::ValType> Types) {
if (!Types.empty()) {
- OS << "\t.param \t";
-
- // FIXME: Currently this applies to the "current" function; it may
- // be cleaner to specify an explicit symbol as part of the directive.
-
- PrintTypes(OS, Types);
+ OS << "\t.local \t";
+ printTypes(OS, Types);
}
}
-void WebAssemblyTargetAsmStreamer::emitResult(MCSymbol *Symbol,
- ArrayRef<MVT> Types) {
- if (!Types.empty()) {
- OS << "\t.result \t";
+void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; }
- // FIXME: Currently this applies to the "current" function; it may
- // be cleaner to specify an explicit symbol as part of the directive.
+void WebAssemblyTargetAsmStreamer::emitSignature(
+ const wasm::WasmSignature *Sig) {
+ OS << "(";
+ emitParamList(Sig);
+ OS << ") -> (";
+ emitReturnList(Sig);
+ OS << ")";
+}
- PrintTypes(OS, Types);
+void WebAssemblyTargetAsmStreamer::emitParamList(
+ const wasm::WasmSignature *Sig) {
+ auto &Params = Sig->Params;
+ for (auto &Ty : Params) {
+ if (&Ty != &Params[0])
+ OS << ", ";
+ OS << WebAssembly::typeToString(Ty);
}
}
-void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<MVT> Types) {
- if (!Types.empty()) {
- OS << "\t.local \t";
- PrintTypes(OS, Types);
+void WebAssemblyTargetAsmStreamer::emitReturnList(
+ const wasm::WasmSignature *Sig) {
+ auto &Returns = Sig->Returns;
+ for (auto &Ty : Returns) {
+ if (&Ty != &Returns[0])
+ OS << ", ";
+ OS << WebAssembly::typeToString(Ty);
}
}
-void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; }
+void WebAssemblyTargetAsmStreamer::emitFunctionType(const MCSymbolWasm *Sym) {
+ assert(Sym->isFunction());
+ OS << "\t.functype\t" << Sym->getName() << " ";
+ emitSignature(Sym->getSignature());
+ OS << "\n";
+}
-void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType(
- MCSymbol *Symbol, SmallVectorImpl<MVT> &Params, SmallVectorImpl<MVT> &Results) {
- OS << "\t.functype\t" << Symbol->getName();
- if (Results.empty())
- OS << ", void";
- else {
- assert(Results.size() == 1);
- OS << ", " << WebAssembly::TypeToString(Results.front());
- }
- for (auto Ty : Params)
- OS << ", " << WebAssembly::TypeToString(Ty);
- OS << '\n';
+void WebAssemblyTargetAsmStreamer::emitGlobalType(const MCSymbolWasm *Sym) {
+ assert(Sym->isGlobal());
+ OS << "\t.globaltype\t" << Sym->getName() << ", "
+ << WebAssembly::typeToString(
+ static_cast<wasm::ValType>(Sym->getGlobalType().Type))
+ << '\n';
}
-void WebAssemblyTargetAsmStreamer::emitGlobalImport(StringRef name) {
- OS << "\t.import_global\t" << name << '\n';
+void WebAssemblyTargetAsmStreamer::emitEventType(const MCSymbolWasm *Sym) {
+ assert(Sym->isEvent());
+ OS << "\t.eventtype\t" << Sym->getName() << " ";
+ emitParamList(Sym->getSignature());
+ OS << "\n";
}
-void WebAssemblyTargetAsmStreamer::emitImportModule(MCSymbolWasm *Sym,
+void WebAssemblyTargetAsmStreamer::emitImportModule(const MCSymbolWasm *Sym,
StringRef ModuleName) {
OS << "\t.import_module\t" << Sym->getName() << ", " << ModuleName << '\n';
}
@@ -111,27 +121,9 @@ void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
OS << "\t.indidx \t" << *Value << '\n';
}
-void WebAssemblyTargetWasmStreamer::emitParam(MCSymbol *Symbol,
- ArrayRef<MVT> Types) {
- SmallVector<wasm::ValType, 4> Params;
- for (MVT Ty : Types)
- Params.push_back(WebAssembly::toValType(Ty));
-
- cast<MCSymbolWasm>(Symbol)->setParams(std::move(Params));
-}
-
-void WebAssemblyTargetWasmStreamer::emitResult(MCSymbol *Symbol,
- ArrayRef<MVT> Types) {
- SmallVector<wasm::ValType, 4> Returns;
- for (MVT Ty : Types)
- Returns.push_back(WebAssembly::toValType(Ty));
-
- cast<MCSymbolWasm>(Symbol)->setReturns(std::move(Returns));
-}
-
-void WebAssemblyTargetWasmStreamer::emitLocal(ArrayRef<MVT> Types) {
- SmallVector<std::pair<MVT, uint32_t>, 4> Grouped;
- for (MVT Type : Types) {
+void WebAssemblyTargetWasmStreamer::emitLocal(ArrayRef<wasm::ValType> Types) {
+ SmallVector<std::pair<wasm::ValType, uint32_t>, 4> Grouped;
+ for (auto Type : Types) {
if (Grouped.empty() || Grouped.back().first != Type)
Grouped.push_back(std::make_pair(Type, 1));
else
@@ -141,7 +133,7 @@ void WebAssemblyTargetWasmStreamer::emitLocal(ArrayRef<MVT> Types) {
Streamer.EmitULEB128IntValue(Grouped.size());
for (auto Pair : Grouped) {
Streamer.EmitULEB128IntValue(Pair.second);
- emitValueType(WebAssembly::toValType(Pair.first));
+ emitValueType(Pair.first);
}
}
@@ -152,34 +144,3 @@ void WebAssemblyTargetWasmStreamer::emitEndFunc() {
void WebAssemblyTargetWasmStreamer::emitIndIdx(const MCExpr *Value) {
llvm_unreachable(".indidx encoding not yet implemented");
}
-
-void WebAssemblyTargetWasmStreamer::emitIndirectFunctionType(
- MCSymbol *Symbol, SmallVectorImpl<MVT> &Params,
- SmallVectorImpl<MVT> &Results) {
- MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Symbol);
- if (WasmSym->isFunction()) {
- // Symbol already has its arguments and result set.
- return;
- }
-
- SmallVector<wasm::ValType, 4> ValParams;
- for (MVT Ty : Params)
- ValParams.push_back(WebAssembly::toValType(Ty));
-
- SmallVector<wasm::ValType, 1> ValResults;
- for (MVT Ty : Results)
- ValResults.push_back(WebAssembly::toValType(Ty));
-
- WasmSym->setParams(std::move(ValParams));
- WasmSym->setReturns(std::move(ValResults));
- WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
-}
-
-void WebAssemblyTargetWasmStreamer::emitGlobalImport(StringRef name) {
- llvm_unreachable(".global_import is not needed for direct wasm output");
-}
-
-void WebAssemblyTargetWasmStreamer::emitImportModule(MCSymbolWasm *Sym,
- StringRef ModuleName) {
- Sym->setModuleName(ModuleName);
-}
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
index cafcb04ccd11..3073938118b4 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
@@ -31,24 +31,21 @@ class WebAssemblyTargetStreamer : public MCTargetStreamer {
public:
explicit WebAssemblyTargetStreamer(MCStreamer &S);
- /// .param
- virtual void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) = 0;
- /// .result
- virtual void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) = 0;
/// .local
- virtual void emitLocal(ArrayRef<MVT> Types) = 0;
+ virtual void emitLocal(ArrayRef<wasm::ValType> Types) = 0;
/// .endfunc
virtual void emitEndFunc() = 0;
/// .functype
- virtual void emitIndirectFunctionType(MCSymbol *Symbol,
- SmallVectorImpl<MVT> &Params,
- SmallVectorImpl<MVT> &Results) = 0;
+ virtual void emitFunctionType(const MCSymbolWasm *Sym) = 0;
/// .indidx
virtual void emitIndIdx(const MCExpr *Value) = 0;
- /// .import_global
- virtual void emitGlobalImport(StringRef name) = 0;
+ /// .globaltype
+ virtual void emitGlobalType(const MCSymbolWasm *Sym) = 0;
+ /// .eventtype
+ virtual void emitEventType(const MCSymbolWasm *Sym) = 0;
/// .import_module
- virtual void emitImportModule(MCSymbolWasm *Sym, StringRef ModuleName) = 0;
+ virtual void emitImportModule(const MCSymbolWasm *Sym,
+ StringRef ModuleName) = 0;
protected:
void emitValueType(wasm::ValType Type);
@@ -57,20 +54,20 @@ protected:
/// This part is for ascii assembly output
class WebAssemblyTargetAsmStreamer final : public WebAssemblyTargetStreamer {
formatted_raw_ostream &OS;
+ void emitSignature(const wasm::WasmSignature *Sig);
+ void emitParamList(const wasm::WasmSignature *Sig);
+ void emitReturnList(const wasm::WasmSignature *Sig);
public:
WebAssemblyTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);
- void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
- void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
- void emitLocal(ArrayRef<MVT> Types) override;
+ void emitLocal(ArrayRef<wasm::ValType> Types) override;
void emitEndFunc() override;
- void emitIndirectFunctionType(MCSymbol *Symbol,
- SmallVectorImpl<MVT> &Params,
- SmallVectorImpl<MVT> &Results) override;
+ void emitFunctionType(const MCSymbolWasm *Sym) override;
void emitIndIdx(const MCExpr *Value) override;
- void emitGlobalImport(StringRef name) override;
- void emitImportModule(MCSymbolWasm *Sym, StringRef ModuleName) override;
+ void emitGlobalType(const MCSymbolWasm *Sym) override;
+ void emitEventType(const MCSymbolWasm *Sym) override;
+ void emitImportModule(const MCSymbolWasm *Sym, StringRef ModuleName) override;
};
/// This part is for Wasm object output
@@ -78,16 +75,29 @@ class WebAssemblyTargetWasmStreamer final : public WebAssemblyTargetStreamer {
public:
explicit WebAssemblyTargetWasmStreamer(MCStreamer &S);
- void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
- void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override;
- void emitLocal(ArrayRef<MVT> Types) override;
+ void emitLocal(ArrayRef<wasm::ValType> Types) override;
void emitEndFunc() override;
- void emitIndirectFunctionType(MCSymbol *Symbol,
- SmallVectorImpl<MVT> &Params,
- SmallVectorImpl<MVT> &Results) override;
+ void emitFunctionType(const MCSymbolWasm *Sym) override {}
void emitIndIdx(const MCExpr *Value) override;
- void emitGlobalImport(StringRef name) override;
- void emitImportModule(MCSymbolWasm *Sym, StringRef ModuleName) override;
+ void emitGlobalType(const MCSymbolWasm *Sym) override {}
+ void emitEventType(const MCSymbolWasm *Sym) override {}
+ void emitImportModule(const MCSymbolWasm *Sym,
+ StringRef ModuleName) override {}
+};
+
+/// This part is for null output
+class WebAssemblyTargetNullStreamer final : public WebAssemblyTargetStreamer {
+public:
+ explicit WebAssemblyTargetNullStreamer(MCStreamer &S)
+ : WebAssemblyTargetStreamer(S) {}
+
+ void emitLocal(ArrayRef<wasm::ValType>) override {}
+ void emitEndFunc() override {}
+ void emitFunctionType(const MCSymbolWasm *) override {}
+ void emitIndIdx(const MCExpr *) override {}
+ void emitGlobalType(const MCSymbolWasm *) override {}
+ void emitEventType(const MCSymbolWasm *) override {}
+ void emitImportModule(const MCSymbolWasm *, StringRef) override {}
};
} // end namespace llvm
diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
index 4fb12d40b01b..763e30be8e02 100644
--- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
+++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
@@ -81,16 +81,23 @@ static const MCSection *GetFixupSection(const MCExpr *Expr) {
return nullptr;
}
-unsigned
-WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
- const MCFixup &Fixup) const {
+static bool IsGlobalType(const MCValue &Target) {
+ const MCSymbolRefExpr *RefA = Target.getSymA();
+ return RefA && RefA->getKind() == MCSymbolRefExpr::VK_WebAssembly_GLOBAL;
+}
+
+static bool IsEventType(const MCValue &Target) {
+ const MCSymbolRefExpr *RefA = Target.getSymA();
+ return RefA && RefA->getKind() == MCSymbolRefExpr::VK_WebAssembly_EVENT;
+}
+
+unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
+ const MCFixup &Fixup) const {
// WebAssembly functions are not allocated in the data address space. To
// resolve a pointer to a function, we must use a special relocation type.
bool IsFunction = IsFunctionExpr(Fixup.getValue());
switch (unsigned(Fixup.getKind())) {
- case WebAssembly::fixup_code_global_index:
- return wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB;
case WebAssembly::fixup_code_sleb128_i32:
if (IsFunction)
return wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB;
@@ -98,10 +105,14 @@ WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
case WebAssembly::fixup_code_sleb128_i64:
llvm_unreachable("fixup_sleb128_i64 not implemented yet");
case WebAssembly::fixup_code_uleb128_i32:
+ if (IsGlobalType(Target))
+ return wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB;
if (IsFunctionType(Target))
return wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB;
if (IsFunction)
return wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB;
+ if (IsEventType(Target))
+ return wasm::R_WEBASSEMBLY_EVENT_INDEX_LEB;
return wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB;
case FK_Data_4:
if (IsFunction)
diff --git a/lib/Target/WebAssembly/README.txt b/lib/Target/WebAssembly/README.txt
index ef0099f07efb..a154b4bf7ea8 100644
--- a/lib/Target/WebAssembly/README.txt
+++ b/lib/Target/WebAssembly/README.txt
@@ -94,10 +94,10 @@ WebAssemblyTargetLowering.
//===---------------------------------------------------------------------===//
Instead of the OptimizeReturned pass, which should consider preserving the
-"returned" attribute through to MachineInstrs and extending the StoreResults
-pass to do this optimization on calls too. That would also let the
-WebAssemblyPeephole pass clean up dead defs for such calls, as it does for
-stores.
+"returned" attribute through to MachineInstrs and extending the
+MemIntrinsicResults pass to do this optimization on calls too. That would also
+let the WebAssemblyPeephole pass clean up dead defs for such calls, as it does
+for stores.
//===---------------------------------------------------------------------===//
@@ -120,8 +120,8 @@ code like this:
It could be done with a smaller encoding like this:
i32.const $push5=, 0
- tee_local $push6=, $4=, $pop5
- copy_local $3=, $pop6
+ local.tee $push6=, $4=, $pop5
+ local.copy $3=, $pop6
//===---------------------------------------------------------------------===//
@@ -180,11 +180,11 @@ floating-point constants.
//===---------------------------------------------------------------------===//
The function @dynamic_alloca_redzone in test/CodeGen/WebAssembly/userstack.ll
-ends up with a tee_local in its prolog which has an unused result, requiring
+ends up with a local.tee in its prolog which has an unused result, requiring
an extra drop:
- get_global $push8=, 0
- tee_local $push9=, 1, $pop8
+ global.get $push8=, 0
+ local.tee $push9=, 1, $pop8
drop $pop9
[...]
diff --git a/lib/Target/WebAssembly/WebAssembly.h b/lib/Target/WebAssembly/WebAssembly.h
index 05b7b21fb597..45145c0a6527 100644
--- a/lib/Target/WebAssembly/WebAssembly.h
+++ b/lib/Target/WebAssembly/WebAssembly.h
@@ -39,10 +39,11 @@ FunctionPass *createWebAssemblyArgumentMove();
FunctionPass *createWebAssemblySetP2AlignOperands();
// Late passes.
+FunctionPass *createWebAssemblyEHRestoreStackPointer();
FunctionPass *createWebAssemblyReplacePhysRegs();
FunctionPass *createWebAssemblyPrepareForLiveIntervals();
FunctionPass *createWebAssemblyOptimizeLiveIntervals();
-FunctionPass *createWebAssemblyStoreResults();
+FunctionPass *createWebAssemblyMemIntrinsicResults();
FunctionPass *createWebAssemblyRegStackify();
FunctionPass *createWebAssemblyRegColoring();
FunctionPass *createWebAssemblyExplicitLocals();
@@ -63,10 +64,11 @@ void initializeFixFunctionBitcastsPass(PassRegistry &);
void initializeOptimizeReturnedPass(PassRegistry &);
void initializeWebAssemblyArgumentMovePass(PassRegistry &);
void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &);
+void initializeWebAssemblyEHRestoreStackPointerPass(PassRegistry &);
void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &);
void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &);
void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &);
-void initializeWebAssemblyStoreResultsPass(PassRegistry &);
+void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &);
void initializeWebAssemblyRegStackifyPass(PassRegistry &);
void initializeWebAssemblyRegColoringPass(PassRegistry &);
void initializeWebAssemblyExplicitLocalsPass(PassRegistry &);
diff --git a/lib/Target/WebAssembly/WebAssembly.td b/lib/Target/WebAssembly/WebAssembly.td
index 2f301da8e422..6b218f8aa880 100644
--- a/lib/Target/WebAssembly/WebAssembly.td
+++ b/lib/Target/WebAssembly/WebAssembly.td
@@ -23,8 +23,15 @@ include "llvm/Target/Target.td"
// WebAssembly Subtarget features.
//===----------------------------------------------------------------------===//
-def FeatureSIMD128 : SubtargetFeature<"simd128", "HasSIMD128", "true",
+def FeatureSIMD128 : SubtargetFeature<"simd128", "SIMDLevel", "SIMD128",
"Enable 128-bit SIMD">;
+
+def FeatureUnimplementedSIMD128 :
+ SubtargetFeature<"unimplemented-simd128",
+ "SIMDLevel", "UnimplementedSIMD128",
+ "Enable 128-bit SIMD not yet implemented in engines",
+ [FeatureSIMD128]>;
+
def FeatureAtomics : SubtargetFeature<"atomics", "HasAtomics", "true",
"Enable Atomics">;
def FeatureNontrappingFPToInt :
@@ -71,7 +78,8 @@ def : ProcessorModel<"generic", NoSchedModel, []>;
// Latest and greatest experimental version of WebAssembly. Bugs included!
def : ProcessorModel<"bleeding-edge", NoSchedModel,
- [FeatureSIMD128, FeatureAtomics]>;
+ [FeatureSIMD128, FeatureAtomics,
+ FeatureNontrappingFPToInt, FeatureSignExt]>;
//===----------------------------------------------------------------------===//
// Target Declaration
diff --git a/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp b/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp
index 4af9cd150bf7..e49e2b67f435 100644
--- a/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp
@@ -24,10 +24,10 @@
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
-#include "llvm/Transforms/Utils/ModuleUtils.h"
-#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
#define DEBUG_TYPE "wasm-add-missing-prototypes"
@@ -60,16 +60,17 @@ ModulePass *llvm::createWebAssemblyAddMissingPrototypes() {
}
bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) {
- LLVM_DEBUG(dbgs() << "runnning AddMissingPrototypes\n");
+ LLVM_DEBUG(dbgs() << "********** Add Missing Prototypes **********\n");
- std::vector<std::pair<Function*, Function*>> Replacements;
+ std::vector<std::pair<Function *, Function *>> Replacements;
// Find all the prototype-less function declarations
for (Function &F : M) {
if (!F.isDeclaration() || !F.hasFnAttribute("no-prototype"))
continue;
- LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F.getName() << "\n");
+ LLVM_DEBUG(dbgs() << "Found no-prototype function: " << F.getName()
+ << "\n");
// When clang emits prototype-less C functions it uses (...), i.e. varargs
// function that take no arguments (have no sentinel). When we see a
@@ -83,23 +84,29 @@ bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) {
"Functions with 'no-prototype' attribute should not have params: " +
F.getName());
-
// Create a function prototype based on the first call site (first bitcast)
// that we find.
FunctionType *NewType = nullptr;
- Function* NewF = nullptr;
+ Function *NewF = nullptr;
for (Use &U : F.uses()) {
LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n");
- if (BitCastOperator *BC = dyn_cast<BitCastOperator>(U.getUser())) {
- FunctionType *DestType =
- cast<FunctionType>(BC->getDestTy()->getPointerElementType());
-
- // Create a new function with the correct type
- NewType = DestType;
- NewF = Function::Create(NewType, F.getLinkage(), F.getName());
- NewF->setAttributes(F.getAttributes());
- NewF->removeFnAttr("no-prototype");
- break;
+ if (auto *BC = dyn_cast<BitCastOperator>(U.getUser())) {
+ if (auto *DestType = dyn_cast<FunctionType>(
+ BC->getDestTy()->getPointerElementType())) {
+ if (!NewType) {
+ // Create a new function with the correct type
+ NewType = DestType;
+ NewF = Function::Create(NewType, F.getLinkage(), F.getName());
+ NewF->setAttributes(F.getAttributes());
+ NewF->removeFnAttr("no-prototype");
+ } else {
+ if (NewType != DestType) {
+ report_fatal_error("Prototypeless function used with "
+ "conflicting signatures: " +
+ F.getName());
+ }
+ }
+ }
}
}
@@ -110,32 +117,42 @@ bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) {
continue;
}
- for (Use &U : F.uses()) {
- if (BitCastOperator *BC = dyn_cast<BitCastOperator>(U.getUser())) {
- FunctionType *DestType =
- cast<FunctionType>(BC->getDestTy()->getPointerElementType());
- if (NewType != DestType) {
- report_fatal_error(
- "Prototypeless function used with conflicting signatures: " +
- F.getName());
- }
- BC->replaceAllUsesWith(NewF);
- Replacements.emplace_back(&F, NewF);
- } else {
- dbgs() << *U.getUser()->getType() << "\n";
+ SmallVector<Instruction *, 4> DeadInsts;
+
+ for (Use &US : F.uses()) {
+ User *U = US.getUser();
+ if (auto *BC = dyn_cast<BitCastOperator>(U)) {
+ if (auto *Inst = dyn_cast<BitCastInst>(U)) {
+ // Replace with a new bitcast
+ IRBuilder<> Builder(Inst);
+ Value *NewCast = Builder.CreatePointerCast(NewF, BC->getDestTy());
+ Inst->replaceAllUsesWith(NewCast);
+ DeadInsts.push_back(Inst);
+ } else if (auto *Const = dyn_cast<ConstantExpr>(U)) {
+ Constant *NewConst =
+ ConstantExpr::getPointerCast(NewF, BC->getDestTy());
+ Const->replaceAllUsesWith(NewConst);
+ } else {
+ dbgs() << *U->getType() << "\n";
#ifndef NDEBUG
- U.getUser()->dump();
+ U->dump();
#endif
- report_fatal_error(
- "unexpected use of prototypeless function: " + F.getName() + "\n");
+ report_fatal_error("unexpected use of prototypeless function: " +
+ F.getName() + "\n");
+ }
}
}
+
+ for (auto I : DeadInsts)
+ I->eraseFromParent();
+ Replacements.emplace_back(&F, NewF);
}
+
// Finally replace the old function declarations with the new ones
for (auto &Pair : Replacements) {
- Function* Old = Pair.first;
- Function* New = Pair.second;
+ Function *Old = Pair.first;
+ Function *New = Pair.second;
Old->eraseFromParent();
M.getFunctionList().push_back(New);
}
diff --git a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 1f280e1d13fc..c4f03dfa7f9e 100644
--- a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -50,7 +50,7 @@ MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const {
const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo();
const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16,
- MVT::v4i32, MVT::v4f32})
+ MVT::v4i32, MVT::v2i64, MVT::v4f32, MVT::v2f64})
if (TRI->isTypeLegalForClass(*TRC, T))
return T;
LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo);
@@ -78,24 +78,45 @@ WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
//===----------------------------------------------------------------------===//
void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
+ for (auto &It : OutContext.getSymbols()) {
+ // Emit a .globaltype and .eventtype declaration.
+ auto Sym = cast<MCSymbolWasm>(It.getValue());
+ if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL)
+ getTargetStreamer()->emitGlobalType(Sym);
+ else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT)
+ getTargetStreamer()->emitEventType(Sym);
+ }
+
for (const auto &F : M) {
// Emit function type info for all undefined functions
if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
SmallVector<MVT, 4> Results;
SmallVector<MVT, 4> Params;
- ComputeSignatureVTs(F, TM, Params, Results);
- MCSymbol *Sym = getSymbol(&F);
- getTargetStreamer()->emitIndirectFunctionType(Sym, Params, Results);
+ ComputeSignatureVTs(F.getFunctionType(), F, TM, Params, Results);
+ auto *Sym = cast<MCSymbolWasm>(getSymbol(&F));
+ Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ if (!Sym->getSignature()) {
+ auto Signature = SignatureFromMVTs(Results, Params);
+ Sym->setSignature(Signature.get());
+ addSignature(std::move(Signature));
+ }
+ // FIXME: this was originally intended for post-linking and was only used
+ // for imports that were only called indirectly (i.e. s2wasm could not
+ // infer the type from a call). With object files it applies to all
+ // imports. so fix the names and the tests, or rethink how import
+ // delcarations work in asm files.
+ getTargetStreamer()->emitFunctionType(Sym);
if (TM.getTargetTriple().isOSBinFormatWasm() &&
F.hasFnAttribute("wasm-import-module")) {
- MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym);
- StringRef Name = F.getFnAttribute("wasm-import-module")
- .getValueAsString();
- getTargetStreamer()->emitImportModule(WasmSym, Name);
+ StringRef Name =
+ F.getFnAttribute("wasm-import-module").getValueAsString();
+ Sym->setModuleName(Name);
+ getTargetStreamer()->emitImportModule(Sym, Name);
}
}
}
+
for (const auto &G : M.globals()) {
if (!G.hasInitializer() && G.hasExternalLinkage()) {
if (G.getValueType()->isSized()) {
@@ -137,10 +158,18 @@ void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
}
void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
- getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams());
-
- SmallVector<MVT, 4> ResultVTs;
const Function &F = MF->getFunction();
+ SmallVector<MVT, 1> ResultVTs;
+ SmallVector<MVT, 4> ParamVTs;
+ ComputeSignatureVTs(F.getFunctionType(), F, TM, ParamVTs, ResultVTs);
+ auto Signature = SignatureFromMVTs(ResultVTs, ParamVTs);
+ auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym);
+ WasmSym->setSignature(Signature.get());
+ addSignature(std::move(Signature));
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+
+ // FIXME: clean up how params and results are emitted (use signatures)
+ getTargetStreamer()->emitFunctionType(WasmSym);
// Emit the function index.
if (MDNode *Idx = F.getMetadata("wasm.index")) {
@@ -150,16 +179,9 @@ void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
}
- ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
-
- // If the return type needs to be legalized it will get converted into
- // passing a pointer.
- if (ResultVTs.size() == 1)
- getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs);
- else
- getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>());
-
- getTargetStreamer()->emitLocal(MFI->getLocals());
+ SmallVector<wasm::ValType, 16> Locals;
+ ValTypesFromMVTs(MFI->getLocals(), Locals);
+ getTargetStreamer()->emitLocal(Locals);
AsmPrinter::EmitFunctionBodyStart();
}
@@ -168,42 +190,63 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
switch (MI->getOpcode()) {
- case WebAssembly::ARGUMENT_I32:
- case WebAssembly::ARGUMENT_I64:
- case WebAssembly::ARGUMENT_F32:
- case WebAssembly::ARGUMENT_F64:
+ case WebAssembly::ARGUMENT_i32:
+ case WebAssembly::ARGUMENT_i32_S:
+ case WebAssembly::ARGUMENT_i64:
+ case WebAssembly::ARGUMENT_i64_S:
+ case WebAssembly::ARGUMENT_f32:
+ case WebAssembly::ARGUMENT_f32_S:
+ case WebAssembly::ARGUMENT_f64:
+ case WebAssembly::ARGUMENT_f64_S:
case WebAssembly::ARGUMENT_v16i8:
+ case WebAssembly::ARGUMENT_v16i8_S:
case WebAssembly::ARGUMENT_v8i16:
+ case WebAssembly::ARGUMENT_v8i16_S:
case WebAssembly::ARGUMENT_v4i32:
+ case WebAssembly::ARGUMENT_v4i32_S:
+ case WebAssembly::ARGUMENT_v2i64:
+ case WebAssembly::ARGUMENT_v2i64_S:
case WebAssembly::ARGUMENT_v4f32:
+ case WebAssembly::ARGUMENT_v4f32_S:
+ case WebAssembly::ARGUMENT_v2f64:
+ case WebAssembly::ARGUMENT_v2f64_S:
// These represent values which are live into the function entry, so there's
// no instruction to emit.
break;
case WebAssembly::FALLTHROUGH_RETURN_I32:
+ case WebAssembly::FALLTHROUGH_RETURN_I32_S:
case WebAssembly::FALLTHROUGH_RETURN_I64:
+ case WebAssembly::FALLTHROUGH_RETURN_I64_S:
case WebAssembly::FALLTHROUGH_RETURN_F32:
+ case WebAssembly::FALLTHROUGH_RETURN_F32_S:
case WebAssembly::FALLTHROUGH_RETURN_F64:
+ case WebAssembly::FALLTHROUGH_RETURN_F64_S:
case WebAssembly::FALLTHROUGH_RETURN_v16i8:
+ case WebAssembly::FALLTHROUGH_RETURN_v16i8_S:
case WebAssembly::FALLTHROUGH_RETURN_v8i16:
+ case WebAssembly::FALLTHROUGH_RETURN_v8i16_S:
case WebAssembly::FALLTHROUGH_RETURN_v4i32:
- case WebAssembly::FALLTHROUGH_RETURN_v4f32: {
+ case WebAssembly::FALLTHROUGH_RETURN_v4i32_S:
+ case WebAssembly::FALLTHROUGH_RETURN_v2i64:
+ case WebAssembly::FALLTHROUGH_RETURN_v2i64_S:
+ case WebAssembly::FALLTHROUGH_RETURN_v4f32:
+ case WebAssembly::FALLTHROUGH_RETURN_v4f32_S:
+ case WebAssembly::FALLTHROUGH_RETURN_v2f64:
+ case WebAssembly::FALLTHROUGH_RETURN_v2f64_S: {
// These instructions represent the implicit return at the end of a
- // function body. The operand is always a pop.
- assert(MFI->isVRegStackified(MI->getOperand(0).getReg()));
-
+ // function body. Always pops one value off the stack.
if (isVerbose()) {
- OutStreamer->AddComment("fallthrough-return: $pop" +
- Twine(MFI->getWARegStackId(
- MFI->getWAReg(MI->getOperand(0).getReg()))));
+ OutStreamer->AddComment("fallthrough-return-value");
OutStreamer->AddBlankLine();
}
break;
}
case WebAssembly::FALLTHROUGH_RETURN_VOID:
+ case WebAssembly::FALLTHROUGH_RETURN_VOID_S:
// This instruction represents the implicit return at the end of a
// function body with no return value.
if (isVerbose()) {
- OutStreamer->AddComment("fallthrough-return");
+ OutStreamer->AddComment("fallthrough-return-void");
OutStreamer->AddBlankLine();
}
break;
@@ -244,6 +287,9 @@ bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
OS << MO.getImm();
return false;
case MachineOperand::MO_Register:
+ // FIXME: only opcode that still contains registers, as required by
+ // MachineInstr::getDebugVariable().
+ assert(MI->getOpcode() == WebAssembly::INLINEASM);
OS << regToString(MO);
return false;
case MachineOperand::MO_GlobalAddress:
diff --git a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h
index 23817b4e5126..f6cb5610bad3 100644
--- a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h
+++ b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h
@@ -25,18 +25,23 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : public AsmPrinter {
const WebAssemblySubtarget *Subtarget;
const MachineRegisterInfo *MRI;
WebAssemblyFunctionInfo *MFI;
+ // TODO: Do the uniquing of Signatures here instead of ObjectFileWriter?
+ std::vector<std::unique_ptr<wasm::WasmSignature>> Signatures;
public:
explicit WebAssemblyAsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
- : AsmPrinter(TM, std::move(Streamer)),
- Subtarget(nullptr), MRI(nullptr), MFI(nullptr) {}
+ : AsmPrinter(TM, std::move(Streamer)), Subtarget(nullptr), MRI(nullptr),
+ MFI(nullptr) {}
StringRef getPassName() const override {
return "WebAssembly Assembly Printer";
}
const WebAssemblySubtarget &getSubtarget() const { return *Subtarget; }
+ void addSignature(std::unique_ptr<wasm::WasmSignature> &&Sig) {
+ Signatures.push_back(std::move(Sig));
+ }
//===------------------------------------------------------------------===//
// MachineFunctionPass Implementation.
diff --git a/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp b/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp
index 267a51433cd1..fc827e9d5780 100644
--- a/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp
@@ -11,14 +11,15 @@
/// This file implements a CFG sorting pass.
///
/// This pass reorders the blocks in a function to put them into topological
-/// order, ignoring loop backedges, and without any loop being interrupted
-/// by a block not dominated by the loop header, with special care to keep the
-/// order as similar as possible to the original order.
+/// order, ignoring loop backedges, and without any loop or exception being
+/// interrupted by a block not dominated by the its header, with special care
+/// to keep the order as similar as possible to the original order.
///
////===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssembly.h"
+#include "WebAssemblyExceptionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
#include "llvm/ADT/PriorityQueue.h"
@@ -35,6 +36,73 @@ using namespace llvm;
#define DEBUG_TYPE "wasm-cfg-sort"
namespace {
+
+// Wrapper for loops and exceptions
+class Region {
+public:
+ virtual ~Region() = default;
+ virtual MachineBasicBlock *getHeader() const = 0;
+ virtual bool contains(const MachineBasicBlock *MBB) const = 0;
+ virtual unsigned getNumBlocks() const = 0;
+ using block_iterator = typename ArrayRef<MachineBasicBlock *>::const_iterator;
+ virtual iterator_range<block_iterator> blocks() const = 0;
+ virtual bool isLoop() const = 0;
+};
+
+template <typename T> class ConcreteRegion : public Region {
+ const T *Region;
+
+public:
+ ConcreteRegion(const T *Region) : Region(Region) {}
+ MachineBasicBlock *getHeader() const override { return Region->getHeader(); }
+ bool contains(const MachineBasicBlock *MBB) const override {
+ return Region->contains(MBB);
+ }
+ unsigned getNumBlocks() const override { return Region->getNumBlocks(); }
+ iterator_range<block_iterator> blocks() const override {
+ return Region->blocks();
+ }
+ bool isLoop() const override { return false; }
+};
+
+template <> bool ConcreteRegion<MachineLoop>::isLoop() const { return true; }
+
+// This class has information of nested Regions; this is analogous to what
+// LoopInfo is for loops.
+class RegionInfo {
+ const MachineLoopInfo &MLI;
+ const WebAssemblyExceptionInfo &WEI;
+ std::vector<const Region *> Regions;
+ DenseMap<const MachineLoop *, std::unique_ptr<Region>> LoopMap;
+ DenseMap<const WebAssemblyException *, std::unique_ptr<Region>> ExceptionMap;
+
+public:
+ RegionInfo(const MachineLoopInfo &MLI, const WebAssemblyExceptionInfo &WEI)
+ : MLI(MLI), WEI(WEI) {}
+
+ // Returns a smallest loop or exception that contains MBB
+ const Region *getRegionFor(const MachineBasicBlock *MBB) {
+ const auto *ML = MLI.getLoopFor(MBB);
+ const auto *WE = WEI.getExceptionFor(MBB);
+ if (!ML && !WE)
+ return nullptr;
+ if ((ML && !WE) || (ML && WE && ML->getNumBlocks() < WE->getNumBlocks())) {
+ // If the smallest region containing MBB is a loop
+ if (LoopMap.count(ML))
+ return LoopMap[ML].get();
+ LoopMap[ML] = llvm::make_unique<ConcreteRegion<MachineLoop>>(ML);
+ return LoopMap[ML].get();
+ } else {
+ // If the smallest region containing MBB is an exception
+ if (ExceptionMap.count(WE))
+ return ExceptionMap[WE].get();
+ ExceptionMap[WE] =
+ llvm::make_unique<ConcreteRegion<WebAssemblyException>>(WE);
+ return ExceptionMap[WE].get();
+ }
+ }
+};
+
class WebAssemblyCFGSort final : public MachineFunctionPass {
StringRef getPassName() const override { return "WebAssembly CFG Sort"; }
@@ -44,6 +112,8 @@ class WebAssemblyCFGSort final : public MachineFunctionPass {
AU.addPreserved<MachineDominatorTree>();
AU.addRequired<MachineLoopInfo>();
AU.addPreserved<MachineLoopInfo>();
+ AU.addRequired<WebAssemblyExceptionInfo>();
+ AU.addPreserved<WebAssemblyExceptionInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}
@@ -81,10 +151,48 @@ static void MaybeUpdateTerminator(MachineBasicBlock *MBB) {
}
namespace {
+// EH pads are selected first regardless of the block comparison order.
+// When only one of the BBs is an EH pad, we give a higher priority to it, to
+// prevent common mismatches between possibly throwing calls and ehpads they
+// unwind to, as in the example below:
+//
+// bb0:
+// call @foo // If this throws, unwind to bb2
+// bb1:
+// call @bar // If this throws, unwind to bb3
+// bb2 (ehpad):
+// handler_bb2
+// bb3 (ehpad):
+// handler_bb3
+// continuing code
+//
+// Because this pass tries to preserve the original BB order, this order will
+// not change. But this will result in this try-catch structure in CFGStackify,
+// resulting in a mismatch:
+// try
+// try
+// call @foo
+// call @bar // This should unwind to bb3, not bb2!
+// catch
+// handler_bb2
+// end
+// catch
+// handler_bb3
+// end
+// continuing code
+//
+// If we give a higher priority to an EH pad whenever it is ready in this
+// example, when both bb1 and bb2 are ready, we would pick up bb2 first.
+
/// Sort blocks by their number.
struct CompareBlockNumbers {
bool operator()(const MachineBasicBlock *A,
const MachineBasicBlock *B) const {
+ if (A->isEHPad() && !B->isEHPad())
+ return false;
+ if (!A->isEHPad() && B->isEHPad())
+ return true;
+
return A->getNumber() > B->getNumber();
}
};
@@ -92,29 +200,36 @@ struct CompareBlockNumbers {
struct CompareBlockNumbersBackwards {
bool operator()(const MachineBasicBlock *A,
const MachineBasicBlock *B) const {
+ // We give a higher priority to an EH pad
+ if (A->isEHPad() && !B->isEHPad())
+ return false;
+ if (!A->isEHPad() && B->isEHPad())
+ return true;
+
return A->getNumber() < B->getNumber();
}
};
-/// Bookkeeping for a loop to help ensure that we don't mix blocks not dominated
-/// by the loop header among the loop's blocks.
+/// Bookkeeping for a region to help ensure that we don't mix blocks not
+/// dominated by the its header among its blocks.
struct Entry {
- const MachineLoop *Loop;
+ const Region *TheRegion;
unsigned NumBlocksLeft;
/// List of blocks not dominated by Loop's header that are deferred until
/// after all of Loop's blocks have been seen.
std::vector<MachineBasicBlock *> Deferred;
- explicit Entry(const MachineLoop *L)
- : Loop(L), NumBlocksLeft(L->getNumBlocks()) {}
+ explicit Entry(const class Region *R)
+ : TheRegion(R), NumBlocksLeft(R->getNumBlocks()) {}
};
} // end anonymous namespace
-/// Sort the blocks, taking special care to make sure that loops are not
+/// Sort the blocks, taking special care to make sure that regions are not
/// interrupted by blocks not dominated by their header.
/// TODO: There are many opportunities for improving the heuristics here.
/// Explore them.
static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
+ const WebAssemblyExceptionInfo &WEI,
const MachineDominatorTree &MDT) {
// Prepare for a topological sort: Record the number of predecessors each
// block has, ignoring loop backedges.
@@ -131,35 +246,39 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
}
// Topological sort the CFG, with additional constraints:
- // - Between a loop header and the last block in the loop, there can be
- // no blocks not dominated by the loop header.
+ // - Between a region header and the last block in the region, there can be
+ // no blocks not dominated by its header.
// - It's desirable to preserve the original block order when possible.
// We use two ready lists; Preferred and Ready. Preferred has recently
// processed successors, to help preserve block sequences from the original
- // order. Ready has the remaining ready blocks.
+ // order. Ready has the remaining ready blocks. EH blocks are picked first
+ // from both queues.
PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>,
CompareBlockNumbers>
Preferred;
PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>,
CompareBlockNumbersBackwards>
Ready;
- SmallVector<Entry, 4> Loops;
+
+ RegionInfo SUI(MLI, WEI);
+ SmallVector<Entry, 4> Entries;
for (MachineBasicBlock *MBB = &MF.front();;) {
- const MachineLoop *L = MLI.getLoopFor(MBB);
- if (L) {
- // If MBB is a loop header, add it to the active loop list. We can't put
- // any blocks that it doesn't dominate until we see the end of the loop.
- if (L->getHeader() == MBB)
- Loops.push_back(Entry(L));
- // For each active loop the block is in, decrement the count. If MBB is
- // the last block in an active loop, take it off the list and pick up any
- // blocks deferred because the header didn't dominate them.
- for (Entry &E : Loops)
- if (E.Loop->contains(MBB) && --E.NumBlocksLeft == 0)
+ const Region *R = SUI.getRegionFor(MBB);
+ if (R) {
+ // If MBB is a region header, add it to the active region list. We can't
+ // put any blocks that it doesn't dominate until we see the end of the
+ // region.
+ if (R->getHeader() == MBB)
+ Entries.push_back(Entry(R));
+ // For each active region the block is in, decrement the count. If MBB is
+ // the last block in an active region, take it off the list and pick up
+ // any blocks deferred because the header didn't dominate them.
+ for (Entry &E : Entries)
+ if (E.TheRegion->contains(MBB) && --E.NumBlocksLeft == 0)
for (auto DeferredBlock : E.Deferred)
Ready.push(DeferredBlock);
- while (!Loops.empty() && Loops.back().NumBlocksLeft == 0)
- Loops.pop_back();
+ while (!Entries.empty() && Entries.back().NumBlocksLeft == 0)
+ Entries.pop_back();
}
// The main topological sort logic.
for (MachineBasicBlock *Succ : MBB->successors()) {
@@ -177,19 +296,19 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
while (!Preferred.empty()) {
Next = Preferred.top();
Preferred.pop();
- // If X isn't dominated by the top active loop header, defer it until that
- // loop is done.
- if (!Loops.empty() &&
- !MDT.dominates(Loops.back().Loop->getHeader(), Next)) {
- Loops.back().Deferred.push_back(Next);
+ // If X isn't dominated by the top active region header, defer it until
+ // that region is done.
+ if (!Entries.empty() &&
+ !MDT.dominates(Entries.back().TheRegion->getHeader(), Next)) {
+ Entries.back().Deferred.push_back(Next);
Next = nullptr;
continue;
}
// If Next was originally ordered before MBB, and it isn't because it was
// loop-rotated above the header, it's not preferred.
if (Next->getNumber() < MBB->getNumber() &&
- (!L || !L->contains(Next) ||
- L->getHeader()->getNumber() < Next->getNumber())) {
+ (!R || !R->contains(Next) ||
+ R->getHeader()->getNumber() < Next->getNumber())) {
Ready.push(Next);
Next = nullptr;
continue;
@@ -207,11 +326,11 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
for (;;) {
Next = Ready.top();
Ready.pop();
- // If Next isn't dominated by the top active loop header, defer it until
- // that loop is done.
- if (!Loops.empty() &&
- !MDT.dominates(Loops.back().Loop->getHeader(), Next)) {
- Loops.back().Deferred.push_back(Next);
+ // If Next isn't dominated by the top active region header, defer it
+ // until that region is done.
+ if (!Entries.empty() &&
+ !MDT.dominates(Entries.back().TheRegion->getHeader(), Next)) {
+ Entries.back().Deferred.push_back(Next);
continue;
}
break;
@@ -222,11 +341,11 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
MaybeUpdateTerminator(MBB);
MBB = Next;
}
- assert(Loops.empty() && "Active loop list not finished");
+ assert(Entries.empty() && "Active sort region list not finished");
MF.RenumberBlocks();
#ifndef NDEBUG
- SmallSetVector<MachineLoop *, 8> OnStack;
+ SmallSetVector<const Region *, 8> OnStack;
// Insert a sentinel representing the degenerate loop that starts at the
// function entry block and includes the entire function as a "loop" that
@@ -235,29 +354,39 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
for (auto &MBB : MF) {
assert(MBB.getNumber() >= 0 && "Renumbered blocks should be non-negative.");
+ const Region *Region = SUI.getRegionFor(&MBB);
+
+ if (Region && &MBB == Region->getHeader()) {
+ if (Region->isLoop()) {
+ // Loop header. The loop predecessor should be sorted above, and the
+ // other predecessors should be backedges below.
+ for (auto Pred : MBB.predecessors())
+ assert(
+ (Pred->getNumber() < MBB.getNumber() || Region->contains(Pred)) &&
+ "Loop header predecessors must be loop predecessors or "
+ "backedges");
+ } else {
+ // Not a loop header. All predecessors should be sorted above.
+ for (auto Pred : MBB.predecessors())
+ assert(Pred->getNumber() < MBB.getNumber() &&
+ "Non-loop-header predecessors should be topologically sorted");
+ }
+ assert(OnStack.insert(Region) &&
+ "Regions should be declared at most once.");
- MachineLoop *Loop = MLI.getLoopFor(&MBB);
- if (Loop && &MBB == Loop->getHeader()) {
- // Loop header. The loop predecessor should be sorted above, and the other
- // predecessors should be backedges below.
- for (auto Pred : MBB.predecessors())
- assert(
- (Pred->getNumber() < MBB.getNumber() || Loop->contains(Pred)) &&
- "Loop header predecessors must be loop predecessors or backedges");
- assert(OnStack.insert(Loop) && "Loops should be declared at most once.");
} else {
// Not a loop header. All predecessors should be sorted above.
for (auto Pred : MBB.predecessors())
assert(Pred->getNumber() < MBB.getNumber() &&
"Non-loop-header predecessors should be topologically sorted");
- assert(OnStack.count(MLI.getLoopFor(&MBB)) &&
- "Blocks must be nested in their loops");
+ assert(OnStack.count(SUI.getRegionFor(&MBB)) &&
+ "Blocks must be nested in their regions");
}
while (OnStack.size() > 1 && &MBB == WebAssembly::getBottom(OnStack.back()))
OnStack.pop_back();
}
assert(OnStack.pop_back_val() == nullptr &&
- "The function entry block shouldn't actually be a loop header");
+ "The function entry block shouldn't actually be a region header");
assert(OnStack.empty() &&
"Control flow stack pushes and pops should be balanced.");
#endif
@@ -269,12 +398,13 @@ bool WebAssemblyCFGSort::runOnMachineFunction(MachineFunction &MF) {
<< MF.getName() << '\n');
const auto &MLI = getAnalysis<MachineLoopInfo>();
+ const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>();
auto &MDT = getAnalysis<MachineDominatorTree>();
// Liveness is not tracked for VALUE_STACK physreg.
MF.getRegInfo().invalidateLiveness();
- // Sort the blocks, with contiguous loops.
- SortBlocks(MF, MLI, MDT);
+ // Sort the blocks, with contiguous sort regions.
+ SortBlocks(MF, MLI, WEI, MDT);
return true;
}
diff --git a/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
index 70ce40cefed7..f8f5f4040c86 100644
--- a/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
@@ -10,16 +10,21 @@
/// \file
/// This file implements a CFG stacking pass.
///
-/// This pass inserts BLOCK and LOOP markers to mark the start of scopes, since
-/// scope boundaries serve as the labels for WebAssembly's control transfers.
+/// This pass inserts BLOCK, LOOP, and TRY markers to mark the start of scopes,
+/// since scope boundaries serve as the labels for WebAssembly's control
+/// transfers.
///
/// This is sufficient to convert arbitrary CFGs into a form that works on
/// WebAssembly, provided that all loops are single-entry.
///
+/// In case we use exceptions, this pass also fixes mismatches in unwind
+/// destinations created during transforming CFG into wasm structured format.
+///
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "WebAssembly.h"
+#include "WebAssemblyExceptionInfo.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
@@ -29,6 +34,8 @@
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/WasmEHFuncInfo.h"
+#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -40,26 +47,57 @@ class WebAssemblyCFGStackify final : public MachineFunctionPass {
StringRef getPassName() const override { return "WebAssembly CFG Stackify"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesCFG();
AU.addRequired<MachineDominatorTree>();
- AU.addPreserved<MachineDominatorTree>();
AU.addRequired<MachineLoopInfo>();
- AU.addPreserved<MachineLoopInfo>();
+ AU.addRequired<WebAssemblyExceptionInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool runOnMachineFunction(MachineFunction &MF) override;
+ // For each block whose label represents the end of a scope, record the block
+ // which holds the beginning of the scope. This will allow us to quickly skip
+ // over scoped regions when walking blocks.
+ SmallVector<MachineBasicBlock *, 8> ScopeTops;
+
+ void placeMarkers(MachineFunction &MF);
+ void placeBlockMarker(MachineBasicBlock &MBB);
+ void placeLoopMarker(MachineBasicBlock &MBB);
+ void placeTryMarker(MachineBasicBlock &MBB);
+ void rewriteDepthImmediates(MachineFunction &MF);
+ void fixEndsAtEndOfFunction(MachineFunction &MF);
+
+ // For each BLOCK|LOOP|TRY, the corresponding END_(BLOCK|LOOP|TRY).
+ DenseMap<const MachineInstr *, MachineInstr *> BeginToEnd;
+ // For each END_(BLOCK|LOOP|TRY), the corresponding BLOCK|LOOP|TRY.
+ DenseMap<const MachineInstr *, MachineInstr *> EndToBegin;
+ // <TRY marker, EH pad> map
+ DenseMap<const MachineInstr *, MachineBasicBlock *> TryToEHPad;
+ // <EH pad, TRY marker> map
+ DenseMap<const MachineBasicBlock *, MachineInstr *> EHPadToTry;
+ // <LOOP|TRY marker, Loop/exception bottom BB> map
+ DenseMap<const MachineInstr *, MachineBasicBlock *> BeginToBottom;
+
+ // Helper functions to register scope information created by marker
+ // instructions.
+ void registerScope(MachineInstr *Begin, MachineInstr *End);
+ void registerTryScope(MachineInstr *Begin, MachineInstr *End,
+ MachineBasicBlock *EHPad);
+
+ MachineBasicBlock *getBottom(const MachineInstr *Begin);
+
public:
static char ID; // Pass identification, replacement for typeid
WebAssemblyCFGStackify() : MachineFunctionPass(ID) {}
+ ~WebAssemblyCFGStackify() override { releaseMemory(); }
+ void releaseMemory() override;
};
} // end anonymous namespace
char WebAssemblyCFGStackify::ID = 0;
INITIALIZE_PASS(WebAssemblyCFGStackify, DEBUG_TYPE,
- "Insert BLOCK and LOOP markers for WebAssembly scopes",
- false, false)
+ "Insert BLOCK and LOOP markers for WebAssembly scopes", false,
+ false)
FunctionPass *llvm::createWebAssemblyCFGStackify() {
return new WebAssemblyCFGStackify();
@@ -73,34 +111,121 @@ FunctionPass *llvm::createWebAssemblyCFGStackify() {
static bool ExplicitlyBranchesTo(MachineBasicBlock *Pred,
MachineBasicBlock *MBB) {
for (MachineInstr &MI : Pred->terminators())
- for (MachineOperand &MO : MI.explicit_operands())
- if (MO.isMBB() && MO.getMBB() == MBB)
- return true;
+ // Even if a rethrow takes a BB argument, it is not a branch
+ if (!WebAssembly::isRethrow(MI))
+ for (MachineOperand &MO : MI.explicit_operands())
+ if (MO.isMBB() && MO.getMBB() == MBB)
+ return true;
return false;
}
+// Returns an iterator to the earliest position possible within the MBB,
+// satisfying the restrictions given by BeforeSet and AfterSet. BeforeSet
+// contains instructions that should go before the marker, and AfterSet contains
+// ones that should go after the marker. In this function, AfterSet is only
+// used for sanity checking.
+static MachineBasicBlock::iterator
+GetEarliestInsertPos(MachineBasicBlock *MBB,
+ const SmallPtrSet<const MachineInstr *, 4> &BeforeSet,
+ const SmallPtrSet<const MachineInstr *, 4> &AfterSet) {
+ auto InsertPos = MBB->end();
+ while (InsertPos != MBB->begin()) {
+ if (BeforeSet.count(&*std::prev(InsertPos))) {
+#ifndef NDEBUG
+ // Sanity check
+ for (auto Pos = InsertPos, E = MBB->begin(); Pos != E; --Pos)
+ assert(!AfterSet.count(&*std::prev(Pos)));
+#endif
+ break;
+ }
+ --InsertPos;
+ }
+ return InsertPos;
+}
+
+// Returns an iterator to the latest position possible within the MBB,
+// satisfying the restrictions given by BeforeSet and AfterSet. BeforeSet
+// contains instructions that should go before the marker, and AfterSet contains
+// ones that should go after the marker. In this function, BeforeSet is only
+// used for sanity checking.
+static MachineBasicBlock::iterator
+GetLatestInsertPos(MachineBasicBlock *MBB,
+ const SmallPtrSet<const MachineInstr *, 4> &BeforeSet,
+ const SmallPtrSet<const MachineInstr *, 4> &AfterSet) {
+ auto InsertPos = MBB->begin();
+ while (InsertPos != MBB->end()) {
+ if (AfterSet.count(&*InsertPos)) {
+#ifndef NDEBUG
+ // Sanity check
+ for (auto Pos = InsertPos, E = MBB->end(); Pos != E; ++Pos)
+ assert(!BeforeSet.count(&*Pos));
+#endif
+ break;
+ }
+ ++InsertPos;
+ }
+ return InsertPos;
+}
+
+void WebAssemblyCFGStackify::registerScope(MachineInstr *Begin,
+ MachineInstr *End) {
+ BeginToEnd[Begin] = End;
+ EndToBegin[End] = Begin;
+}
+
+void WebAssemblyCFGStackify::registerTryScope(MachineInstr *Begin,
+ MachineInstr *End,
+ MachineBasicBlock *EHPad) {
+ registerScope(Begin, End);
+ TryToEHPad[Begin] = EHPad;
+ EHPadToTry[EHPad] = Begin;
+}
+
+// Given a LOOP/TRY marker, returns its bottom BB. Use cached information if any
+// to prevent recomputation.
+MachineBasicBlock *
+WebAssemblyCFGStackify::getBottom(const MachineInstr *Begin) {
+ const auto &MLI = getAnalysis<MachineLoopInfo>();
+ const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>();
+ if (BeginToBottom.count(Begin))
+ return BeginToBottom[Begin];
+ if (Begin->getOpcode() == WebAssembly::LOOP) {
+ MachineLoop *L = MLI.getLoopFor(Begin->getParent());
+ assert(L);
+ BeginToBottom[Begin] = WebAssembly::getBottom(L);
+ } else if (Begin->getOpcode() == WebAssembly::TRY) {
+ WebAssemblyException *WE = WEI.getExceptionFor(TryToEHPad[Begin]);
+ assert(WE);
+ BeginToBottom[Begin] = WebAssembly::getBottom(WE);
+ } else
+ assert(false);
+ return BeginToBottom[Begin];
+}
+
/// Insert a BLOCK marker for branches to MBB (if needed).
-static void PlaceBlockMarker(
- MachineBasicBlock &MBB, MachineFunction &MF,
- SmallVectorImpl<MachineBasicBlock *> &ScopeTops,
- DenseMap<const MachineInstr *, MachineInstr *> &BlockTops,
- DenseMap<const MachineInstr *, MachineInstr *> &LoopTops,
- const WebAssemblyInstrInfo &TII,
- const MachineLoopInfo &MLI,
- MachineDominatorTree &MDT,
- WebAssemblyFunctionInfo &MFI) {
+void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) {
+ // This should have been handled in placeTryMarker.
+ if (MBB.isEHPad())
+ return;
+
+ MachineFunction &MF = *MBB.getParent();
+ auto &MDT = getAnalysis<MachineDominatorTree>();
+ const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
+ const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
+
// First compute the nearest common dominator of all forward non-fallthrough
// predecessors so that we minimize the time that the BLOCK is on the stack,
// which reduces overall stack height.
MachineBasicBlock *Header = nullptr;
bool IsBranchedTo = false;
int MBBNumber = MBB.getNumber();
- for (MachineBasicBlock *Pred : MBB.predecessors())
+ for (MachineBasicBlock *Pred : MBB.predecessors()) {
if (Pred->getNumber() < MBBNumber) {
Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred;
if (ExplicitlyBranchesTo(Pred, &MBB))
IsBranchedTo = true;
}
+ }
if (!Header)
return;
if (!IsBranchedTo)
@@ -125,43 +250,93 @@ static void PlaceBlockMarker(
}
// Decide where in Header to put the BLOCK.
- MachineBasicBlock::iterator InsertPos;
- MachineLoop *HeaderLoop = MLI.getLoopFor(Header);
- if (HeaderLoop &&
- MBB.getNumber() > WebAssembly::getBottom(HeaderLoop)->getNumber()) {
- // Header is the header of a loop that does not lexically contain MBB, so
- // the BLOCK needs to be above the LOOP, after any END constructs.
- InsertPos = Header->begin();
- while (InsertPos->getOpcode() == WebAssembly::END_BLOCK ||
- InsertPos->getOpcode() == WebAssembly::END_LOOP)
- ++InsertPos;
- } else {
- // Otherwise, insert the BLOCK as late in Header as we can, but before the
- // beginning of the local expression tree and any nested BLOCKs.
- InsertPos = Header->getFirstTerminator();
- while (InsertPos != Header->begin() &&
- WebAssembly::isChild(*std::prev(InsertPos), MFI) &&
- std::prev(InsertPos)->getOpcode() != WebAssembly::LOOP &&
- std::prev(InsertPos)->getOpcode() != WebAssembly::END_BLOCK &&
- std::prev(InsertPos)->getOpcode() != WebAssembly::END_LOOP)
- --InsertPos;
+
+ // Instructions that should go before the BLOCK.
+ SmallPtrSet<const MachineInstr *, 4> BeforeSet;
+ // Instructions that should go after the BLOCK.
+ SmallPtrSet<const MachineInstr *, 4> AfterSet;
+ for (const auto &MI : *Header) {
+ // If there is a previously placed LOOP/TRY marker and the bottom block of
+ // the loop/exception is above MBB, it should be after the BLOCK, because
+ // the loop/exception is nested in this block. Otherwise it should be before
+ // the BLOCK.
+ if (MI.getOpcode() == WebAssembly::LOOP ||
+ MI.getOpcode() == WebAssembly::TRY) {
+ if (MBB.getNumber() > getBottom(&MI)->getNumber())
+ AfterSet.insert(&MI);
+#ifndef NDEBUG
+ else
+ BeforeSet.insert(&MI);
+#endif
+ }
+
+ // All previously inserted BLOCK markers should be after the BLOCK because
+ // they are all nested blocks.
+ if (MI.getOpcode() == WebAssembly::BLOCK)
+ AfterSet.insert(&MI);
+
+#ifndef NDEBUG
+ // All END_(BLOCK|LOOP|TRY) markers should be before the BLOCK.
+ if (MI.getOpcode() == WebAssembly::END_BLOCK ||
+ MI.getOpcode() == WebAssembly::END_LOOP ||
+ MI.getOpcode() == WebAssembly::END_TRY)
+ BeforeSet.insert(&MI);
+#endif
+
+ // Terminators should go after the BLOCK.
+ if (MI.isTerminator())
+ AfterSet.insert(&MI);
+ }
+
+ // Local expression tree should go after the BLOCK.
+ for (auto I = Header->getFirstTerminator(), E = Header->begin(); I != E;
+ --I) {
+ if (std::prev(I)->isDebugInstr() || std::prev(I)->isPosition())
+ continue;
+ if (WebAssembly::isChild(*std::prev(I), MFI))
+ AfterSet.insert(&*std::prev(I));
+ else
+ break;
}
// Add the BLOCK.
+ auto InsertPos = GetLatestInsertPos(Header, BeforeSet, AfterSet);
MachineInstr *Begin =
BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos),
TII.get(WebAssembly::BLOCK))
.addImm(int64_t(WebAssembly::ExprType::Void));
+ // Decide where in Header to put the END_BLOCK.
+ BeforeSet.clear();
+ AfterSet.clear();
+ for (auto &MI : MBB) {
+#ifndef NDEBUG
+ // END_BLOCK should precede existing LOOP and TRY markers.
+ if (MI.getOpcode() == WebAssembly::LOOP ||
+ MI.getOpcode() == WebAssembly::TRY)
+ AfterSet.insert(&MI);
+#endif
+
+ // If there is a previously placed END_LOOP marker and the header of the
+ // loop is above this block's header, the END_LOOP should be placed after
+ // the BLOCK, because the loop contains this block. Otherwise the END_LOOP
+ // should be placed before the BLOCK. The same for END_TRY.
+ if (MI.getOpcode() == WebAssembly::END_LOOP ||
+ MI.getOpcode() == WebAssembly::END_TRY) {
+ if (EndToBegin[&MI]->getParent()->getNumber() >= Header->getNumber())
+ BeforeSet.insert(&MI);
+#ifndef NDEBUG
+ else
+ AfterSet.insert(&MI);
+#endif
+ }
+ }
+
// Mark the end of the block.
- InsertPos = MBB.begin();
- while (InsertPos != MBB.end() &&
- InsertPos->getOpcode() == WebAssembly::END_LOOP &&
- LoopTops[&*InsertPos]->getParent()->getNumber() >= Header->getNumber())
- ++InsertPos;
+ InsertPos = GetEarliestInsertPos(&MBB, BeforeSet, AfterSet);
MachineInstr *End = BuildMI(MBB, InsertPos, MBB.findPrevDebugLoc(InsertPos),
TII.get(WebAssembly::END_BLOCK));
- BlockTops[End] = Begin;
+ registerScope(Begin, End);
// Track the farthest-spanning scope that ends at this point.
int Number = MBB.getNumber();
@@ -171,11 +346,11 @@ static void PlaceBlockMarker(
}
/// Insert a LOOP marker for a loop starting at MBB (if it's a loop header).
-static void PlaceLoopMarker(
- MachineBasicBlock &MBB, MachineFunction &MF,
- SmallVectorImpl<MachineBasicBlock *> &ScopeTops,
- DenseMap<const MachineInstr *, MachineInstr *> &LoopTops,
- const WebAssemblyInstrInfo &TII, const MachineLoopInfo &MLI) {
+void WebAssemblyCFGStackify::placeLoopMarker(MachineBasicBlock &MBB) {
+ MachineFunction &MF = *MBB.getParent();
+ const auto &MLI = getAnalysis<MachineLoopInfo>();
+ const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
+
MachineLoop *Loop = MLI.getLoopFor(&MBB);
if (!Loop || Loop->getHeader() != &MBB)
return;
@@ -193,22 +368,43 @@ static void PlaceLoopMarker(
}
MachineBasicBlock *AfterLoop = &*Iter;
- // Mark the beginning of the loop (after the end of any existing loop that
- // ends here).
- auto InsertPos = MBB.begin();
- while (InsertPos != MBB.end() &&
- InsertPos->getOpcode() == WebAssembly::END_LOOP)
- ++InsertPos;
+ // Decide where in Header to put the LOOP.
+ SmallPtrSet<const MachineInstr *, 4> BeforeSet;
+ SmallPtrSet<const MachineInstr *, 4> AfterSet;
+ for (const auto &MI : MBB) {
+ // LOOP marker should be after any existing loop that ends here. Otherwise
+ // we assume the instruction belongs to the loop.
+ if (MI.getOpcode() == WebAssembly::END_LOOP)
+ BeforeSet.insert(&MI);
+#ifndef NDEBUG
+ else
+ AfterSet.insert(&MI);
+#endif
+ }
+
+ // Mark the beginning of the loop.
+ auto InsertPos = GetEarliestInsertPos(&MBB, BeforeSet, AfterSet);
MachineInstr *Begin = BuildMI(MBB, InsertPos, MBB.findDebugLoc(InsertPos),
TII.get(WebAssembly::LOOP))
.addImm(int64_t(WebAssembly::ExprType::Void));
- // Mark the end of the loop (using arbitrary debug location that branched
- // to the loop end as its location).
+ // Decide where in Header to put the END_LOOP.
+ BeforeSet.clear();
+ AfterSet.clear();
+#ifndef NDEBUG
+ for (const auto &MI : MBB)
+ // Existing END_LOOP markers belong to parent loops of this loop
+ if (MI.getOpcode() == WebAssembly::END_LOOP)
+ AfterSet.insert(&MI);
+#endif
+
+ // Mark the end of the loop (using arbitrary debug location that branched to
+ // the loop end as its location).
+ InsertPos = GetEarliestInsertPos(AfterLoop, BeforeSet, AfterSet);
DebugLoc EndDL = (*AfterLoop->pred_rbegin())->findBranchDebugLoc();
- MachineInstr *End = BuildMI(*AfterLoop, AfterLoop->begin(), EndDL,
- TII.get(WebAssembly::END_LOOP));
- LoopTops[End] = Begin;
+ MachineInstr *End =
+ BuildMI(*AfterLoop, InsertPos, EndDL, TII.get(WebAssembly::END_LOOP));
+ registerScope(Begin, End);
assert((!ScopeTops[AfterLoop->getNumber()] ||
ScopeTops[AfterLoop->getNumber()]->getNumber() < MBB.getNumber()) &&
@@ -217,6 +413,183 @@ static void PlaceLoopMarker(
ScopeTops[AfterLoop->getNumber()] = &MBB;
}
+void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
+ if (!MBB.isEHPad())
+ return;
+
+ // catch_all terminate pad is grouped together with catch terminate pad and
+ // does not need a separate TRY and END_TRY marker.
+ if (WebAssembly::isCatchAllTerminatePad(MBB))
+ return;
+
+ MachineFunction &MF = *MBB.getParent();
+ auto &MDT = getAnalysis<MachineDominatorTree>();
+ const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
+ const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>();
+ const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
+
+ // Compute the nearest common dominator of all unwind predecessors
+ MachineBasicBlock *Header = nullptr;
+ int MBBNumber = MBB.getNumber();
+ for (auto *Pred : MBB.predecessors()) {
+ if (Pred->getNumber() < MBBNumber) {
+ Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred;
+ assert(!ExplicitlyBranchesTo(Pred, &MBB) &&
+ "Explicit branch to an EH pad!");
+ }
+ }
+ if (!Header)
+ return;
+
+ // If this try is at the bottom of the function, insert a dummy block at the
+ // end.
+ WebAssemblyException *WE = WEI.getExceptionFor(&MBB);
+ assert(WE);
+ MachineBasicBlock *Bottom = WebAssembly::getBottom(WE);
+
+ auto Iter = std::next(MachineFunction::iterator(Bottom));
+ if (Iter == MF.end()) {
+ MachineBasicBlock *Label = MF.CreateMachineBasicBlock();
+ // Give it a fake predecessor so that AsmPrinter prints its label.
+ Label->addSuccessor(Label);
+ MF.push_back(Label);
+ Iter = std::next(MachineFunction::iterator(Bottom));
+ }
+ MachineBasicBlock *AfterTry = &*Iter;
+
+ assert(AfterTry != &MF.front());
+ MachineBasicBlock *LayoutPred =
+ &*std::prev(MachineFunction::iterator(AfterTry));
+
+ // If the nearest common dominator is inside a more deeply nested context,
+ // walk out to the nearest scope which isn't more deeply nested.
+ for (MachineFunction::iterator I(LayoutPred), E(Header); I != E; --I) {
+ if (MachineBasicBlock *ScopeTop = ScopeTops[I->getNumber()]) {
+ if (ScopeTop->getNumber() > Header->getNumber()) {
+ // Skip over an intervening scope.
+ I = std::next(MachineFunction::iterator(ScopeTop));
+ } else {
+ // We found a scope level at an appropriate depth.
+ Header = ScopeTop;
+ break;
+ }
+ }
+ }
+
+ // Decide where in Header to put the TRY.
+
+ // Instructions that should go before the BLOCK.
+ SmallPtrSet<const MachineInstr *, 4> BeforeSet;
+ // Instructions that should go after the BLOCK.
+ SmallPtrSet<const MachineInstr *, 4> AfterSet;
+ for (const auto &MI : *Header) {
+ // If there is a previously placed LOOP marker and the bottom block of
+ // the loop is above MBB, the LOOP should be after the TRY, because the
+ // loop is nested in this try. Otherwise it should be before the TRY.
+ if (MI.getOpcode() == WebAssembly::LOOP) {
+ if (MBB.getNumber() > Bottom->getNumber())
+ AfterSet.insert(&MI);
+#ifndef NDEBUG
+ else
+ BeforeSet.insert(&MI);
+#endif
+ }
+
+ // All previously inserted TRY markers should be after the TRY because they
+ // are all nested trys.
+ if (MI.getOpcode() == WebAssembly::TRY)
+ AfterSet.insert(&MI);
+
+#ifndef NDEBUG
+ // All END_(LOOP/TRY) markers should be before the TRY.
+ if (MI.getOpcode() == WebAssembly::END_LOOP ||
+ MI.getOpcode() == WebAssembly::END_TRY)
+ BeforeSet.insert(&MI);
+#endif
+
+ // Terminators should go after the TRY.
+ if (MI.isTerminator())
+ AfterSet.insert(&MI);
+ }
+
+ // Local expression tree should go after the TRY.
+ for (auto I = Header->getFirstTerminator(), E = Header->begin(); I != E;
+ --I) {
+ if (std::prev(I)->isDebugInstr() || std::prev(I)->isPosition())
+ continue;
+ if (WebAssembly::isChild(*std::prev(I), MFI))
+ AfterSet.insert(&*std::prev(I));
+ else
+ break;
+ }
+
+ // If Header unwinds to MBB (= Header contains 'invoke'), the try block should
+ // contain the call within it. So the call should go after the TRY. The
+ // exception is when the header's terminator is a rethrow instruction, in
+ // which case that instruction, not a call instruction before it, is gonna
+ // throw.
+ if (MBB.isPredecessor(Header)) {
+ auto TermPos = Header->getFirstTerminator();
+ if (TermPos == Header->end() || !WebAssembly::isRethrow(*TermPos)) {
+ for (const auto &MI : reverse(*Header)) {
+ if (MI.isCall()) {
+ AfterSet.insert(&MI);
+ break;
+ }
+ }
+ }
+ }
+
+ // Add the TRY.
+ auto InsertPos = GetLatestInsertPos(Header, BeforeSet, AfterSet);
+ MachineInstr *Begin =
+ BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos),
+ TII.get(WebAssembly::TRY))
+ .addImm(int64_t(WebAssembly::ExprType::Void));
+
+ // Decide where in Header to put the END_TRY.
+ BeforeSet.clear();
+ AfterSet.clear();
+ for (const auto &MI : *AfterTry) {
+#ifndef NDEBUG
+ // END_TRY should precede existing LOOP markers.
+ if (MI.getOpcode() == WebAssembly::LOOP)
+ AfterSet.insert(&MI);
+
+ // All END_TRY markers placed earlier belong to exceptions that contains
+ // this one.
+ if (MI.getOpcode() == WebAssembly::END_TRY)
+ AfterSet.insert(&MI);
+#endif
+
+ // If there is a previously placed END_LOOP marker and its header is after
+ // where TRY marker is, this loop is contained within the 'catch' part, so
+ // the END_TRY marker should go after that. Otherwise, the whole try-catch
+ // is contained within this loop, so the END_TRY should go before that.
+ if (MI.getOpcode() == WebAssembly::END_LOOP) {
+ if (EndToBegin[&MI]->getParent()->getNumber() >= Header->getNumber())
+ BeforeSet.insert(&MI);
+#ifndef NDEBUG
+ else
+ AfterSet.insert(&MI);
+#endif
+ }
+ }
+
+ // Mark the end of the TRY.
+ InsertPos = GetEarliestInsertPos(AfterTry, BeforeSet, AfterSet);
+ MachineInstr *End =
+ BuildMI(*AfterTry, InsertPos, Bottom->findBranchDebugLoc(),
+ TII.get(WebAssembly::END_TRY));
+ registerTryScope(Begin, End, &MBB);
+
+ // Track the farthest-spanning scope that ends at this point.
+ int Number = AfterTry->getNumber();
+ if (!ScopeTops[Number] ||
+ ScopeTops[Number]->getNumber() > Header->getNumber())
+ ScopeTops[Number] = Header;
+}
+
static unsigned
GetDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack,
const MachineBasicBlock *MBB) {
@@ -237,11 +610,8 @@ GetDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack,
/// that end at the function end need to have a return type signature that
/// matches the function signature, even though it's unreachable. This function
/// checks for such cases and fixes up the signatures.
-static void FixEndsAtEndOfFunction(
- MachineFunction &MF,
- const WebAssemblyFunctionInfo &MFI,
- DenseMap<const MachineInstr *, MachineInstr *> &BlockTops,
- DenseMap<const MachineInstr *, MachineInstr *> &LoopTops) {
+void WebAssemblyCFGStackify::fixEndsAtEndOfFunction(MachineFunction &MF) {
+ const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
assert(MFI.getResults().size() <= 1);
if (MFI.getResults().empty())
@@ -249,16 +619,31 @@ static void FixEndsAtEndOfFunction(
WebAssembly::ExprType retType;
switch (MFI.getResults().front().SimpleTy) {
- case MVT::i32: retType = WebAssembly::ExprType::I32; break;
- case MVT::i64: retType = WebAssembly::ExprType::I64; break;
- case MVT::f32: retType = WebAssembly::ExprType::F32; break;
- case MVT::f64: retType = WebAssembly::ExprType::F64; break;
- case MVT::v16i8: retType = WebAssembly::ExprType::I8x16; break;
- case MVT::v8i16: retType = WebAssembly::ExprType::I16x8; break;
- case MVT::v4i32: retType = WebAssembly::ExprType::I32x4; break;
- case MVT::v4f32: retType = WebAssembly::ExprType::F32x4; break;
- case MVT::ExceptRef: retType = WebAssembly::ExprType::ExceptRef; break;
- default: llvm_unreachable("unexpected return type");
+ case MVT::i32:
+ retType = WebAssembly::ExprType::I32;
+ break;
+ case MVT::i64:
+ retType = WebAssembly::ExprType::I64;
+ break;
+ case MVT::f32:
+ retType = WebAssembly::ExprType::F32;
+ break;
+ case MVT::f64:
+ retType = WebAssembly::ExprType::F64;
+ break;
+ case MVT::v16i8:
+ case MVT::v8i16:
+ case MVT::v4i32:
+ case MVT::v2i64:
+ case MVT::v4f32:
+ case MVT::v2f64:
+ retType = WebAssembly::ExprType::V128;
+ break;
+ case MVT::ExceptRef:
+ retType = WebAssembly::ExprType::ExceptRef;
+ break;
+ default:
+ llvm_unreachable("unexpected return type");
}
for (MachineBasicBlock &MBB : reverse(MF)) {
@@ -266,11 +651,11 @@ static void FixEndsAtEndOfFunction(
if (MI.isPosition() || MI.isDebugInstr())
continue;
if (MI.getOpcode() == WebAssembly::END_BLOCK) {
- BlockTops[&MI]->getOperand(0).setImm(int32_t(retType));
+ EndToBegin[&MI]->getOperand(0).setImm(int32_t(retType));
continue;
}
if (MI.getOpcode() == WebAssembly::END_LOOP) {
- LoopTops[&MI]->getOperand(0).setImm(int32_t(retType));
+ EndToBegin[&MI]->getOperand(0).setImm(int32_t(retType));
continue;
}
// Something other than an `end`. We're done.
@@ -281,60 +666,108 @@ static void FixEndsAtEndOfFunction(
// WebAssembly functions end with an end instruction, as if the function body
// were a block.
-static void AppendEndToFunction(
- MachineFunction &MF,
- const WebAssemblyInstrInfo &TII) {
+static void AppendEndToFunction(MachineFunction &MF,
+ const WebAssemblyInstrInfo &TII) {
BuildMI(MF.back(), MF.back().end(),
MF.back().findPrevDebugLoc(MF.back().end()),
TII.get(WebAssembly::END_FUNCTION));
}
-/// Insert LOOP and BLOCK markers at appropriate places.
-static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI,
- const WebAssemblyInstrInfo &TII,
- MachineDominatorTree &MDT,
- WebAssemblyFunctionInfo &MFI) {
- // For each block whose label represents the end of a scope, record the block
- // which holds the beginning of the scope. This will allow us to quickly skip
- // over scoped regions when walking blocks. We allocate one more than the
- // number of blocks in the function to accommodate for the possible fake block
- // we may insert at the end.
- SmallVector<MachineBasicBlock *, 8> ScopeTops(MF.getNumBlockIDs() + 1);
-
- // For each LOOP_END, the corresponding LOOP.
- DenseMap<const MachineInstr *, MachineInstr *> LoopTops;
-
- // For each END_BLOCK, the corresponding BLOCK.
- DenseMap<const MachineInstr *, MachineInstr *> BlockTops;
-
- for (auto &MBB : MF) {
- // Place the LOOP for MBB if MBB is the header of a loop.
- PlaceLoopMarker(MBB, MF, ScopeTops, LoopTops, TII, MLI);
-
- // Place the BLOCK for MBB if MBB is branched to from above.
- PlaceBlockMarker(MBB, MF, ScopeTops, BlockTops, LoopTops, TII, MLI, MDT, MFI);
- }
+/// Insert LOOP/TRY/BLOCK markers at appropriate places.
+void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) {
+ const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo();
+ // We allocate one more than the number of blocks in the function to
+ // accommodate for the possible fake block we may insert at the end.
+ ScopeTops.resize(MF.getNumBlockIDs() + 1);
+ // Place the LOOP for MBB if MBB is the header of a loop.
+ for (auto &MBB : MF)
+ placeLoopMarker(MBB);
+ // Place the TRY for MBB if MBB is the EH pad of an exception.
+ if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm &&
+ MF.getFunction().hasPersonalityFn())
+ for (auto &MBB : MF)
+ placeTryMarker(MBB);
+ // Place the BLOCK for MBB if MBB is branched to from above.
+ for (auto &MBB : MF)
+ placeBlockMarker(MBB);
+}
+void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) {
+ const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
// Now rewrite references to basic blocks to be depth immediates.
+ // We need two stacks: one for normal scopes and the other for EH pad scopes.
+ // EH pad stack is used to rewrite depths in rethrow instructions.
SmallVector<const MachineBasicBlock *, 8> Stack;
+ SmallVector<const MachineBasicBlock *, 8> EHPadStack;
for (auto &MBB : reverse(MF)) {
- for (auto &MI : reverse(MBB)) {
+ for (auto I = MBB.rbegin(), E = MBB.rend(); I != E; ++I) {
+ MachineInstr &MI = *I;
switch (MI.getOpcode()) {
case WebAssembly::BLOCK:
- assert(ScopeTops[Stack.back()->getNumber()]->getNumber() <= MBB.getNumber() &&
- "Block should be balanced");
+ assert(ScopeTops[Stack.back()->getNumber()]->getNumber() <=
+ MBB.getNumber() &&
+ "Block/try should be balanced");
Stack.pop_back();
break;
+
+ case WebAssembly::TRY:
+ assert(ScopeTops[Stack.back()->getNumber()]->getNumber() <=
+ MBB.getNumber() &&
+ "Block/try marker should be balanced");
+ Stack.pop_back();
+ EHPadStack.pop_back();
+ break;
+
+ case WebAssembly::CATCH_I32:
+ case WebAssembly::CATCH_I64:
+ case WebAssembly::CATCH_ALL:
+ // Currently the only case there are more than one catch for a try is
+ // for catch terminate pad, in the form of
+ // try
+ // catch
+ // call @__clang_call_terminate
+ // unreachable
+ // catch_all
+ // call @std::terminate
+ // unreachable
+ // end
+ // So we shouldn't push the current BB for the second catch_all block
+ // here.
+ if (!WebAssembly::isCatchAllTerminatePad(MBB))
+ EHPadStack.push_back(&MBB);
+ break;
+
case WebAssembly::LOOP:
assert(Stack.back() == &MBB && "Loop top should be balanced");
Stack.pop_back();
break;
+
case WebAssembly::END_BLOCK:
+ case WebAssembly::END_TRY:
Stack.push_back(&MBB);
break;
+
case WebAssembly::END_LOOP:
- Stack.push_back(LoopTops[&MI]->getParent());
+ Stack.push_back(EndToBegin[&MI]->getParent());
+ break;
+
+ case WebAssembly::RETHROW: {
+ // Rewrite MBB operands to be depth immediates.
+ unsigned EHPadDepth = GetDepth(EHPadStack, MI.getOperand(0).getMBB());
+ MI.RemoveOperand(0);
+ MI.addOperand(MF, MachineOperand::CreateImm(EHPadDepth));
break;
+ }
+
+ case WebAssembly::RETHROW_TO_CALLER: {
+ MachineInstr *Rethrow =
+ BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(WebAssembly::RETHROW))
+ .addImm(EHPadStack.size());
+ MI.eraseFromParent();
+ I = MachineBasicBlock::reverse_iterator(Rethrow);
+ break;
+ }
+
default:
if (MI.isTerminator()) {
// Rewrite MBB operands to be depth immediates.
@@ -352,13 +785,15 @@ static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI,
}
}
assert(Stack.empty() && "Control flow should be balanced");
+}
- // Fix up block/loop signatures at the end of the function to conform to
- // WebAssembly's rules.
- FixEndsAtEndOfFunction(MF, MFI, BlockTops, LoopTops);
-
- // Add an end instruction at the end of the function body.
- AppendEndToFunction(MF, TII);
+void WebAssemblyCFGStackify::releaseMemory() {
+ ScopeTops.clear();
+ BeginToEnd.clear();
+ EndToBegin.clear();
+ TryToEHPad.clear();
+ EHPadToTry.clear();
+ BeginToBottom.clear();
}
bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) {
@@ -366,15 +801,27 @@ bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) {
"********** Function: "
<< MF.getName() << '\n');
- const auto &MLI = getAnalysis<MachineLoopInfo>();
- auto &MDT = getAnalysis<MachineDominatorTree>();
+ releaseMemory();
+
// Liveness is not tracked for VALUE_STACK physreg.
- const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
- WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
MF.getRegInfo().invalidateLiveness();
- // Place the BLOCK and LOOP markers to indicate the beginnings of scopes.
- PlaceMarkers(MF, MLI, TII, MDT, MFI);
+ // Place the BLOCK/LOOP/TRY markers to indicate the beginnings of scopes.
+ placeMarkers(MF);
+
+ // Convert MBB operands in terminators to relative depth immediates.
+ rewriteDepthImmediates(MF);
+
+ // Fix up block/loop/try signatures at the end of the function to conform to
+ // WebAssembly's rules.
+ fixEndsAtEndOfFunction(MF);
+
+ // Add an end instruction at the end of the function body.
+ const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
+ if (!MF.getSubtarget<WebAssemblySubtarget>()
+ .getTargetTriple()
+ .isOSBinFormatELF())
+ AppendEndToFunction(MF, TII);
return true;
}
diff --git a/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp b/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp
index c1820bf66bc0..aaa6d286598f 100644
--- a/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp
@@ -64,16 +64,30 @@ FunctionPass *llvm::createWebAssemblyCallIndirectFixup() {
static unsigned GetNonPseudoCallIndirectOpcode(const MachineInstr &MI) {
switch (MI.getOpcode()) {
using namespace WebAssembly;
- case PCALL_INDIRECT_VOID: return CALL_INDIRECT_VOID;
- case PCALL_INDIRECT_I32: return CALL_INDIRECT_I32;
- case PCALL_INDIRECT_I64: return CALL_INDIRECT_I64;
- case PCALL_INDIRECT_F32: return CALL_INDIRECT_F32;
- case PCALL_INDIRECT_F64: return CALL_INDIRECT_F64;
- case PCALL_INDIRECT_v16i8: return CALL_INDIRECT_v16i8;
- case PCALL_INDIRECT_v8i16: return CALL_INDIRECT_v8i16;
- case PCALL_INDIRECT_v4i32: return CALL_INDIRECT_v4i32;
- case PCALL_INDIRECT_v4f32: return CALL_INDIRECT_v4f32;
- default: return INSTRUCTION_LIST_END;
+ case PCALL_INDIRECT_VOID:
+ return CALL_INDIRECT_VOID;
+ case PCALL_INDIRECT_I32:
+ return CALL_INDIRECT_I32;
+ case PCALL_INDIRECT_I64:
+ return CALL_INDIRECT_I64;
+ case PCALL_INDIRECT_F32:
+ return CALL_INDIRECT_F32;
+ case PCALL_INDIRECT_F64:
+ return CALL_INDIRECT_F64;
+ case PCALL_INDIRECT_v16i8:
+ return CALL_INDIRECT_v16i8;
+ case PCALL_INDIRECT_v8i16:
+ return CALL_INDIRECT_v8i16;
+ case PCALL_INDIRECT_v4i32:
+ return CALL_INDIRECT_v4i32;
+ case PCALL_INDIRECT_v2i64:
+ return CALL_INDIRECT_v2i64;
+ case PCALL_INDIRECT_v4f32:
+ return CALL_INDIRECT_v4f32;
+ case PCALL_INDIRECT_v2f64:
+ return CALL_INDIRECT_v2f64;
+ default:
+ return INSTRUCTION_LIST_END;
}
}
@@ -84,7 +98,7 @@ static bool IsPseudoCallIndirect(const MachineInstr &MI) {
bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** Fixing up CALL_INDIRECTs **********\n"
- << MF.getName() << '\n');
+ << "********** Function: " << MF.getName() << '\n');
bool Changed = false;
const WebAssemblyInstrInfo *TII =
@@ -110,10 +124,8 @@ bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) {
Ops.push_back(MachineOperand::CreateImm(0));
for (const MachineOperand &MO :
- make_range(MI.operands_begin() +
- MI.getDesc().getNumDefs() + 1,
- MI.operands_begin() +
- MI.getNumExplicitOperands()))
+ make_range(MI.operands_begin() + MI.getDesc().getNumDefs() + 1,
+ MI.operands_begin() + MI.getNumExplicitOperands()))
Ops.push_back(MO);
Ops.push_back(MI.getOperand(MI.getDesc().getNumDefs()));
@@ -133,4 +145,3 @@ bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) {
return Changed;
}
-
diff --git a/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp
new file mode 100644
index 000000000000..8ecc159951ad
--- /dev/null
+++ b/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp
@@ -0,0 +1,46 @@
+//===-- WebAssemblyDebugValueManager.cpp - WebAssembly DebugValue Manager -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the manager for MachineInstr DebugValues.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssemblyDebugValueManager.h"
+#include "WebAssemblyMachineFunctionInfo.h"
+#include "llvm/CodeGen/MachineInstr.h"
+
+using namespace llvm;
+
+WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(
+ MachineInstr *Instr) {
+ Instr->collectDebugValues(DbgValues);
+}
+
+void WebAssemblyDebugValueManager::move(MachineInstr *Insert) {
+ MachineBasicBlock *MBB = Insert->getParent();
+ for (MachineInstr *DBI : reverse(DbgValues))
+ MBB->splice(Insert, DBI->getParent(), DBI);
+}
+
+void WebAssemblyDebugValueManager::updateReg(unsigned Reg) {
+ for (auto *DBI : DbgValues)
+ DBI->getOperand(0).setReg(Reg);
+}
+
+void WebAssemblyDebugValueManager::clone(MachineInstr *Insert,
+ unsigned NewReg) {
+ MachineBasicBlock *MBB = Insert->getParent();
+ MachineFunction *MF = MBB->getParent();
+ for (MachineInstr *DBI : reverse(DbgValues)) {
+ MachineInstr *Clone = MF->CloneMachineInstr(DBI);
+ Clone->getOperand(0).setReg(NewReg);
+ MBB->insert(Insert, Clone);
+ }
+}
diff --git a/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h
new file mode 100644
index 000000000000..73f317214058
--- /dev/null
+++ b/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h
@@ -0,0 +1,38 @@
+// WebAssemblyDebugValueManager.h - WebAssembly DebugValue Manager -*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of the WebAssembly-specific
+/// manager for DebugValues associated with the specific MachineInstr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYDEBUGVALUEMANAGER_H
+#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYDEBUGVALUEMANAGER_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class MachineInstr;
+
+class WebAssemblyDebugValueManager {
+ SmallVector<MachineInstr *, 2> DbgValues;
+
+public:
+ WebAssemblyDebugValueManager(MachineInstr *Instr);
+
+ void move(MachineInstr *Insert);
+ void updateReg(unsigned Reg);
+ void clone(MachineInstr *Insert, unsigned NewReg);
+};
+
+} // end namespace llvm
+
+#endif
diff --git a/lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp b/lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp
new file mode 100644
index 000000000000..c86260ba408c
--- /dev/null
+++ b/lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp
@@ -0,0 +1,87 @@
+//===-- WebAssemblyEHRestoreStackPointer.cpp - __stack_pointer restoration ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// After the stack is unwound due to a thrown exception, the __stack_pointer
+/// global can point to an invalid address. This inserts instructions that
+/// restore __stack_pointer global.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssembly.h"
+#include "WebAssemblySubtarget.h"
+#include "WebAssemblyUtilities.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/MC/MCAsmInfo.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-eh-restore-stack-pointer"
+
+namespace {
+class WebAssemblyEHRestoreStackPointer final : public MachineFunctionPass {
+public:
+ static char ID; // Pass identification, replacement for typeid
+ WebAssemblyEHRestoreStackPointer() : MachineFunctionPass(ID) {}
+
+ StringRef getPassName() const override {
+ return "WebAssembly Restore Stack Pointer for Exception Handling";
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+};
+} // end anonymous namespace
+
+char WebAssemblyEHRestoreStackPointer::ID = 0;
+INITIALIZE_PASS(WebAssemblyEHRestoreStackPointer, DEBUG_TYPE,
+ "Restore Stack Pointer for Exception Handling", true, false)
+
+FunctionPass *llvm::createWebAssemblyEHRestoreStackPointer() {
+ return new WebAssemblyEHRestoreStackPointer();
+}
+
+bool WebAssemblyEHRestoreStackPointer::runOnMachineFunction(
+ MachineFunction &MF) {
+ LLVM_DEBUG(dbgs() << "********** EH Restore Stack Pointer **********\n"
+ "********** Function: "
+ << MF.getName() << '\n');
+
+ const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>(
+ MF.getSubtarget().getFrameLowering());
+ if (!FrameLowering->needsPrologForEH(MF))
+ return false;
+ bool Changed = false;
+
+ for (auto &MBB : MF) {
+ if (!MBB.isEHPad())
+ continue;
+ Changed = true;
+
+ // Insert __stack_pointer restoring instructions at the beginning of each EH
+ // pad, after the catch instruction. (Catch instructions may have been
+ // reordered, and catch_all instructions have not been inserted yet, but
+ // those cases are handled in LateEHPrepare).
+ //
+ // Here it is safe to assume that SP32 holds the latest value of
+ // __stack_pointer, because the only exception for this case is when a
+ // function uses the red zone, but that only happens with leaf functions,
+ // and we don't restore __stack_pointer in leaf functions anyway.
+ auto InsertPos = MBB.begin();
+ if (WebAssembly::isCatch(*MBB.begin()))
+ InsertPos++;
+ FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
+ MBB.begin()->getDebugLoc());
+ }
+ return Changed;
+}
diff --git a/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp b/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp
index 84683d48a90a..6b3a3e765786 100644
--- a/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp
@@ -13,8 +13,8 @@
//===----------------------------------------------------------------------===//
#include "WebAssemblyExceptionInfo.h"
-#include "WebAssemblyUtilities.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyUtilities.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/CodeGen/MachineDominanceFrontier.h"
#include "llvm/CodeGen/MachineDominators.h"
@@ -32,7 +32,10 @@ INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier)
INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE,
"WebAssembly Exception Information", true, true)
-bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &F) {
+bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) {
+ LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"
+ "********** Function: "
+ << MF.getName() << '\n');
releaseMemory();
auto &MDT = getAnalysis<MachineDominatorTree>();
auto &MDF = getAnalysis<MachineDominanceFrontier>();
diff --git a/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
index 8619cbdcb5ee..27aabe6ba0bd 100644
--- a/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp
@@ -11,7 +11,7 @@
/// This file converts any remaining registers into WebAssembly locals.
///
/// After register stackification and register coloring, convert non-stackified
-/// registers into locals, inserting explicit get_local and set_local
+/// registers into locals, inserting explicit local.get and local.set
/// instructions.
///
//===----------------------------------------------------------------------===//
@@ -31,12 +31,14 @@ using namespace llvm;
#define DEBUG_TYPE "wasm-explicit-locals"
-// A command-line option to disable this pass. Note that this produces output
-// which is not valid WebAssembly, though it may be more convenient for writing
-// LLVM unit tests with.
-static cl::opt<bool> DisableWebAssemblyExplicitLocals(
- "disable-wasm-explicit-locals", cl::ReallyHidden,
- cl::desc("WebAssembly: Disable emission of get_local/set_local."),
+// A command-line option to disable this pass, and keep implicit locals
+// for the purpose of testing with lit/llc ONLY.
+// This produces output which is not valid WebAssembly, and is not supported
+// by assemblers/disassemblers and other MC based tools.
+static cl::opt<bool> WasmDisableExplicitLocals(
+ "wasm-disable-explicit-locals", cl::Hidden,
+ cl::desc("WebAssembly: output implicit locals in"
+ " instruction output for test purposes only."),
cl::init(false));
namespace {
@@ -94,54 +96,54 @@ static unsigned getDropOpcode(const TargetRegisterClass *RC) {
llvm_unreachable("Unexpected register class");
}
-/// Get the appropriate get_local opcode for the given register class.
+/// Get the appropriate local.get opcode for the given register class.
static unsigned getGetLocalOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
- return WebAssembly::GET_LOCAL_I32;
+ return WebAssembly::LOCAL_GET_I32;
if (RC == &WebAssembly::I64RegClass)
- return WebAssembly::GET_LOCAL_I64;
+ return WebAssembly::LOCAL_GET_I64;
if (RC == &WebAssembly::F32RegClass)
- return WebAssembly::GET_LOCAL_F32;
+ return WebAssembly::LOCAL_GET_F32;
if (RC == &WebAssembly::F64RegClass)
- return WebAssembly::GET_LOCAL_F64;
+ return WebAssembly::LOCAL_GET_F64;
if (RC == &WebAssembly::V128RegClass)
- return WebAssembly::GET_LOCAL_V128;
+ return WebAssembly::LOCAL_GET_V128;
if (RC == &WebAssembly::EXCEPT_REFRegClass)
- return WebAssembly::GET_LOCAL_EXCEPT_REF;
+ return WebAssembly::LOCAL_GET_EXCEPT_REF;
llvm_unreachable("Unexpected register class");
}
-/// Get the appropriate set_local opcode for the given register class.
+/// Get the appropriate local.set opcode for the given register class.
static unsigned getSetLocalOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
- return WebAssembly::SET_LOCAL_I32;
+ return WebAssembly::LOCAL_SET_I32;
if (RC == &WebAssembly::I64RegClass)
- return WebAssembly::SET_LOCAL_I64;
+ return WebAssembly::LOCAL_SET_I64;
if (RC == &WebAssembly::F32RegClass)
- return WebAssembly::SET_LOCAL_F32;
+ return WebAssembly::LOCAL_SET_F32;
if (RC == &WebAssembly::F64RegClass)
- return WebAssembly::SET_LOCAL_F64;
+ return WebAssembly::LOCAL_SET_F64;
if (RC == &WebAssembly::V128RegClass)
- return WebAssembly::SET_LOCAL_V128;
+ return WebAssembly::LOCAL_SET_V128;
if (RC == &WebAssembly::EXCEPT_REFRegClass)
- return WebAssembly::SET_LOCAL_EXCEPT_REF;
+ return WebAssembly::LOCAL_SET_EXCEPT_REF;
llvm_unreachable("Unexpected register class");
}
-/// Get the appropriate tee_local opcode for the given register class.
+/// Get the appropriate local.tee opcode for the given register class.
static unsigned getTeeLocalOpcode(const TargetRegisterClass *RC) {
if (RC == &WebAssembly::I32RegClass)
- return WebAssembly::TEE_LOCAL_I32;
+ return WebAssembly::LOCAL_TEE_I32;
if (RC == &WebAssembly::I64RegClass)
- return WebAssembly::TEE_LOCAL_I64;
+ return WebAssembly::LOCAL_TEE_I64;
if (RC == &WebAssembly::F32RegClass)
- return WebAssembly::TEE_LOCAL_F32;
+ return WebAssembly::LOCAL_TEE_F32;
if (RC == &WebAssembly::F64RegClass)
- return WebAssembly::TEE_LOCAL_F64;
+ return WebAssembly::LOCAL_TEE_F64;
if (RC == &WebAssembly::V128RegClass)
- return WebAssembly::TEE_LOCAL_V128;
+ return WebAssembly::LOCAL_TEE_V128;
if (RC == &WebAssembly::EXCEPT_REFRegClass)
- return WebAssembly::TEE_LOCAL_EXCEPT_REF;
+ return WebAssembly::LOCAL_TEE_EXCEPT_REF;
llvm_unreachable("Unexpected register class");
}
@@ -155,6 +157,8 @@ static MVT typeForRegClass(const TargetRegisterClass *RC) {
return MVT::f32;
if (RC == &WebAssembly::F64RegClass)
return MVT::f64;
+ if (RC == &WebAssembly::V128RegClass)
+ return MVT::v16i8;
if (RC == &WebAssembly::EXCEPT_REFRegClass)
return MVT::ExceptRef;
llvm_unreachable("unrecognized register class");
@@ -162,7 +166,7 @@ static MVT typeForRegClass(const TargetRegisterClass *RC) {
/// Given a MachineOperand of a stackified vreg, return the instruction at the
/// start of the expression tree.
-static MachineInstr *FindStartOfTree(MachineOperand &MO,
+static MachineInstr *findStartOfTree(MachineOperand &MO,
MachineRegisterInfo &MRI,
WebAssemblyFunctionInfo &MFI) {
unsigned Reg = MO.getReg();
@@ -173,7 +177,7 @@ static MachineInstr *FindStartOfTree(MachineOperand &MO,
for (MachineOperand &DefMO : Def->explicit_uses()) {
if (!DefMO.isReg())
continue;
- return FindStartOfTree(DefMO, MRI, MFI);
+ return findStartOfTree(DefMO, MRI, MFI);
}
// If there were no stackified uses, we've reached the start.
@@ -186,7 +190,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
<< MF.getName() << '\n');
// Disable this pass if directed to do so.
- if (DisableWebAssemblyExplicitLocals)
+ if (WasmDisableExplicitLocals)
return false;
bool Changed = false;
@@ -206,19 +210,19 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
break;
unsigned Reg = MI.getOperand(0).getReg();
assert(!MFI.isVRegStackified(Reg));
- Reg2Local[Reg] = MI.getOperand(1).getImm();
+ Reg2Local[Reg] = static_cast<unsigned>(MI.getOperand(1).getImm());
MI.eraseFromParent();
Changed = true;
}
// Start assigning local numbers after the last parameter.
- unsigned CurLocal = MFI.getParams().size();
+ unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size());
// Precompute the set of registers that are unused, so that we can insert
// drops to their defs.
BitVector UseEmpty(MRI.getNumVirtRegs());
- for (unsigned i = 0, e = MRI.getNumVirtRegs(); i < e; ++i)
- UseEmpty[i] = MRI.use_empty(TargetRegisterInfo::index2VirtReg(i));
+ for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I)
+ UseEmpty[I] = MRI.use_empty(TargetRegisterInfo::index2VirtReg(I));
// Visit each instruction in the function.
for (MachineBasicBlock &MBB : MF) {
@@ -229,8 +233,8 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
if (MI.isDebugInstr() || MI.isLabel())
continue;
- // Replace tee instructions with tee_local. The difference is that tee
- // instructins have two defs, while tee_local instructions have one def
+ // 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.
if (WebAssembly::isTee(MI)) {
assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));
@@ -249,7 +253,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
MFI.stackifyVReg(NewReg);
}
- // Replace the TEE with a TEE_LOCAL.
+ // Replace the TEE with a LOCAL_TEE.
unsigned LocalId =
getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg());
unsigned Opc = getTeeLocalOpcode(RC);
@@ -263,7 +267,7 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
continue;
}
- // Insert set_locals for any defs that aren't stackified yet. Currently
+ // Insert local.sets for any defs that aren't stackified yet. Currently
// we handle at most one def.
assert(MI.getDesc().getNumDefs() <= 1);
if (MI.getDesc().getNumDefs() == 1) {
@@ -292,15 +296,16 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
.addReg(NewReg);
}
MI.getOperand(0).setReg(NewReg);
- // This register operand is now being used by the inserted drop
- // instruction, so make it undead.
+ // This register operand of the original instruction is now being used
+ // by the inserted drop or local.set instruction, so make it not dead
+ // yet.
MI.getOperand(0).setIsDead(false);
MFI.stackifyVReg(NewReg);
Changed = true;
}
}
- // Insert get_locals for any uses that aren't stackified yet.
+ // Insert local.gets for any uses that aren't stackified yet.
MachineInstr *InsertPt = &MI;
for (MachineOperand &MO : reverse(MI.explicit_uses())) {
if (!MO.isReg())
@@ -314,15 +319,17 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
if (MO.isDef()) {
assert(MI.getOpcode() == TargetOpcode::INLINEASM);
unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
- MRI.removeRegOperandFromUseList(&MO);
- MO = MachineOperand::CreateImm(LocalId);
+ // 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));
+ MO.ChangeToImmediate(LocalId);
continue;
}
// If we see a stackified register, prepare to insert subsequent
- // get_locals before the start of its tree.
+ // local.gets before the start of its tree.
if (MFI.isVRegStackified(OldReg)) {
- InsertPt = FindStartOfTree(MO, MRI, MFI);
+ InsertPt = findStartOfTree(MO, MRI, MFI);
continue;
}
@@ -330,12 +337,13 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
// indices as immediates.
if (MI.getOpcode() == TargetOpcode::INLINEASM) {
unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
- MRI.removeRegOperandFromUseList(&MO);
- MO = MachineOperand::CreateImm(LocalId);
+ // Untie it first if this reg operand is tied to another operand.
+ MI.untieRegOperand(MI.getOperandNo(&MO));
+ MO.ChangeToImmediate(LocalId);
continue;
}
- // Insert a get_local.
+ // Insert a local.get.
unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
unsigned NewReg = MRI.createVirtualRegister(RC);
@@ -361,13 +369,13 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
// Define the locals.
// TODO: Sort the locals for better compression.
MFI.setNumLocals(CurLocal - MFI.getParams().size());
- for (size_t i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) {
- unsigned Reg = TargetRegisterInfo::index2VirtReg(i);
- auto I = Reg2Local.find(Reg);
- if (I == Reg2Local.end() || I->second < MFI.getParams().size())
+ for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {
+ unsigned Reg = TargetRegisterInfo::index2VirtReg(I);
+ auto RL = Reg2Local.find(Reg);
+ if (RL == Reg2Local.end() || RL->second < MFI.getParams().size())
continue;
- MFI.setLocal(I->second - MFI.getParams().size(),
+ MFI.setLocal(RL->second - MFI.getParams().size(),
typeForRegClass(MRI.getRegClass(Reg)));
Changed = true;
}
diff --git a/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
index 566ef68c027d..3856700cca94 100644
--- a/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -37,7 +37,10 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Operator.h"
+#include "llvm/IR/PatternMatch.h"
+
using namespace llvm;
+using namespace PatternMatch;
#define DEBUG_TYPE "wasm-fastisel"
@@ -114,8 +117,8 @@ private:
// Utility helper routines
MVT::SimpleValueType getSimpleType(Type *Ty) {
EVT VT = TLI.getValueType(DL, Ty, /*HandleUnknown=*/true);
- return VT.isSimple() ? VT.getSimpleVT().SimpleTy :
- MVT::INVALID_SIMPLE_VALUE_TYPE;
+ return VT.isSimple() ? VT.getSimpleVT().SimpleTy
+ : MVT::INVALID_SIMPLE_VALUE_TYPE;
}
MVT::SimpleValueType getLegalType(MVT::SimpleValueType VT) {
switch (VT) {
@@ -138,6 +141,11 @@ private:
if (Subtarget->hasSIMD128())
return VT;
break;
+ case MVT::v2i64:
+ case MVT::v2f64:
+ if (Subtarget->hasUnimplementedSIMD128())
+ return VT;
+ break;
default:
break;
}
@@ -153,11 +161,9 @@ private:
MVT::SimpleValueType From);
unsigned signExtendToI32(unsigned Reg, const Value *V,
MVT::SimpleValueType From);
- unsigned zeroExtend(unsigned Reg, const Value *V,
- MVT::SimpleValueType From,
+ unsigned zeroExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From,
MVT::SimpleValueType To);
- unsigned signExtend(unsigned Reg, const Value *V,
- MVT::SimpleValueType From,
+ unsigned signExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From,
MVT::SimpleValueType To);
unsigned getRegForUnsignedValue(const Value *V);
unsigned getRegForSignedValue(const Value *V);
@@ -374,14 +380,12 @@ void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
if (Addr.isRegBase()) {
unsigned Reg = Addr.getReg();
if (Reg == 0) {
- Reg = createResultReg(Subtarget->hasAddr64() ?
- &WebAssembly::I64RegClass :
- &WebAssembly::I32RegClass);
- unsigned Opc = Subtarget->hasAddr64() ?
- WebAssembly::CONST_I64 :
- WebAssembly::CONST_I32;
+ Reg = createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
+ : &WebAssembly::I32RegClass);
+ unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64
+ : WebAssembly::CONST_I32;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg)
- .addImm(0);
+ .addImm(0);
Addr.setReg(Reg);
}
}
@@ -419,9 +423,10 @@ unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V, bool &Not) {
return getRegForValue(ICmp->getOperand(0));
}
- if (BinaryOperator::isNot(V) && V->getType()->isIntegerTy(32)) {
+ Value *NotV;
+ if (match(V, m_Not(m_Value(NotV))) && V->getType()->isIntegerTy(32)) {
Not = true;
- return getRegForValue(BinaryOperator::getNotArgument(V));
+ return getRegForValue(NotV);
}
Not = false;
@@ -438,13 +443,12 @@ unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V,
switch (From) {
case MVT::i1:
- // If the value is naturally an i1, we don't need to mask it.
- // TODO: Recursively examine selects, phis, and, or, xor, constants.
- if (From == MVT::i1 && V != nullptr) {
- if (isa<CmpInst>(V) ||
- (isa<Argument>(V) && cast<Argument>(V)->hasZExtAttr()))
- return copyValue(Reg);
- }
+ // If the value is naturally an i1, we don't need to mask it. We only know
+ // if a value is naturally an i1 if it is definitely lowered by FastISel,
+ // not a DAG ISel fallback.
+ if (V != nullptr && isa<Argument>(V) && cast<Argument>(V)->hasZExtAttr())
+ return copyValue(Reg);
+ break;
case MVT::i8:
case MVT::i16:
break;
@@ -457,13 +461,13 @@ unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V,
unsigned Imm = createResultReg(&WebAssembly::I32RegClass);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(WebAssembly::CONST_I32), Imm)
- .addImm(~(~uint64_t(0) << MVT(From).getSizeInBits()));
+ .addImm(~(~uint64_t(0) << MVT(From).getSizeInBits()));
unsigned Result = createResultReg(&WebAssembly::I32RegClass);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(WebAssembly::AND_I32), Result)
- .addReg(Reg)
- .addReg(Imm);
+ .addReg(Reg)
+ .addReg(Imm);
return Result;
}
@@ -487,19 +491,19 @@ unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V,
unsigned Imm = createResultReg(&WebAssembly::I32RegClass);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(WebAssembly::CONST_I32), Imm)
- .addImm(32 - MVT(From).getSizeInBits());
+ .addImm(32 - MVT(From).getSizeInBits());
unsigned Left = createResultReg(&WebAssembly::I32RegClass);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(WebAssembly::SHL_I32), Left)
- .addReg(Reg)
- .addReg(Imm);
+ .addReg(Reg)
+ .addReg(Imm);
unsigned Right = createResultReg(&WebAssembly::I32RegClass);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(WebAssembly::SHR_S_I32), Right)
- .addReg(Left)
- .addReg(Imm);
+ .addReg(Left)
+ .addReg(Imm);
return Right;
}
@@ -562,8 +566,7 @@ unsigned WebAssemblyFastISel::getRegForSignedValue(const Value *V) {
unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *V,
bool IsSigned) {
- return IsSigned ? getRegForSignedValue(V) :
- getRegForUnsignedValue(V);
+ return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(V);
}
unsigned WebAssemblyFastISel::notValue(unsigned Reg) {
@@ -572,15 +575,15 @@ unsigned WebAssemblyFastISel::notValue(unsigned Reg) {
unsigned NotReg = createResultReg(&WebAssembly::I32RegClass);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(WebAssembly::EQZ_I32), NotReg)
- .addReg(Reg);
+ .addReg(Reg);
return NotReg;
}
unsigned WebAssemblyFastISel::copyValue(unsigned Reg) {
unsigned ResultReg = createResultReg(MRI.getRegClass(Reg));
- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
- TII.get(WebAssembly::COPY), ResultReg)
- .addReg(Reg);
+ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(WebAssembly::COPY),
+ ResultReg)
+ .addReg(Reg);
return ResultReg;
}
@@ -589,12 +592,11 @@ unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) {
FuncInfo.StaticAllocaMap.find(AI);
if (SI != FuncInfo.StaticAllocaMap.end()) {
- unsigned ResultReg = createResultReg(Subtarget->hasAddr64() ?
- &WebAssembly::I64RegClass :
- &WebAssembly::I32RegClass);
- unsigned Opc = Subtarget->hasAddr64() ?
- WebAssembly::COPY_I64 :
- WebAssembly::COPY_I32;
+ unsigned ResultReg =
+ createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
+ : &WebAssembly::I32RegClass);
+ unsigned Opc =
+ Subtarget->hasAddr64() ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg)
.addFrameIndex(SI->second);
return ResultReg;
@@ -605,14 +607,13 @@ unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) {
unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) {
if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) {
- unsigned ResultReg = createResultReg(Subtarget->hasAddr64() ?
- &WebAssembly::I64RegClass :
- &WebAssembly::I32RegClass);
- unsigned Opc = Subtarget->hasAddr64() ?
- WebAssembly::CONST_I64 :
- WebAssembly::CONST_I32;
+ unsigned ResultReg =
+ createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
+ : &WebAssembly::I32RegClass);
+ unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64
+ : WebAssembly::CONST_I32;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg)
- .addGlobalAddress(GV);
+ .addGlobalAddress(GV);
return ResultReg;
}
@@ -651,19 +652,19 @@ bool WebAssemblyFastISel::fastLowerArguments() {
case MVT::i8:
case MVT::i16:
case MVT::i32:
- Opc = WebAssembly::ARGUMENT_I32;
+ Opc = WebAssembly::ARGUMENT_i32;
RC = &WebAssembly::I32RegClass;
break;
case MVT::i64:
- Opc = WebAssembly::ARGUMENT_I64;
+ Opc = WebAssembly::ARGUMENT_i64;
RC = &WebAssembly::I64RegClass;
break;
case MVT::f32:
- Opc = WebAssembly::ARGUMENT_F32;
+ Opc = WebAssembly::ARGUMENT_f32;
RC = &WebAssembly::F32RegClass;
break;
case MVT::f64:
- Opc = WebAssembly::ARGUMENT_F64;
+ Opc = WebAssembly::ARGUMENT_f64;
RC = &WebAssembly::F64RegClass;
break;
case MVT::v16i8:
@@ -678,12 +679,20 @@ bool WebAssemblyFastISel::fastLowerArguments() {
Opc = WebAssembly::ARGUMENT_v4i32;
RC = &WebAssembly::V128RegClass;
break;
+ case MVT::v2i64:
+ Opc = WebAssembly::ARGUMENT_v2i64;
+ RC = &WebAssembly::V128RegClass;
+ break;
case MVT::v4f32:
Opc = WebAssembly::ARGUMENT_v4f32;
RC = &WebAssembly::V128RegClass;
break;
+ case MVT::v2f64:
+ Opc = WebAssembly::ARGUMENT_v2f64;
+ RC = &WebAssembly::V128RegClass;
+ break;
case MVT::ExceptRef:
- Opc = WebAssembly::ARGUMENT_EXCEPT_REF;
+ Opc = WebAssembly::ARGUMENT_ExceptRef;
RC = &WebAssembly::EXCEPT_REFRegClass;
break;
default:
@@ -691,7 +700,7 @@ bool WebAssemblyFastISel::fastLowerArguments() {
}
unsigned ResultReg = createResultReg(RC);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg)
- .addImm(i);
+ .addImm(i);
updateValueMap(&Arg, ResultReg);
++i;
@@ -710,7 +719,8 @@ bool WebAssemblyFastISel::fastLowerArguments() {
}
if (!F->getReturnType()->isVoidTy()) {
- MVT::SimpleValueType RetTy = getLegalType(getSimpleType(F->getReturnType()));
+ MVT::SimpleValueType RetTy =
+ getLegalType(getSimpleType(F->getReturnType()));
if (RetTy == MVT::INVALID_SIMPLE_VALUE_TYPE) {
MFI->clearParamsAndResults();
return false;
@@ -768,23 +778,33 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
ResultReg = createResultReg(&WebAssembly::F64RegClass);
break;
case MVT::v16i8:
- Opc =
- IsDirect ? WebAssembly::CALL_v16i8 : WebAssembly::PCALL_INDIRECT_v16i8;
+ Opc = IsDirect ? WebAssembly::CALL_v16i8
+ : WebAssembly::PCALL_INDIRECT_v16i8;
ResultReg = createResultReg(&WebAssembly::V128RegClass);
break;
case MVT::v8i16:
- Opc =
- IsDirect ? WebAssembly::CALL_v8i16 : WebAssembly::PCALL_INDIRECT_v8i16;
+ Opc = IsDirect ? WebAssembly::CALL_v8i16
+ : WebAssembly::PCALL_INDIRECT_v8i16;
ResultReg = createResultReg(&WebAssembly::V128RegClass);
break;
case MVT::v4i32:
- Opc =
- IsDirect ? WebAssembly::CALL_v4i32 : WebAssembly::PCALL_INDIRECT_v4i32;
+ Opc = IsDirect ? WebAssembly::CALL_v4i32
+ : WebAssembly::PCALL_INDIRECT_v4i32;
+ ResultReg = createResultReg(&WebAssembly::V128RegClass);
+ break;
+ case MVT::v2i64:
+ Opc = IsDirect ? WebAssembly::CALL_v2i64
+ : WebAssembly::PCALL_INDIRECT_v2i64;
ResultReg = createResultReg(&WebAssembly::V128RegClass);
break;
case MVT::v4f32:
- Opc =
- IsDirect ? WebAssembly::CALL_v4f32 : WebAssembly::PCALL_INDIRECT_v4f32;
+ Opc = IsDirect ? WebAssembly::CALL_v4f32
+ : WebAssembly::PCALL_INDIRECT_v4f32;
+ ResultReg = createResultReg(&WebAssembly::V128RegClass);
+ break;
+ case MVT::v2f64:
+ Opc = IsDirect ? WebAssembly::CALL_v2f64
+ : WebAssembly::PCALL_INDIRECT_v2f64;
ResultReg = createResultReg(&WebAssembly::V128RegClass);
break;
case MVT::ExceptRef:
@@ -853,11 +873,11 @@ bool WebAssemblyFastISel::selectSelect(const Instruction *I) {
const SelectInst *Select = cast<SelectInst>(I);
bool Not;
- unsigned CondReg = getRegForI1Value(Select->getCondition(), Not);
+ unsigned CondReg = getRegForI1Value(Select->getCondition(), Not);
if (CondReg == 0)
return false;
- unsigned TrueReg = getRegForValue(Select->getTrueValue());
+ unsigned TrueReg = getRegForValue(Select->getTrueValue());
if (TrueReg == 0)
return false;
@@ -900,9 +920,9 @@ bool WebAssemblyFastISel::selectSelect(const Instruction *I) {
unsigned ResultReg = createResultReg(RC);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg)
- .addReg(TrueReg)
- .addReg(FalseReg)
- .addReg(CondReg);
+ .addReg(TrueReg)
+ .addReg(FalseReg)
+ .addReg(CondReg);
updateValueMap(Select, ResultReg);
return true;
@@ -1002,7 +1022,8 @@ bool WebAssemblyFastISel::selectICmp(const Instruction *I) {
Opc = I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64;
isSigned = true;
break;
- default: return false;
+ default:
+ return false;
}
unsigned LHS = getRegForPromotedValue(ICmp->getOperand(0), isSigned);
@@ -1210,7 +1231,8 @@ bool WebAssemblyFastISel::selectStore(const Instruction *I) {
case MVT::f64:
Opc = WebAssembly::STORE_F64;
break;
- default: return false;
+ default:
+ return false;
}
materializeLoadStoreOperands(Addr);
@@ -1275,8 +1297,10 @@ bool WebAssemblyFastISel::selectRet(const Instruction *I) {
unsigned Opc;
switch (getSimpleType(RV->getType())) {
- case MVT::i1: case MVT::i8:
- case MVT::i16: case MVT::i32:
+ case MVT::i1:
+ case MVT::i8:
+ case MVT::i16:
+ case MVT::i32:
Opc = WebAssembly::RETURN_I32;
break;
case MVT::i64:
@@ -1297,13 +1321,20 @@ bool WebAssemblyFastISel::selectRet(const Instruction *I) {
case MVT::v4i32:
Opc = WebAssembly::RETURN_v4i32;
break;
+ case MVT::v2i64:
+ Opc = WebAssembly::RETURN_v2i64;
+ break;
case MVT::v4f32:
Opc = WebAssembly::RETURN_v4f32;
break;
+ case MVT::v2f64:
+ Opc = WebAssembly::RETURN_v2f64;
+ break;
case MVT::ExceptRef:
Opc = WebAssembly::RETURN_EXCEPT_REF;
break;
- default: return false;
+ default:
+ return false;
}
unsigned Reg;
@@ -1333,19 +1364,32 @@ bool WebAssemblyFastISel::fastSelectInstruction(const Instruction *I) {
if (selectCall(I))
return true;
break;
- case Instruction::Select: return selectSelect(I);
- case Instruction::Trunc: return selectTrunc(I);
- case Instruction::ZExt: return selectZExt(I);
- case Instruction::SExt: return selectSExt(I);
- case Instruction::ICmp: return selectICmp(I);
- case Instruction::FCmp: return selectFCmp(I);
- case Instruction::BitCast: return selectBitCast(I);
- case Instruction::Load: return selectLoad(I);
- case Instruction::Store: return selectStore(I);
- case Instruction::Br: return selectBr(I);
- case Instruction::Ret: return selectRet(I);
- case Instruction::Unreachable: return selectUnreachable(I);
- default: break;
+ case Instruction::Select:
+ return selectSelect(I);
+ case Instruction::Trunc:
+ return selectTrunc(I);
+ case Instruction::ZExt:
+ return selectZExt(I);
+ case Instruction::SExt:
+ return selectSExt(I);
+ case Instruction::ICmp:
+ return selectICmp(I);
+ case Instruction::FCmp:
+ return selectFCmp(I);
+ case Instruction::BitCast:
+ return selectBitCast(I);
+ case Instruction::Load:
+ return selectLoad(I);
+ case Instruction::Store:
+ return selectStore(I);
+ case Instruction::Br:
+ return selectBr(I);
+ case Instruction::Ret:
+ return selectRet(I);
+ case Instruction::Unreachable:
+ return selectUnreachable(I);
+ default:
+ break;
}
// Fall back to target-independent instruction selection.
diff --git a/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp b/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
index d5e47ee82513..1a416520f97d 100644
--- a/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp
@@ -36,10 +36,10 @@ using namespace llvm;
#define DEBUG_TYPE "wasm-fix-function-bitcasts"
-static cl::opt<bool> TemporaryWorkarounds(
- "wasm-temporary-workarounds",
- cl::desc("Apply certain temporary workarounds"),
- cl::init(true), cl::Hidden);
+static cl::opt<bool>
+ TemporaryWorkarounds("wasm-temporary-workarounds",
+ cl::desc("Apply certain temporary workarounds"),
+ cl::init(true), cl::Hidden);
namespace {
class FixFunctionBitcasts final : public ModulePass {
@@ -103,14 +103,29 @@ static void FindUses(Value *V, Function &F,
// - Return value is not needed: drop it
// - Return value needed but not present: supply an undef
//
-// For now, return nullptr without creating a wrapper if the wrapper cannot
-// be generated due to incompatible types.
+// If the all the argument types of trivially castable to one another (i.e.
+// I32 vs pointer type) then we don't create a wrapper at all (return nullptr
+// instead).
+//
+// If there is a type mismatch that we know would result in an invalid wasm
+// module then generate wrapper that contains unreachable (i.e. abort at
+// runtime). Such programs are deep into undefined behaviour territory,
+// but we choose to fail at runtime rather than generate and invalid module
+// or fail at compiler time. The reason we delay the error is that we want
+// to support the CMake which expects to be able to compile and link programs
+// that refer to functions with entirely incorrect signatures (this is how
+// CMake detects the existence of a function in a toolchain).
+//
+// For bitcasts that involve struct types we don't know at this stage if they
+// would be equivalent at the wasm level and so we can't know if we need to
+// generate a wrapper.
static Function *CreateWrapper(Function *F, FunctionType *Ty) {
Module *M = F->getParent();
- Function *Wrapper =
- Function::Create(Ty, Function::PrivateLinkage, "bitcast", M);
+ Function *Wrapper = Function::Create(Ty, Function::PrivateLinkage,
+ F->getName() + "_bitcast", M);
BasicBlock *BB = BasicBlock::Create(M->getContext(), "body", Wrapper);
+ const DataLayout &DL = BB->getModule()->getDataLayout();
// Determine what arguments to pass.
SmallVector<Value *, 4> Args;
@@ -118,38 +133,103 @@ static Function *CreateWrapper(Function *F, FunctionType *Ty) {
Function::arg_iterator AE = Wrapper->arg_end();
FunctionType::param_iterator PI = F->getFunctionType()->param_begin();
FunctionType::param_iterator PE = F->getFunctionType()->param_end();
+ bool TypeMismatch = false;
+ bool WrapperNeeded = false;
+
+ Type *ExpectedRtnType = F->getFunctionType()->getReturnType();
+ Type *RtnType = Ty->getReturnType();
+
+ if ((F->getFunctionType()->getNumParams() != Ty->getNumParams()) ||
+ (F->getFunctionType()->isVarArg() != Ty->isVarArg()) ||
+ (ExpectedRtnType != RtnType))
+ WrapperNeeded = true;
+
for (; AI != AE && PI != PE; ++AI, ++PI) {
- if (AI->getType() != *PI) {
- Wrapper->eraseFromParent();
- return nullptr;
+ Type *ArgType = AI->getType();
+ Type *ParamType = *PI;
+
+ if (ArgType == ParamType) {
+ Args.push_back(&*AI);
+ } else {
+ if (CastInst::isBitOrNoopPointerCastable(ArgType, ParamType, DL)) {
+ Instruction *PtrCast =
+ CastInst::CreateBitOrPointerCast(AI, ParamType, "cast");
+ BB->getInstList().push_back(PtrCast);
+ Args.push_back(PtrCast);
+ } else if (ArgType->isStructTy() || ParamType->isStructTy()) {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: struct param type in bitcast: "
+ << F->getName() << "\n");
+ WrapperNeeded = false;
+ } else {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: arg type mismatch calling: "
+ << F->getName() << "\n");
+ LLVM_DEBUG(dbgs() << "Arg[" << Args.size() << "] Expected: "
+ << *ParamType << " Got: " << *ArgType << "\n");
+ TypeMismatch = true;
+ break;
+ }
}
- Args.push_back(&*AI);
}
- for (; PI != PE; ++PI)
- Args.push_back(UndefValue::get(*PI));
- if (F->isVarArg())
- for (; AI != AE; ++AI)
- Args.push_back(&*AI);
- CallInst *Call = CallInst::Create(F, Args, "", BB);
+ if (WrapperNeeded && !TypeMismatch) {
+ for (; PI != PE; ++PI)
+ Args.push_back(UndefValue::get(*PI));
+ if (F->isVarArg())
+ for (; AI != AE; ++AI)
+ Args.push_back(&*AI);
- // Determine what value to return.
- if (Ty->getReturnType()->isVoidTy())
- ReturnInst::Create(M->getContext(), BB);
- else if (F->getFunctionType()->getReturnType()->isVoidTy())
- ReturnInst::Create(M->getContext(), UndefValue::get(Ty->getReturnType()),
- BB);
- else if (F->getFunctionType()->getReturnType() == Ty->getReturnType())
- ReturnInst::Create(M->getContext(), Call, BB);
- else {
+ CallInst *Call = CallInst::Create(F, Args, "", BB);
+
+ Type *ExpectedRtnType = F->getFunctionType()->getReturnType();
+ Type *RtnType = Ty->getReturnType();
+ // Determine what value to return.
+ if (RtnType->isVoidTy()) {
+ ReturnInst::Create(M->getContext(), BB);
+ } else if (ExpectedRtnType->isVoidTy()) {
+ LLVM_DEBUG(dbgs() << "Creating dummy return: " << *RtnType << "\n");
+ ReturnInst::Create(M->getContext(), UndefValue::get(RtnType), BB);
+ } else if (RtnType == ExpectedRtnType) {
+ ReturnInst::Create(M->getContext(), Call, BB);
+ } else if (CastInst::isBitOrNoopPointerCastable(ExpectedRtnType, RtnType,
+ DL)) {
+ Instruction *Cast =
+ CastInst::CreateBitOrPointerCast(Call, RtnType, "cast");
+ BB->getInstList().push_back(Cast);
+ ReturnInst::Create(M->getContext(), Cast, BB);
+ } else if (RtnType->isStructTy() || ExpectedRtnType->isStructTy()) {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: struct return type in bitcast: "
+ << F->getName() << "\n");
+ WrapperNeeded = false;
+ } else {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: return type mismatch calling: "
+ << F->getName() << "\n");
+ LLVM_DEBUG(dbgs() << "Expected: " << *ExpectedRtnType
+ << " Got: " << *RtnType << "\n");
+ TypeMismatch = true;
+ }
+ }
+
+ if (TypeMismatch) {
+ // Create a new wrapper that simply contains `unreachable`.
+ Wrapper->eraseFromParent();
+ Wrapper = Function::Create(Ty, Function::PrivateLinkage,
+ F->getName() + "_bitcast_invalid", M);
+ BasicBlock *BB = BasicBlock::Create(M->getContext(), "body", Wrapper);
+ new UnreachableInst(M->getContext(), BB);
+ Wrapper->setName(F->getName() + "_bitcast_invalid");
+ } else if (!WrapperNeeded) {
+ LLVM_DEBUG(dbgs() << "CreateWrapper: no wrapper needed: " << F->getName()
+ << "\n");
Wrapper->eraseFromParent();
return nullptr;
}
-
+ LLVM_DEBUG(dbgs() << "CreateWrapper: " << F->getName() << "\n");
return Wrapper;
}
bool FixFunctionBitcasts::runOnModule(Module &M) {
+ LLVM_DEBUG(dbgs() << "********** Fix Function Bitcasts **********\n");
+
Function *Main = nullptr;
CallInst *CallMain = nullptr;
SmallVector<std::pair<Use *, Function *>, 0> Uses;
@@ -166,19 +246,17 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
if (!TemporaryWorkarounds && !F.isDeclaration() && F.getName() == "main") {
Main = &F;
LLVMContext &C = M.getContext();
- Type *MainArgTys[] = {
- PointerType::get(Type::getInt8PtrTy(C), 0),
- Type::getInt32Ty(C)
- };
+ Type *MainArgTys[] = {Type::getInt32Ty(C),
+ PointerType::get(Type::getInt8PtrTy(C), 0)};
FunctionType *MainTy = FunctionType::get(Type::getInt32Ty(C), MainArgTys,
/*isVarArg=*/false);
if (F.getFunctionType() != MainTy) {
- Value *Args[] = {
- UndefValue::get(MainArgTys[0]),
- UndefValue::get(MainArgTys[1])
- };
- Value *Casted = ConstantExpr::getBitCast(Main,
- PointerType::get(MainTy, 0));
+ LLVM_DEBUG(dbgs() << "Found `main` function with incorrect type: "
+ << *F.getFunctionType() << "\n");
+ Value *Args[] = {UndefValue::get(MainArgTys[0]),
+ UndefValue::get(MainArgTys[1])};
+ Value *Casted =
+ ConstantExpr::getBitCast(Main, PointerType::get(MainTy, 0));
CallMain = CallInst::Create(Casted, Args, "call_main");
Use *UseMain = &CallMain->getOperandUse(2);
Uses.push_back(std::make_pair(UseMain, &F));
@@ -200,11 +278,6 @@ bool FixFunctionBitcasts::runOnModule(Module &M) {
if (!Ty)
continue;
- // Bitcasted vararg functions occur in Emscripten's implementation of
- // EM_ASM, so suppress wrappers for them for now.
- if (TemporaryWorkarounds && (Ty->isVarArg() || F->isVarArg()))
- continue;
-
auto Pair = Wrappers.insert(std::make_pair(std::make_pair(F, Ty), nullptr));
if (Pair.second)
Pair.first->second = CreateWrapper(F, Ty);
diff --git a/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp b/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp
index bea027be7711..108f2879a071 100644
--- a/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp
@@ -8,8 +8,8 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// This file implements a pass that transforms irreducible control flow
-/// into reducible control flow. Irreducible control flow means multiple-entry
+/// This file implements a pass that transforms irreducible control flow into
+/// reducible control flow. Irreducible control flow means multiple-entry
/// loops; they appear as CFG cycles that are not recorded in MachineLoopInfo
/// due to being unnatural.
///
@@ -17,12 +17,36 @@
/// it linearizes control flow, turning diamonds into two triangles, which is
/// both unnecessary and undesirable for WebAssembly.
///
-/// TODO: The transformation implemented here handles all irreducible control
-/// flow, without exponential code-size expansion, though it does so by creating
-/// inefficient code in many cases. Ideally, we should add other
-/// transformations, including code-duplicating cases, which can be more
-/// efficient in common cases, and they can fall back to this conservative
-/// implementation as needed.
+/// The big picture: Ignoring natural loops (seeing them monolithically), we
+/// find all the blocks which can return to themselves ("loopers"). Loopers
+/// reachable from the non-loopers are loop entries: if there are 2 or more,
+/// then we have irreducible control flow. We fix that as follows: a new block
+/// is created that can dispatch to each of the loop entries, based on the
+/// value of a label "helper" variable, and we replace direct branches to the
+/// entries with assignments to the label variable and a branch to the dispatch
+/// block. Then the dispatch block is the single entry in a new natural loop.
+///
+/// This is similar to what the Relooper [1] does, both identify looping code
+/// that requires multiple entries, and resolve it in a similar way. In
+/// Relooper terminology, we implement a Multiple shape in a Loop shape. Note
+/// also that like the Relooper, we implement a "minimal" intervention: we only
+/// use the "label" helper for the blocks we absolutely must and no others. We
+/// also prioritize code size and do not perform node splitting (i.e. we don't
+/// duplicate code in order to resolve irreducibility).
+///
+/// The difference between this code and the Relooper is that the Relooper also
+/// generates ifs and loops and works in a recursive manner, knowing at each
+/// point what the entries are, and recursively breaks down the problem. Here
+/// we just want to resolve irreducible control flow, and we also want to use
+/// as much LLVM infrastructure as possible. So we use the MachineLoopInfo to
+/// identify natural loops, etc., and we start with the whole CFG and must
+/// identify both the looping code and its entries.
+///
+/// [1] Alon Zakai. 2011. Emscripten: an LLVM-to-JavaScript compiler. In
+/// Proceedings of the ACM international conference companion on Object oriented
+/// programming systems languages and applications companion (SPLASH '11). ACM,
+/// New York, NY, USA, 301-312. DOI=10.1145/2048147.2048224
+/// http://doi.acm.org/10.1145/2048147.2048224
///
//===----------------------------------------------------------------------===//
@@ -46,141 +70,203 @@ using namespace llvm;
#define DEBUG_TYPE "wasm-fix-irreducible-control-flow"
namespace {
-class WebAssemblyFixIrreducibleControlFlow final : public MachineFunctionPass {
- StringRef getPassName() const override {
- return "WebAssembly Fix Irreducible Control Flow";
- }
-
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesCFG();
- AU.addRequired<MachineDominatorTree>();
- AU.addPreserved<MachineDominatorTree>();
- AU.addRequired<MachineLoopInfo>();
- AU.addPreserved<MachineLoopInfo>();
- MachineFunctionPass::getAnalysisUsage(AU);
- }
-
- bool runOnMachineFunction(MachineFunction &MF) override;
-
- bool VisitLoop(MachineFunction &MF, MachineLoopInfo &MLI, MachineLoop *Loop);
+class LoopFixer {
public:
- static char ID; // Pass identification, replacement for typeid
- WebAssemblyFixIrreducibleControlFlow() : MachineFunctionPass(ID) {}
-};
-} // end anonymous namespace
+ LoopFixer(MachineFunction &MF, MachineLoopInfo &MLI, MachineLoop *Loop)
+ : MF(MF), MLI(MLI), Loop(Loop) {}
-char WebAssemblyFixIrreducibleControlFlow::ID = 0;
-INITIALIZE_PASS(WebAssemblyFixIrreducibleControlFlow, DEBUG_TYPE,
- "Removes irreducible control flow", false, false)
+ // Run the fixer on the given inputs. Returns whether changes were made.
+ bool run();
-FunctionPass *llvm::createWebAssemblyFixIrreducibleControlFlow() {
- return new WebAssemblyFixIrreducibleControlFlow();
-}
+private:
+ MachineFunction &MF;
+ MachineLoopInfo &MLI;
+ MachineLoop *Loop;
-namespace {
+ MachineBasicBlock *Header;
+ SmallPtrSet<MachineBasicBlock *, 4> LoopBlocks;
-/// A utility for walking the blocks of a loop, handling a nested inner
-/// loop as a monolithic conceptual block.
-class MetaBlock {
- MachineBasicBlock *Block;
- SmallVector<MachineBasicBlock *, 2> Preds;
- SmallVector<MachineBasicBlock *, 2> Succs;
+ using BlockSet = SmallPtrSet<MachineBasicBlock *, 4>;
+ DenseMap<MachineBasicBlock *, BlockSet> Reachable;
-public:
- explicit MetaBlock(MachineBasicBlock *MBB)
- : Block(MBB), Preds(MBB->pred_begin(), MBB->pred_end()),
- Succs(MBB->succ_begin(), MBB->succ_end()) {}
+ // The worklist contains pairs of recent additions, (a, b), where we just
+ // added a link a => b.
+ using BlockPair = std::pair<MachineBasicBlock *, MachineBasicBlock *>;
+ SmallVector<BlockPair, 4> WorkList;
- explicit MetaBlock(MachineLoop *Loop) : Block(Loop->getHeader()) {
- Loop->getExitBlocks(Succs);
- for (MachineBasicBlock *Pred : Block->predecessors())
- if (!Loop->contains(Pred))
- Preds.push_back(Pred);
+ // Get a canonical block to represent a block or a loop: the block, or if in
+ // an inner loop, the loop header, of it in an outer loop scope, we can
+ // ignore it. We need to call this on all blocks we work on.
+ MachineBasicBlock *canonicalize(MachineBasicBlock *MBB) {
+ MachineLoop *InnerLoop = MLI.getLoopFor(MBB);
+ if (InnerLoop == Loop) {
+ return MBB;
+ } else {
+ // This is either in an outer or an inner loop, and not in ours.
+ if (!LoopBlocks.count(MBB)) {
+ // It's in outer code, ignore it.
+ return nullptr;
+ }
+ assert(InnerLoop);
+ // It's in an inner loop, canonicalize it to the header of that loop.
+ return InnerLoop->getHeader();
+ }
}
- MachineBasicBlock *getBlock() const { return Block; }
-
- const SmallVectorImpl<MachineBasicBlock *> &predecessors() const {
- return Preds;
- }
- const SmallVectorImpl<MachineBasicBlock *> &successors() const {
- return Succs;
+ // For a successor we can additionally ignore it if it's a branch back to a
+ // natural loop top, as when we are in the scope of a loop, we just care
+ // about internal irreducibility, and can ignore the loop we are in. We need
+ // to call this on all blocks in a context where they are a successor.
+ MachineBasicBlock *canonicalizeSuccessor(MachineBasicBlock *MBB) {
+ if (Loop && MBB == Loop->getHeader()) {
+ // Ignore branches going to the loop's natural header.
+ return nullptr;
+ }
+ return canonicalize(MBB);
}
- bool operator==(const MetaBlock &MBB) { return Block == MBB.Block; }
- bool operator!=(const MetaBlock &MBB) { return Block != MBB.Block; }
+ // Potentially insert a new reachable edge, and if so, note it as further
+ // work.
+ void maybeInsert(MachineBasicBlock *MBB, MachineBasicBlock *Succ) {
+ assert(MBB == canonicalize(MBB));
+ assert(Succ);
+ // Succ may not be interesting as a sucessor.
+ Succ = canonicalizeSuccessor(Succ);
+ if (!Succ)
+ return;
+ if (Reachable[MBB].insert(Succ).second) {
+ // For there to be further work, it means that we have
+ // X => MBB => Succ
+ // for some other X, and in that case X => Succ would be a new edge for
+ // us to discover later. However, if we don't care about MBB as a
+ // successor, then we don't care about that anyhow.
+ if (canonicalizeSuccessor(MBB)) {
+ WorkList.emplace_back(MBB, Succ);
+ }
+ }
+ }
};
-class SuccessorList final : public MetaBlock {
- size_t Index;
- size_t Num;
+bool LoopFixer::run() {
+ Header = Loop ? Loop->getHeader() : &*MF.begin();
-public:
- explicit SuccessorList(MachineBasicBlock *MBB)
- : MetaBlock(MBB), Index(0), Num(successors().size()) {}
+ // Identify all the blocks in this loop scope.
+ if (Loop) {
+ for (auto *MBB : Loop->getBlocks()) {
+ LoopBlocks.insert(MBB);
+ }
+ } else {
+ for (auto &MBB : MF) {
+ LoopBlocks.insert(&MBB);
+ }
+ }
- explicit SuccessorList(MachineLoop *Loop)
- : MetaBlock(Loop), Index(0), Num(successors().size()) {}
+ // Compute which (canonicalized) blocks each block can reach.
- bool HasNext() const { return Index != Num; }
+ // Add all the initial work.
+ for (auto *MBB : LoopBlocks) {
+ MachineLoop *InnerLoop = MLI.getLoopFor(MBB);
- MachineBasicBlock *Next() {
- assert(HasNext());
- return successors()[Index++];
+ if (InnerLoop == Loop) {
+ for (auto *Succ : MBB->successors()) {
+ maybeInsert(MBB, Succ);
+ }
+ } else {
+ // It can't be in an outer loop - we loop on LoopBlocks - and so it must
+ // be an inner loop.
+ assert(InnerLoop);
+ // Check if we are the canonical block for this loop.
+ if (canonicalize(MBB) != MBB) {
+ continue;
+ }
+ // The successors are those of the loop.
+ SmallVector<MachineBasicBlock *, 2> ExitBlocks;
+ InnerLoop->getExitBlocks(ExitBlocks);
+ for (auto *Succ : ExitBlocks) {
+ maybeInsert(MBB, Succ);
+ }
+ }
}
-};
-} // end anonymous namespace
+ // Do work until we are all done.
+ while (!WorkList.empty()) {
+ MachineBasicBlock *MBB;
+ MachineBasicBlock *Succ;
+ std::tie(MBB, Succ) = WorkList.pop_back_val();
+ // The worklist item is an edge we just added, so it must have valid blocks
+ // (and not something canonicalized to nullptr).
+ assert(MBB);
+ assert(Succ);
+ // The successor in that pair must also be a valid successor.
+ assert(MBB == canonicalizeSuccessor(MBB));
+ // We recently added MBB => Succ, and that means we may have enabled
+ // Pred => MBB => Succ. Check all the predecessors. Note that our loop here
+ // is correct for both a block and a block representing a loop, as the loop
+ // is natural and so the predecessors are all predecessors of the loop
+ // header, which is the block we have here.
+ for (auto *Pred : MBB->predecessors()) {
+ // Canonicalize, make sure it's relevant, and check it's not the same
+ // block (an update to the block itself doesn't help compute that same
+ // block).
+ Pred = canonicalize(Pred);
+ if (Pred && Pred != MBB) {
+ maybeInsert(Pred, Succ);
+ }
+ }
+ }
-bool WebAssemblyFixIrreducibleControlFlow::VisitLoop(MachineFunction &MF,
- MachineLoopInfo &MLI,
- MachineLoop *Loop) {
- MachineBasicBlock *Header = Loop ? Loop->getHeader() : &*MF.begin();
- SetVector<MachineBasicBlock *> RewriteSuccs;
+ // It's now trivial to identify the loopers.
+ SmallPtrSet<MachineBasicBlock *, 4> Loopers;
+ for (auto MBB : LoopBlocks) {
+ if (Reachable[MBB].count(MBB)) {
+ Loopers.insert(MBB);
+ }
+ }
+ // The header cannot be a looper. At the toplevel, LLVM does not allow the
+ // entry to be in a loop, and in a natural loop we should ignore the header.
+ assert(Loopers.count(Header) == 0);
- // DFS through Loop's body, looking for irreducible control flow. Loop is
- // natural, and we stay in its body, and we treat any nested loops
- // monolithically, so any cycles we encounter indicate irreducibility.
- SmallPtrSet<MachineBasicBlock *, 8> OnStack;
- SmallPtrSet<MachineBasicBlock *, 8> Visited;
- SmallVector<SuccessorList, 4> LoopWorklist;
- LoopWorklist.push_back(SuccessorList(Header));
- OnStack.insert(Header);
- Visited.insert(Header);
- while (!LoopWorklist.empty()) {
- SuccessorList &Top = LoopWorklist.back();
- if (Top.HasNext()) {
- MachineBasicBlock *Next = Top.Next();
- if (Next == Header || (Loop && !Loop->contains(Next)))
- continue;
- if (LLVM_LIKELY(OnStack.insert(Next).second)) {
- if (!Visited.insert(Next).second) {
- OnStack.erase(Next);
- continue;
- }
- MachineLoop *InnerLoop = MLI.getLoopFor(Next);
- if (InnerLoop != Loop)
- LoopWorklist.push_back(SuccessorList(InnerLoop));
- else
- LoopWorklist.push_back(SuccessorList(Next));
- } else {
- RewriteSuccs.insert(Top.getBlock());
+ // Find the entries, loopers reachable from non-loopers.
+ SmallPtrSet<MachineBasicBlock *, 4> Entries;
+ SmallVector<MachineBasicBlock *, 4> SortedEntries;
+ for (auto *Looper : Loopers) {
+ for (auto *Pred : Looper->predecessors()) {
+ Pred = canonicalize(Pred);
+ if (Pred && !Loopers.count(Pred)) {
+ Entries.insert(Looper);
+ SortedEntries.push_back(Looper);
+ break;
}
- continue;
}
- OnStack.erase(Top.getBlock());
- LoopWorklist.pop_back();
}
- // Most likely, we didn't find any irreducible control flow.
- if (LLVM_LIKELY(RewriteSuccs.empty()))
+ // Check if we found irreducible control flow.
+ if (LLVM_LIKELY(Entries.size() <= 1))
return false;
- LLVM_DEBUG(dbgs() << "Irreducible control flow detected!\n");
+ // Sort the entries to ensure a deterministic build.
+ llvm::sort(SortedEntries,
+ [&](const MachineBasicBlock *A, const MachineBasicBlock *B) {
+ auto ANum = A->getNumber();
+ auto BNum = B->getNumber();
+ return ANum < BNum;
+ });
- // Ok. We have irreducible control flow! Create a dispatch block which will
- // contains a jump table to any block in the problematic set of blocks.
+#ifndef NDEBUG
+ for (auto Block : SortedEntries)
+ assert(Block->getNumber() != -1);
+ if (SortedEntries.size() > 1) {
+ for (auto I = SortedEntries.begin(), E = SortedEntries.end() - 1;
+ I != E; ++I) {
+ auto ANum = (*I)->getNumber();
+ auto BNum = (*(std::next(I)))->getNumber();
+ assert(ANum != BNum);
+ }
+ }
+#endif
+
+ // Create a dispatch block which will contain a jump table to the entries.
MachineBasicBlock *Dispatch = MF.CreateMachineBasicBlock();
MF.insert(MF.end(), Dispatch);
MLI.changeLoopFor(Dispatch, Loop);
@@ -196,43 +282,43 @@ bool WebAssemblyFixIrreducibleControlFlow::VisitLoop(MachineFunction &MF,
unsigned Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
MIB.addReg(Reg);
- // Collect all the blocks which need to have their successors rewritten,
- // add the successors to the jump table, and remember their index.
+ // Compute the indices in the superheader, one for each bad block, and
+ // add them as successors.
DenseMap<MachineBasicBlock *, unsigned> Indices;
- SmallVector<MachineBasicBlock *, 4> SuccWorklist(RewriteSuccs.begin(),
- RewriteSuccs.end());
- while (!SuccWorklist.empty()) {
- MachineBasicBlock *MBB = SuccWorklist.pop_back_val();
+ for (auto *MBB : SortedEntries) {
auto Pair = Indices.insert(std::make_pair(MBB, 0));
- if (!Pair.second)
+ if (!Pair.second) {
continue;
+ }
unsigned Index = MIB.getInstr()->getNumExplicitOperands() - 1;
- LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has index " << Index
- << "\n");
-
Pair.first->second = Index;
- for (auto Pred : MBB->predecessors())
- RewriteSuccs.insert(Pred);
MIB.addMBB(MBB);
Dispatch->addSuccessor(MBB);
+ }
- MetaBlock Meta(MBB);
- for (auto *Succ : Meta.successors())
- if (Succ != Header && (!Loop || Loop->contains(Succ)))
- SuccWorklist.push_back(Succ);
+ // Rewrite the problematic successors for every block that wants to reach the
+ // bad blocks. For simplicity, we just introduce a new block for every edge
+ // we need to rewrite. (Fancier things are possible.)
+
+ SmallVector<MachineBasicBlock *, 4> AllPreds;
+ for (auto *MBB : SortedEntries) {
+ for (auto *Pred : MBB->predecessors()) {
+ if (Pred != Dispatch) {
+ AllPreds.push_back(Pred);
+ }
+ }
}
- // Rewrite the problematic successors for every block in RewriteSuccs.
- // For simplicity, we just introduce a new block for every edge we need to
- // rewrite. Fancier things are possible.
- for (MachineBasicBlock *MBB : RewriteSuccs) {
+ for (MachineBasicBlock *MBB : AllPreds) {
DenseMap<MachineBasicBlock *, MachineBasicBlock *> Map;
for (auto *Succ : MBB->successors()) {
- if (!Indices.count(Succ))
+ if (!Entries.count(Succ)) {
continue;
+ }
+ // This is a successor we need to rewrite.
MachineBasicBlock *Split = MF.CreateMachineBasicBlock();
MF.insert(MBB->isLayoutSuccessor(Succ) ? MachineFunction::iterator(Succ)
: MF.end(),
@@ -266,6 +352,55 @@ bool WebAssemblyFixIrreducibleControlFlow::VisitLoop(MachineFunction &MF,
return true;
}
+class WebAssemblyFixIrreducibleControlFlow final : public MachineFunctionPass {
+ StringRef getPassName() const override {
+ return "WebAssembly Fix Irreducible Control Flow";
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ AU.addRequired<MachineDominatorTree>();
+ AU.addPreserved<MachineDominatorTree>();
+ AU.addRequired<MachineLoopInfo>();
+ AU.addPreserved<MachineLoopInfo>();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ bool runIteration(MachineFunction &MF, MachineLoopInfo &MLI) {
+ // Visit the function body, which is identified as a null loop.
+ if (LoopFixer(MF, MLI, nullptr).run()) {
+ return true;
+ }
+
+ // Visit all the loops.
+ SmallVector<MachineLoop *, 8> Worklist(MLI.begin(), MLI.end());
+ while (!Worklist.empty()) {
+ MachineLoop *Loop = Worklist.pop_back_val();
+ Worklist.append(Loop->begin(), Loop->end());
+ if (LoopFixer(MF, MLI, Loop).run()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+public:
+ static char ID; // Pass identification, replacement for typeid
+ WebAssemblyFixIrreducibleControlFlow() : MachineFunctionPass(ID) {}
+};
+} // end anonymous namespace
+
+char WebAssemblyFixIrreducibleControlFlow::ID = 0;
+INITIALIZE_PASS(WebAssemblyFixIrreducibleControlFlow, DEBUG_TYPE,
+ "Removes irreducible control flow", false, false)
+
+FunctionPass *llvm::createWebAssemblyFixIrreducibleControlFlow() {
+ return new WebAssemblyFixIrreducibleControlFlow();
+}
+
bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction(
MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** Fixing Irreducible Control Flow **********\n"
@@ -275,24 +410,19 @@ bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction(
bool Changed = false;
auto &MLI = getAnalysis<MachineLoopInfo>();
- // Visit the function body, which is identified as a null loop.
- Changed |= VisitLoop(MF, MLI, nullptr);
-
- // Visit all the loops.
- SmallVector<MachineLoop *, 8> Worklist(MLI.begin(), MLI.end());
- while (!Worklist.empty()) {
- MachineLoop *CurLoop = Worklist.pop_back_val();
- Worklist.append(CurLoop->begin(), CurLoop->end());
- Changed |= VisitLoop(MF, MLI, CurLoop);
- }
-
- // If we made any changes, completely recompute everything.
- if (LLVM_UNLIKELY(Changed)) {
- LLVM_DEBUG(dbgs() << "Recomputing dominators and loops.\n");
+ // When we modify something, bail out and recompute MLI, then start again, as
+ // we create a new natural loop when we resolve irreducible control flow, and
+ // other loops may become nested in it, etc. In practice this is not an issue
+ // because irreducible control flow is rare, only very few cycles are needed
+ // here.
+ while (LLVM_UNLIKELY(runIteration(MF, MLI))) {
+ // We rewrote part of the function; recompute MLI and start again.
+ LLVM_DEBUG(dbgs() << "Recomputing loops.\n");
MF.getRegInfo().invalidateLiveness();
MF.RenumberBlocks();
getAnalysis<MachineDominatorTree>().runOnMachineFunction(MF);
MLI.runOnMachineFunction(MF);
+ Changed = true;
}
return Changed;
diff --git a/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
index 052c94e9d6a9..2d5aff28d27b 100644
--- a/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp
@@ -30,6 +30,7 @@
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
@@ -42,8 +43,7 @@ using namespace llvm;
/// require stricter alignment than the stack pointer itself. Because we need
/// to shift the stack pointer by some unknown amount to force the alignment,
/// we need to record the value of the stack pointer on entry to the function.
-bool WebAssemblyFrameLowering::hasBP(
- const MachineFunction &MF) const {
+bool WebAssemblyFrameLowering::hasBP(const MachineFunction &MF) const {
const auto *RegInfo =
MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
return RegInfo->needsStackRealignment(MF);
@@ -78,36 +78,60 @@ bool WebAssemblyFrameLowering::hasReservedCallFrame(
return !MF.getFrameInfo().hasVarSizedObjects();
}
+// Returns true if this function needs a local user-space stack pointer for its
+// local frame (not for exception handling).
+bool WebAssemblyFrameLowering::needsSPForLocalFrame(
+ const MachineFunction &MF) const {
+ auto &MFI = MF.getFrameInfo();
+ return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF);
+}
+
+// In function with EH pads, we need to make a copy of the value of
+// __stack_pointer global in SP32 register, in order to use it when restoring
+// __stack_pointer after an exception is caught.
+bool WebAssemblyFrameLowering::needsPrologForEH(
+ const MachineFunction &MF) const {
+ auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType();
+ return EHType == ExceptionHandling::Wasm &&
+ MF.getFunction().hasPersonalityFn() && MF.getFrameInfo().hasCalls();
+}
/// Returns true if this function needs a local user-space stack pointer.
/// Unlike a machine stack pointer, the wasm user stack pointer is a global
/// variable, so it is loaded into a register in the prolog.
-bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF,
- const MachineFrameInfo &MFI) const {
- return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF);
+bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF) const {
+ return needsSPForLocalFrame(MF) || needsPrologForEH(MF);
}
/// Returns true if the local user-space stack pointer needs to be written back
-/// to memory by this function (this is not meaningful if needsSP is false). If
-/// false, the stack red zone can be used and only a local SP is needed.
+/// to __stack_pointer global by this function (this is not meaningful if
+/// needsSP is false). If false, the stack red zone can be used and only a local
+/// SP is needed.
bool WebAssemblyFrameLowering::needsSPWriteback(
- const MachineFunction &MF, const MachineFrameInfo &MFI) const {
- assert(needsSP(MF, MFI));
- return MFI.getStackSize() > RedZoneSize || MFI.hasCalls() ||
- MF.getFunction().hasFnAttribute(Attribute::NoRedZone);
+ const MachineFunction &MF) const {
+ auto &MFI = MF.getFrameInfo();
+ assert(needsSP(MF));
+ // When we don't need a local stack pointer for its local frame but only to
+ // support EH, we don't need to write SP back in the epilog, because we don't
+ // bump down the stack pointer in the prolog. We need to write SP back in the
+ // epilog only if
+ // 1. We need SP not only for EH support but also because we actually use
+ // stack or we have a frame address taken.
+ // 2. We cannot use the red zone.
+ bool CanUseRedZone = MFI.getStackSize() <= RedZoneSize && !MFI.hasCalls() &&
+ !MF.getFunction().hasFnAttribute(Attribute::NoRedZone);
+ return needsSPForLocalFrame(MF) && !CanUseRedZone;
}
-static void writeSPToMemory(unsigned SrcReg, MachineFunction &MF,
- MachineBasicBlock &MBB,
- MachineBasicBlock::iterator &InsertAddr,
- MachineBasicBlock::iterator &InsertStore,
- const DebugLoc &DL) {
+void WebAssemblyFrameLowering::writeSPToGlobal(
+ unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const {
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
const char *ES = "__stack_pointer";
auto *SPSymbol = MF.createExternalSymbolName(ES);
- BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::SET_GLOBAL_I32))
- .addExternalSymbol(SPSymbol)
+ BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::GLOBAL_SET_I32))
+ .addExternalSymbol(SPSymbol, WebAssemblyII::MO_SYMBOL_GLOBAL)
.addReg(SrcReg);
}
@@ -119,9 +143,9 @@ WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
"Call frame pseudos should only be used for dynamic stack adjustment");
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
- needsSPWriteback(MF, MF.getFrameInfo())) {
+ needsSPWriteback(MF)) {
DebugLoc DL = I->getDebugLoc();
- writeSPToMemory(WebAssembly::SP32, MF, MBB, I, I, DL);
+ writeSPToGlobal(WebAssembly::SP32, MF, MBB, I, DL);
}
return MBB.erase(I);
}
@@ -133,7 +157,8 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
assert(MFI.getCalleeSavedInfo().empty() &&
"WebAssembly should not have callee-saved registers");
- if (!needsSP(MF, MFI)) return;
+ if (!needsSP(MF))
+ return;
uint64_t StackSize = MFI.getStackSize();
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
@@ -152,8 +177,8 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
const char *ES = "__stack_pointer";
auto *SPSymbol = MF.createExternalSymbolName(ES);
- BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::GET_GLOBAL_I32), SPReg)
- .addExternalSymbol(SPSymbol);
+ BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::GLOBAL_GET_I32), SPReg)
+ .addExternalSymbol(SPSymbol, WebAssemblyII::MO_SYMBOL_GLOBAL);
bool HasBP = hasBP(MF);
if (HasBP) {
@@ -177,7 +202,7 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
unsigned BitmaskReg = MRI.createVirtualRegister(PtrRC);
unsigned Alignment = MFI.getMaxAlignment();
assert((1u << countTrailingZeros(Alignment)) == Alignment &&
- "Alignment must be a power of 2");
+ "Alignment must be a power of 2");
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), BitmaskReg)
.addImm((int)~(Alignment - 1));
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::AND_I32),
@@ -189,20 +214,19 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
// Unlike most conventional targets (where FP points to the saved FP),
// FP points to the bottom of the fixed-size locals, so we can use positive
// offsets in load/store instructions.
- BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY),
- WebAssembly::FP32)
+ BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), WebAssembly::FP32)
.addReg(WebAssembly::SP32);
}
- if (StackSize && needsSPWriteback(MF, MFI)) {
- writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, InsertPt, DL);
+ if (StackSize && needsSPWriteback(MF)) {
+ writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPt, DL);
}
}
void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
- auto &MFI = MF.getFrameInfo();
- uint64_t StackSize = MFI.getStackSize();
- if (!needsSP(MF, MFI) || !needsSPWriteback(MF, MFI)) return;
+ uint64_t StackSize = MF.getFrameInfo().getStackSize();
+ if (!needsSP(MF) || !needsSPWriteback(MF))
+ return;
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
auto &MRI = MF.getRegInfo();
auto InsertPt = MBB.getFirstTerminator();
@@ -214,7 +238,6 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
// Restore the stack pointer. If we had fixed-size locals, add the offset
// subtracted in the prolog.
unsigned SPReg = 0;
- MachineBasicBlock::iterator InsertAddr = InsertPt;
if (hasBP(MF)) {
auto FI = MF.getInfo<WebAssemblyFunctionInfo>();
SPReg = FI->getBasePointerVreg();
@@ -222,9 +245,8 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
const TargetRegisterClass *PtrRC =
MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
unsigned OffsetReg = MRI.createVirtualRegister(PtrRC);
- InsertAddr =
- BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
- .addImm(StackSize);
+ BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
+ .addImm(StackSize);
// In the epilog we don't need to write the result back to the SP32 physreg
// because it won't be used again. We can use a stackified register instead.
SPReg = MRI.createVirtualRegister(PtrRC);
@@ -235,5 +257,5 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32;
}
- writeSPToMemory(SPReg, MF, MBB, InsertAddr, InsertPt, DL);
+ writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL);
}
diff --git a/lib/Target/WebAssembly/WebAssemblyFrameLowering.h b/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
index fe23e418a3f1..c6fa8261b03f 100644
--- a/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
+++ b/lib/Target/WebAssembly/WebAssemblyFrameLowering.h
@@ -22,9 +22,10 @@ namespace llvm {
class MachineFrameInfo;
class WebAssemblyFrameLowering final : public TargetFrameLowering {
- public:
+public:
/// Size of the red zone for the user stack (leaf functions can use this much
- /// space below the stack pointer without writing it back to memory).
+ /// space below the stack pointer without writing it back to __stack_pointer
+ /// global).
// TODO: (ABI) Revisit and decide how large it should be.
static const size_t RedZoneSize = 128;
@@ -34,9 +35,9 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
/*TransientStackAlignment=*/16,
/*StackRealignable=*/true) {}
- MachineBasicBlock::iterator eliminateCallFramePseudoInstr(
- MachineFunction &MF, MachineBasicBlock &MBB,
- MachineBasicBlock::iterator I) const override;
+ MachineBasicBlock::iterator
+ eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator I) const override;
/// These methods insert prolog and epilog code into the function.
void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
@@ -45,13 +46,21 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
bool hasFP(const MachineFunction &MF) const override;
bool hasReservedCallFrame(const MachineFunction &MF) const override;
- private:
+ bool needsPrologForEH(const MachineFunction &MF) const;
+
+ /// Write SP back to __stack_pointer global.
+ void writeSPToGlobal(unsigned SrcReg, MachineFunction &MF,
+ MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator &InsertStore,
+ const DebugLoc &DL) const;
+
+private:
bool hasBP(const MachineFunction &MF) const;
- bool needsSP(const MachineFunction &MF, const MachineFrameInfo &MFI) const;
- bool needsSPWriteback(const MachineFunction &MF,
- const MachineFrameInfo &MFI) const;
+ bool needsSPForLocalFrame(const MachineFunction &MF) const;
+ bool needsSP(const MachineFunction &MF) const;
+ bool needsSPWriteback(const MachineFunction &MF) const;
};
-} // end namespace llvm
+} // end namespace llvm
#endif
diff --git a/lib/Target/WebAssembly/WebAssemblyISD.def b/lib/Target/WebAssembly/WebAssemblyISD.def
index c12550feabbb..e987d7f7f43a 100644
--- a/lib/Target/WebAssembly/WebAssemblyISD.def
+++ b/lib/Target/WebAssembly/WebAssemblyISD.def
@@ -21,5 +21,10 @@ HANDLE_NODETYPE(ARGUMENT)
HANDLE_NODETYPE(Wrapper)
HANDLE_NODETYPE(BR_IF)
HANDLE_NODETYPE(BR_TABLE)
+HANDLE_NODETYPE(SHUFFLE)
+HANDLE_NODETYPE(VEC_SHL)
+HANDLE_NODETYPE(VEC_SHR_S)
+HANDLE_NODETYPE(VEC_SHR_U)
+HANDLE_NODETYPE(THROW)
// add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here...
diff --git a/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
index fdf3a30a5c0e..0a7464cedc90 100644
--- a/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
@@ -48,6 +48,10 @@ public:
}
bool runOnMachineFunction(MachineFunction &MF) override {
+ LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"
+ "********** Function: "
+ << MF.getName() << '\n');
+
ForCodeSize = MF.getFunction().hasFnAttribute(Attribute::OptimizeForSize) ||
MF.getFunction().hasFnAttribute(Attribute::MinSize);
Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();
diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 283e703e1f6c..003848e34227 100644
--- a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -21,8 +21,10 @@
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAG.h"
+#include "llvm/CodeGen/WasmEHFuncInfo.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/Function.h"
@@ -42,6 +44,8 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
// Booleans always contain 0 or 1.
setBooleanContents(ZeroOrOneBooleanContent);
+ // Except in SIMD vectors
+ setBooleanVectorContents(ZeroOrNegativeOneBooleanContent);
// WebAssembly does not produce floating-point exceptions on normal floating
// point operations.
setHasFloatingPointExceptions(false);
@@ -60,6 +64,10 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
addRegisterClass(MVT::v8i16, &WebAssembly::V128RegClass);
addRegisterClass(MVT::v4i32, &WebAssembly::V128RegClass);
addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass);
+ if (Subtarget->hasUnimplementedSIMD128()) {
+ addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass);
+ addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass);
+ }
}
// Compute derived properties from the register classes.
computeRegisterProperties(Subtarget->getRegisterInfo());
@@ -77,7 +85,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
setOperationAction(ISD::VACOPY, MVT::Other, Expand);
setOperationAction(ISD::VAEND, MVT::Other, Expand);
- for (auto T : {MVT::f32, MVT::f64}) {
+ for (auto T : {MVT::f32, MVT::f64, MVT::v4f32, MVT::v2f64}) {
// Don't expand the floating-point types to constant pools.
setOperationAction(ISD::ConstantFP, T, Legal);
// Expand floating-point comparisons.
@@ -85,17 +93,17 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE})
setCondCodeAction(CC, T, Expand);
// Expand floating-point library function operators.
- for (auto Op : {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM,
- ISD::FMA})
+ for (auto Op :
+ {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM, ISD::FMA})
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})
setOperationAction(Op, T, Legal);
- // Support minnan and maxnan, which otherwise default to expand.
- setOperationAction(ISD::FMINNAN, T, Legal);
- setOperationAction(ISD::FMAXNAN, T, Legal);
+ // Support minimum and maximum, which otherwise default to expand.
+ setOperationAction(ISD::FMINIMUM, T, Legal);
+ setOperationAction(ISD::FMAXIMUM, T, Legal);
// WebAssembly currently has no builtin f16 support.
setOperationAction(ISD::FP16_TO_FP, T, Expand);
setOperationAction(ISD::FP_TO_FP16, T, Expand);
@@ -103,24 +111,75 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
setTruncStoreAction(T, MVT::f16, Expand);
}
- for (auto T : {MVT::i32, MVT::i64}) {
- // Expand unavailable integer operations.
- for (auto Op :
- {ISD::BSWAP, ISD::SMUL_LOHI, ISD::UMUL_LOHI,
- ISD::MULHS, ISD::MULHU, ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS,
- ISD::SRA_PARTS, ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC,
- ISD::SUBE}) {
+ // Support saturating add for i8x16 and i16x8
+ if (Subtarget->hasSIMD128())
+ for (auto T : {MVT::v16i8, MVT::v8i16})
+ for (auto Op : {ISD::SADDSAT, ISD::UADDSAT})
+ setOperationAction(Op, T, Legal);
+
+ // Expand unavailable integer operations.
+ for (auto Op :
+ {ISD::BSWAP, ISD::SMUL_LOHI, ISD::UMUL_LOHI, ISD::MULHS, ISD::MULHU,
+ ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS, ISD::SRA_PARTS,
+ ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC, ISD::SUBE}) {
+ for (auto T : {MVT::i32, MVT::i64}) {
setOperationAction(Op, T, Expand);
}
+ if (Subtarget->hasSIMD128()) {
+ for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) {
+ setOperationAction(Op, T, Expand);
+ }
+ if (Subtarget->hasUnimplementedSIMD128()) {
+ setOperationAction(Op, MVT::v2i64, Expand);
+ }
+ }
+ }
+
+ // There is no i64x2.mul instruction
+ setOperationAction(ISD::MUL, MVT::v2i64, Expand);
+
+ // We have custom shuffle lowering to expose the shuffle mask
+ if (Subtarget->hasSIMD128()) {
+ for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) {
+ setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom);
+ }
+ if (Subtarget->hasUnimplementedSIMD128()) {
+ setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v2i64, Custom);
+ setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v2f64, Custom);
+ }
+ }
+
+ // Custom lowering since wasm shifts must have a scalar shift amount
+ if (Subtarget->hasSIMD128()) {
+ for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32})
+ for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL})
+ setOperationAction(Op, T, Custom);
+ if (Subtarget->hasUnimplementedSIMD128())
+ for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL})
+ setOperationAction(Op, MVT::v2i64, Custom);
}
+ // There are no select instructions for vectors
+ if (Subtarget->hasSIMD128())
+ for (auto Op : {ISD::VSELECT, ISD::SELECT_CC, ISD::SELECT}) {
+ for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32})
+ setOperationAction(Op, T, Expand);
+ if (Subtarget->hasUnimplementedSIMD128())
+ for (auto T : {MVT::v2i64, MVT::v2f64})
+ setOperationAction(Op, T, Expand);
+ }
+
// As a special case, these operators use the type to mean the type to
// sign-extend from.
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand);
if (!Subtarget->hasSignExt()) {
+ // Sign extends are legal only when extending a vector extract
+ auto Action = Subtarget->hasSIMD128() ? Custom : Expand;
for (auto T : {MVT::i8, MVT::i16, MVT::i32})
- setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand);
+ setOperationAction(ISD::SIGN_EXTEND_INREG, T, Action);
}
+ for (auto T : MVT::integer_vector_valuetypes())
+ setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand);
// Dynamic stack allocation: use the default expansion.
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
@@ -142,21 +201,72 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
// - Floating-point extending loads.
// - Floating-point truncating stores.
// - i1 extending loads.
+ // - extending/truncating SIMD loads/stores
setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand);
setTruncStoreAction(MVT::f64, MVT::f32, Expand);
for (auto T : MVT::integer_valuetypes())
for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
setLoadExtAction(Ext, T, MVT::i1, Promote);
+ if (Subtarget->hasSIMD128()) {
+ for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64, MVT::v4f32,
+ MVT::v2f64}) {
+ for (auto MemT : MVT::vector_valuetypes()) {
+ if (MVT(T) != MemT) {
+ setTruncStoreAction(T, MemT, Expand);
+ for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
+ setLoadExtAction(Ext, T, MemT, Expand);
+ }
+ }
+ }
+ }
+
+ // Expand additional SIMD ops that V8 hasn't implemented yet
+ if (Subtarget->hasSIMD128() && !Subtarget->hasUnimplementedSIMD128()) {
+ setOperationAction(ISD::FSQRT, MVT::v4f32, Expand);
+ setOperationAction(ISD::FDIV, MVT::v4f32, Expand);
+ }
+
+ // Custom lower lane accesses to expand out variable indices
+ if (Subtarget->hasSIMD128()) {
+ for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) {
+ setOperationAction(ISD::EXTRACT_VECTOR_ELT, T, Custom);
+ setOperationAction(ISD::INSERT_VECTOR_ELT, T, Custom);
+ }
+ if (Subtarget->hasUnimplementedSIMD128()) {
+ for (auto T : {MVT::v2i64, MVT::v2f64}) {
+ setOperationAction(ISD::EXTRACT_VECTOR_ELT, T, Custom);
+ setOperationAction(ISD::INSERT_VECTOR_ELT, T, Custom);
+ }
+ }
+ }
// Trap lowers to wasm unreachable
setOperationAction(ISD::TRAP, MVT::Other, Legal);
// Exception handling intrinsics
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
+ setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
setMaxAtomicSizeInBitsSupported(64);
}
+TargetLowering::AtomicExpansionKind
+WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const {
+ // We have wasm instructions for these
+ switch (AI->getOperation()) {
+ case AtomicRMWInst::Add:
+ case AtomicRMWInst::Sub:
+ case AtomicRMWInst::And:
+ case AtomicRMWInst::Or:
+ case AtomicRMWInst::Xor:
+ case AtomicRMWInst::Xchg:
+ return AtomicExpansionKind::None;
+ default:
+ break;
+ }
+ return AtomicExpansionKind::CmpXChg;
+}
+
FastISel *WebAssemblyTargetLowering::createFastISel(
FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const {
return WebAssembly::createFastISel(FuncInfo, LibInfo);
@@ -171,7 +281,8 @@ bool WebAssemblyTargetLowering::isOffsetFoldingLegal(
MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
EVT VT) const {
unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1);
- if (BitWidth > 1 && BitWidth < 8) BitWidth = 8;
+ if (BitWidth > 1 && BitWidth < 8)
+ BitWidth = 8;
if (BitWidth > 64) {
// The shift will be lowered to a libcall, and compiler-rt libcalls expect
@@ -190,17 +301,11 @@ MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
// Lower an fp-to-int conversion operator from the LLVM opcode, which has an
// undefined result on invalid/overflow, to the WebAssembly opcode, which
// traps on invalid/overflow.
-static MachineBasicBlock *
-LowerFPToInt(
- MachineInstr &MI,
- DebugLoc DL,
- MachineBasicBlock *BB,
- const TargetInstrInfo &TII,
- bool IsUnsigned,
- bool Int64,
- bool Float64,
- unsigned LoweredOpcode
-) {
+static MachineBasicBlock *LowerFPToInt(MachineInstr &MI, DebugLoc DL,
+ MachineBasicBlock *BB,
+ const TargetInstrInfo &TII,
+ bool IsUnsigned, bool Int64,
+ bool Float64, unsigned LoweredOpcode) {
MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
unsigned OutReg = MI.getOperand(0).getReg();
@@ -232,8 +337,7 @@ LowerFPToInt(
// Transfer the remainder of BB and its successor edges to DoneMBB.
DoneMBB->splice(DoneMBB->begin(), BB,
- std::next(MachineBasicBlock::iterator(MI)),
- BB->end());
+ std::next(MachineBasicBlock::iterator(MI)), BB->end());
DoneMBB->transferSuccessorsAndUpdatePHIs(BB);
BB->addSuccessor(TrueMBB);
@@ -255,45 +359,33 @@ LowerFPToInt(
if (IsUnsigned) {
Tmp0 = InReg;
} else {
- BuildMI(BB, DL, TII.get(Abs), Tmp0)
- .addReg(InReg);
+ BuildMI(BB, DL, TII.get(Abs), Tmp0).addReg(InReg);
}
BuildMI(BB, DL, TII.get(FConst), Tmp1)
.addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal)));
- BuildMI(BB, DL, TII.get(LT), CmpReg)
- .addReg(Tmp0)
- .addReg(Tmp1);
+ BuildMI(BB, DL, TII.get(LT), CmpReg).addReg(Tmp0).addReg(Tmp1);
// For unsigned numbers, we have to do a separate comparison with zero.
if (IsUnsigned) {
Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
- unsigned SecondCmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
+ unsigned SecondCmpReg =
+ MRI.createVirtualRegister(&WebAssembly::I32RegClass);
unsigned AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
BuildMI(BB, DL, TII.get(FConst), Tmp1)
.addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0)));
- BuildMI(BB, DL, TII.get(GE), SecondCmpReg)
- .addReg(Tmp0)
- .addReg(Tmp1);
- BuildMI(BB, DL, TII.get(And), AndReg)
- .addReg(CmpReg)
- .addReg(SecondCmpReg);
+ BuildMI(BB, DL, TII.get(GE), SecondCmpReg).addReg(Tmp0).addReg(Tmp1);
+ BuildMI(BB, DL, TII.get(And), AndReg).addReg(CmpReg).addReg(SecondCmpReg);
CmpReg = AndReg;
}
- BuildMI(BB, DL, TII.get(Eqz), EqzReg)
- .addReg(CmpReg);
+ BuildMI(BB, DL, TII.get(Eqz), EqzReg).addReg(CmpReg);
// Create the CFG diamond to select between doing the conversion or using
// the substitute value.
- BuildMI(BB, DL, TII.get(WebAssembly::BR_IF))
- .addMBB(TrueMBB)
- .addReg(EqzReg);
- BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg)
- .addReg(InReg);
- BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR))
- .addMBB(DoneMBB);
- BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg)
- .addImm(Substitute);
+ BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(TrueMBB).addReg(EqzReg);
+ BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg).addReg(InReg);
+ BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB);
+ BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg).addImm(Substitute);
BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg)
.addReg(FalseReg)
.addMBB(FalseMBB)
@@ -303,16 +395,14 @@ LowerFPToInt(
return DoneMBB;
}
-MachineBasicBlock *
-WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
- MachineInstr &MI,
- MachineBasicBlock *BB
-) const {
+MachineBasicBlock *WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
+ MachineInstr &MI, MachineBasicBlock *BB) const {
const TargetInstrInfo &TII = *Subtarget->getInstrInfo();
DebugLoc DL = MI.getDebugLoc();
switch (MI.getOpcode()) {
- default: llvm_unreachable("Unexpected instr type to insert");
+ default:
+ llvm_unreachable("Unexpected instr type to insert");
case WebAssembly::FP_TO_SINT_I32_F32:
return LowerFPToInt(MI, DL, BB, TII, false, false, false,
WebAssembly::I32_TRUNC_S_F32);
@@ -337,17 +427,17 @@ WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
case WebAssembly::FP_TO_UINT_I64_F64:
return LowerFPToInt(MI, DL, BB, TII, true, true, true,
WebAssembly::I64_TRUNC_U_F64);
- llvm_unreachable("Unexpected instruction to emit with custom inserter");
+ llvm_unreachable("Unexpected instruction to emit with custom inserter");
}
}
-const char *WebAssemblyTargetLowering::getTargetNodeName(
- unsigned Opcode) const {
+const char *
+WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const {
switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) {
- case WebAssemblyISD::FIRST_NUMBER:
- break;
-#define HANDLE_NODETYPE(NODE) \
- case WebAssemblyISD::NODE: \
+ case WebAssemblyISD::FIRST_NUMBER:
+ break;
+#define HANDLE_NODETYPE(NODE) \
+ case WebAssemblyISD::NODE: \
return "WebAssemblyISD::" #NODE;
#include "WebAssemblyISD.def"
#undef HANDLE_NODETYPE
@@ -362,21 +452,21 @@ WebAssemblyTargetLowering::getRegForInlineAsmConstraint(
// WebAssembly register class.
if (Constraint.size() == 1) {
switch (Constraint[0]) {
- case 'r':
- assert(VT != MVT::iPTR && "Pointer MVT not expected here");
- if (Subtarget->hasSIMD128() && VT.isVector()) {
- if (VT.getSizeInBits() == 128)
- return std::make_pair(0U, &WebAssembly::V128RegClass);
- }
- if (VT.isInteger() && !VT.isVector()) {
- if (VT.getSizeInBits() <= 32)
- return std::make_pair(0U, &WebAssembly::I32RegClass);
- if (VT.getSizeInBits() <= 64)
- return std::make_pair(0U, &WebAssembly::I64RegClass);
- }
- break;
- default:
- break;
+ case 'r':
+ assert(VT != MVT::iPTR && "Pointer MVT not expected here");
+ if (Subtarget->hasSIMD128() && VT.isVector()) {
+ if (VT.getSizeInBits() == 128)
+ return std::make_pair(0U, &WebAssembly::V128RegClass);
+ }
+ if (VT.isInteger() && !VT.isVector()) {
+ if (VT.getSizeInBits() <= 32)
+ return std::make_pair(0U, &WebAssembly::I32RegClass);
+ if (VT.getSizeInBits() <= 64)
+ return std::make_pair(0U, &WebAssembly::I64RegClass);
+ }
+ break;
+ default:
+ break;
}
}
@@ -395,16 +485,17 @@ bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const {
bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL,
const AddrMode &AM,
- Type *Ty,
- unsigned AS,
+ Type *Ty, unsigned AS,
Instruction *I) const {
// WebAssembly offsets are added as unsigned without wrapping. The
// isLegalAddressingMode gives us no way to determine if wrapping could be
// happening, so we approximate this by accepting only non-negative offsets.
- if (AM.BaseOffs < 0) return false;
+ if (AM.BaseOffs < 0)
+ return false;
// WebAssembly has no scale register operands.
- if (AM.Scale != 0) return false;
+ if (AM.Scale != 0)
+ return false;
// Everything else is legal.
return true;
@@ -418,7 +509,8 @@ bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses(
// for the kinds of things that LLVM uses this for (merging adjacent stores
// of constants, etc.), WebAssembly implementations will either want the
// unaligned access or they'll split anyway.
- if (Fast) *Fast = true;
+ if (Fast)
+ *Fast = true;
return true;
}
@@ -438,6 +530,46 @@ EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL,
return TargetLowering::getSetCCResultType(DL, C, VT);
}
+bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
+ const CallInst &I,
+ MachineFunction &MF,
+ unsigned Intrinsic) const {
+ switch (Intrinsic) {
+ case Intrinsic::wasm_atomic_notify:
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.memVT = MVT::i32;
+ Info.ptrVal = I.getArgOperand(0);
+ Info.offset = 0;
+ Info.align = 4;
+ // atomic.notify instruction does not really load the memory specified with
+ // this argument, but MachineMemOperand should either be load or store, so
+ // we set this to a load.
+ // FIXME Volatile isn't really correct, but currently all LLVM atomic
+ // instructions are treated as volatiles in the backend, so we should be
+ // consistent. The same applies for wasm_atomic_wait intrinsics too.
+ Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad;
+ return true;
+ case Intrinsic::wasm_atomic_wait_i32:
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.memVT = MVT::i32;
+ Info.ptrVal = I.getArgOperand(0);
+ Info.offset = 0;
+ Info.align = 4;
+ Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad;
+ return true;
+ case Intrinsic::wasm_atomic_wait_i64:
+ Info.opc = ISD::INTRINSIC_W_CHAIN;
+ Info.memVT = MVT::i64;
+ Info.ptrVal = I.getArgOperand(0);
+ Info.offset = 0;
+ Info.align = 8;
+ Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad;
+ return true;
+ default:
+ return false;
+ }
+}
+
//===----------------------------------------------------------------------===//
// WebAssembly Lowering private implementation.
//===----------------------------------------------------------------------===//
@@ -465,8 +597,9 @@ static bool CallingConvSupported(CallingConv::ID CallConv) {
CallConv == CallingConv::CXX_FAST_TLS;
}
-SDValue WebAssemblyTargetLowering::LowerCall(
- CallLoweringInfo &CLI, SmallVectorImpl<SDValue> &InVals) const {
+SDValue
+WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
+ SmallVectorImpl<SDValue> &InVals) const {
SelectionDAG &DAG = CLI.DAG;
SDLoc DL = CLI.DL;
SDValue Chain = CLI.Chain;
@@ -568,9 +701,9 @@ SDValue WebAssemblyTargetLowering::LowerCall(
FINode = DAG.getFrameIndex(FI, getPointerTy(Layout));
SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode,
DAG.getConstant(Offset, DL, PtrVT));
- Chains.push_back(DAG.getStore(
- Chain, DL, Arg, Add,
- MachinePointerInfo::getFixedStack(MF, FI, Offset), 0));
+ Chains.push_back(
+ DAG.getStore(Chain, DL, Arg, Add,
+ MachinePointerInfo::getFixedStack(MF, FI, Offset), 0));
}
if (!Chains.empty())
Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
@@ -588,7 +721,8 @@ SDValue WebAssemblyTargetLowering::LowerCall(
Ops.append(OutVals.begin(),
IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end());
// Add a pointer to the vararg buffer.
- if (IsVarArg) Ops.push_back(FINode);
+ if (IsVarArg)
+ Ops.push_back(FINode);
SmallVector<EVT, 8> InTys;
for (const auto &In : Ins) {
@@ -682,11 +816,10 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(
fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
// Ignore In.getOrigAlign() because all our arguments are passed in
// registers.
- InVals.push_back(
- In.Used
- ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT,
- DAG.getTargetConstant(InVals.size(), DL, MVT::i32))
- : DAG.getUNDEF(In.VT));
+ InVals.push_back(In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT,
+ DAG.getTargetConstant(InVals.size(),
+ DL, MVT::i32))
+ : DAG.getUNDEF(In.VT));
// Record the number and types of arguments.
MFI->addParam(In.VT);
@@ -706,12 +839,18 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments(
MFI->addParam(PtrVT);
}
- // Record the number and types of results.
+ // Record the number and types of arguments and results.
SmallVector<MVT, 4> Params;
SmallVector<MVT, 4> Results;
- ComputeSignatureVTs(MF.getFunction(), DAG.getTarget(), Params, Results);
+ ComputeSignatureVTs(MF.getFunction().getFunctionType(), MF.getFunction(),
+ DAG.getTarget(), Params, Results);
for (MVT VT : Results)
MFI->addResult(VT);
+ // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify
+ // the param logic here with ComputeSignatureVTs
+ assert(MFI->getParams().size() == Params.size() &&
+ std::equal(MFI->getParams().begin(), MFI->getParams().end(),
+ Params.begin()));
return Chain;
}
@@ -724,34 +863,47 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
SelectionDAG &DAG) const {
SDLoc DL(Op);
switch (Op.getOpcode()) {
- default:
- llvm_unreachable("unimplemented operation lowering");
- return SDValue();
- case ISD::FrameIndex:
- return LowerFrameIndex(Op, DAG);
- case ISD::GlobalAddress:
- return LowerGlobalAddress(Op, DAG);
- case ISD::ExternalSymbol:
- return LowerExternalSymbol(Op, DAG);
- case ISD::JumpTable:
- return LowerJumpTable(Op, DAG);
- case ISD::BR_JT:
- return LowerBR_JT(Op, DAG);
- case ISD::VASTART:
- return LowerVASTART(Op, DAG);
- case ISD::BlockAddress:
- case ISD::BRIND:
- fail(DL, DAG, "WebAssembly hasn't implemented computed gotos");
- return SDValue();
- case ISD::RETURNADDR: // Probably nothing meaningful can be returned here.
- fail(DL, DAG, "WebAssembly hasn't implemented __builtin_return_address");
- return SDValue();
- case ISD::FRAMEADDR:
- return LowerFRAMEADDR(Op, DAG);
- case ISD::CopyToReg:
- return LowerCopyToReg(Op, DAG);
- case ISD::INTRINSIC_WO_CHAIN:
- return LowerINTRINSIC_WO_CHAIN(Op, DAG);
+ default:
+ llvm_unreachable("unimplemented operation lowering");
+ return SDValue();
+ case ISD::FrameIndex:
+ return LowerFrameIndex(Op, DAG);
+ case ISD::GlobalAddress:
+ return LowerGlobalAddress(Op, DAG);
+ case ISD::ExternalSymbol:
+ return LowerExternalSymbol(Op, DAG);
+ case ISD::JumpTable:
+ return LowerJumpTable(Op, DAG);
+ case ISD::BR_JT:
+ return LowerBR_JT(Op, DAG);
+ case ISD::VASTART:
+ return LowerVASTART(Op, DAG);
+ case ISD::BlockAddress:
+ case ISD::BRIND:
+ fail(DL, DAG, "WebAssembly hasn't implemented computed gotos");
+ return SDValue();
+ case ISD::RETURNADDR: // Probably nothing meaningful can be returned here.
+ fail(DL, DAG, "WebAssembly hasn't implemented __builtin_return_address");
+ return SDValue();
+ case ISD::FRAMEADDR:
+ return LowerFRAMEADDR(Op, DAG);
+ case ISD::CopyToReg:
+ return LowerCopyToReg(Op, DAG);
+ case ISD::INTRINSIC_WO_CHAIN:
+ return LowerINTRINSIC_WO_CHAIN(Op, DAG);
+ case ISD::EXTRACT_VECTOR_ELT:
+ case ISD::INSERT_VECTOR_ELT:
+ return LowerAccessVectorElement(Op, DAG);
+ case ISD::INTRINSIC_VOID:
+ return LowerINTRINSIC_VOID(Op, DAG);
+ case ISD::SIGN_EXTEND_INREG:
+ return LowerSIGN_EXTEND_INREG(Op, DAG);
+ case ISD::VECTOR_SHUFFLE:
+ return LowerVECTOR_SHUFFLE(Op, DAG);
+ case ISD::SHL:
+ case ISD::SRA:
+ case ISD::SRL:
+ return LowerShift(Op, DAG);
}
}
@@ -763,21 +915,20 @@ SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
// the FI to some LEA-like instruction, but since we don't have that, we
// need to insert some kind of instruction that can take an FI operand and
// produces a value usable by CopyToReg (i.e. in a vreg). So insert a dummy
- // copy_local between Op and its FI operand.
+ // local.copy between Op and its FI operand.
SDValue Chain = Op.getOperand(0);
SDLoc DL(Op);
unsigned Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg();
EVT VT = Src.getValueType();
- SDValue Copy(
- DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32
- : WebAssembly::COPY_I64,
- DL, VT, Src),
- 0);
+ SDValue Copy(DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32
+ : WebAssembly::COPY_I64,
+ DL, VT, Src),
+ 0);
return Op.getNode()->getNumValues() == 1
? DAG.getCopyToReg(Chain, DL, Reg, Copy)
- : DAG.getCopyToReg(Chain, DL, Reg, Copy, Op.getNumOperands() == 4
- ? Op.getOperand(3)
- : SDValue());
+ : DAG.getCopyToReg(Chain, DL, Reg, Copy,
+ Op.getNumOperands() == 4 ? Op.getOperand(3)
+ : SDValue());
}
return SDValue();
}
@@ -817,8 +968,9 @@ SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset()));
}
-SDValue WebAssemblyTargetLowering::LowerExternalSymbol(
- SDValue Op, SelectionDAG &DAG) const {
+SDValue
+WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op,
+ SelectionDAG &DAG) const {
SDLoc DL(Op);
const auto *ES = cast<ExternalSymbolSDNode>(Op);
EVT VT = Op.getValueType();
@@ -829,9 +981,10 @@ SDValue WebAssemblyTargetLowering::LowerExternalSymbol(
// we don't know anything about the symbol other than its name, because all
// external symbols used in target-independent SelectionDAG code are for
// functions.
- return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
- DAG.getTargetExternalSymbol(ES->getSymbol(), VT,
- /*TargetFlags=*/0x1));
+ return DAG.getNode(
+ WebAssemblyISD::Wrapper, DL, VT,
+ DAG.getTargetExternalSymbol(ES->getSymbol(), VT,
+ WebAssemblyII::MO_SYMBOL_FUNCTION));
}
SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op,
@@ -860,7 +1013,8 @@ SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op,
const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs;
// Add an operand for each case.
- for (auto MBB : MBBs) Ops.push_back(DAG.getBasicBlock(MBB));
+ for (auto MBB : MBBs)
+ Ops.push_back(DAG.getBasicBlock(MBB));
// TODO: For now, we just pick something arbitrary for a default case for now.
// We really want to sniff out the guard and put in the real default case (and
@@ -893,10 +1047,181 @@ WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
default:
return {}; // Don't custom lower most intrinsics.
- case Intrinsic::wasm_lsda:
- // TODO For now, just return 0 not to crash
- return DAG.getConstant(0, DL, Op.getValueType());
+ case Intrinsic::wasm_lsda: {
+ MachineFunction &MF = DAG.getMachineFunction();
+ EVT VT = Op.getValueType();
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
+ auto &Context = MF.getMMI().getContext();
+ MCSymbol *S = Context.getOrCreateSymbol(Twine("GCC_except_table") +
+ Twine(MF.getFunctionNumber()));
+ return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
+ DAG.getMCSymbol(S, PtrVT));
+ }
+ }
+}
+
+SDValue
+WebAssemblyTargetLowering::LowerINTRINSIC_VOID(SDValue Op,
+ SelectionDAG &DAG) const {
+ MachineFunction &MF = DAG.getMachineFunction();
+ unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue();
+ SDLoc DL(Op);
+
+ switch (IntNo) {
+ default:
+ return {}; // Don't custom lower most intrinsics.
+
+ case Intrinsic::wasm_throw: {
+ int Tag = cast<ConstantSDNode>(Op.getOperand(2).getNode())->getZExtValue();
+ switch (Tag) {
+ case CPP_EXCEPTION: {
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout());
+ const char *SymName = MF.createExternalSymbolName("__cpp_exception");
+ SDValue SymNode =
+ DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
+ DAG.getTargetExternalSymbol(
+ SymName, PtrVT, WebAssemblyII::MO_SYMBOL_EVENT));
+ return DAG.getNode(WebAssemblyISD::THROW, DL,
+ MVT::Other, // outchain type
+ {
+ Op.getOperand(0), // inchain
+ SymNode, // exception symbol
+ Op.getOperand(3) // thrown value
+ });
+ }
+ default:
+ llvm_unreachable("Invalid tag!");
+ }
+ break;
+ }
+ }
+}
+
+SDValue
+WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op,
+ SelectionDAG &DAG) const {
+ // If sign extension operations are disabled, allow sext_inreg only if operand
+ // is a vector extract. SIMD does not depend on sign extension operations, but
+ // allowing sext_inreg in this context lets us have simple patterns to select
+ // extract_lane_s instructions. Expanding sext_inreg everywhere would be
+ // simpler in this file, but would necessitate large and brittle patterns to
+ // undo the expansion and select extract_lane_s instructions.
+ assert(!Subtarget->hasSignExt() && Subtarget->hasSIMD128());
+ if (Op.getOperand(0).getOpcode() == ISD::EXTRACT_VECTOR_ELT)
+ return Op;
+ // Otherwise expand
+ return SDValue();
+}
+
+SDValue
+WebAssemblyTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDLoc DL(Op);
+ ArrayRef<int> Mask = cast<ShuffleVectorSDNode>(Op.getNode())->getMask();
+ MVT VecType = Op.getOperand(0).getSimpleValueType();
+ assert(VecType.is128BitVector() && "Unexpected shuffle vector type");
+ size_t LaneBytes = VecType.getVectorElementType().getSizeInBits() / 8;
+
+ // Space for two vector args and sixteen mask indices
+ SDValue Ops[18];
+ size_t OpIdx = 0;
+ Ops[OpIdx++] = Op.getOperand(0);
+ Ops[OpIdx++] = Op.getOperand(1);
+
+ // Expand mask indices to byte indices and materialize them as operands
+ for (size_t I = 0, Lanes = Mask.size(); I < Lanes; ++I) {
+ for (size_t J = 0; J < LaneBytes; ++J) {
+ // Lower undefs (represented by -1 in mask) to zero
+ uint64_t ByteIndex =
+ Mask[I] == -1 ? 0 : (uint64_t)Mask[I] * LaneBytes + J;
+ Ops[OpIdx++] = DAG.getConstant(ByteIndex, DL, MVT::i32);
+ }
+ }
+
+ return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops);
+}
+
+SDValue
+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())
+ return Op;
+ else
+ // Perform default expansion
+ return SDValue();
+}
+
+static SDValue UnrollVectorShift(SDValue Op, SelectionDAG &DAG) {
+ EVT LaneT = Op.getSimpleValueType().getVectorElementType();
+ // 32-bit and 64-bit unrolled shifts will have proper semantics
+ if (LaneT.bitsGE(MVT::i32))
+ return DAG.UnrollVectorOp(Op.getNode());
+ // Otherwise mask the shift value to get proper semantics from 32-bit shift
+ SDLoc DL(Op);
+ SDValue ShiftVal = Op.getOperand(1);
+ uint64_t MaskVal = LaneT.getSizeInBits() - 1;
+ SDValue MaskedShiftVal = DAG.getNode(
+ ISD::AND, // mask opcode
+ DL, ShiftVal.getValueType(), // masked value type
+ ShiftVal, // original shift value operand
+ DAG.getConstant(MaskVal, DL, ShiftVal.getValueType()) // mask operand
+ );
+
+ return DAG.UnrollVectorOp(
+ DAG.getNode(Op.getOpcode(), // original shift opcode
+ DL, Op.getValueType(), // original return type
+ Op.getOperand(0), // original vector operand,
+ MaskedShiftVal // new masked shift value operand
+ )
+ .getNode());
+}
+
+SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDLoc DL(Op);
+
+ // Only manually lower vector shifts
+ assert(Op.getSimpleValueType().isVector());
+
+ // Expand all vector shifts until V8 fixes its implementation
+ // TODO: remove this once V8 is fixed
+ if (!Subtarget->hasUnimplementedSIMD128())
+ return UnrollVectorShift(Op, DAG);
+
+ // Unroll non-splat vector shifts
+ BuildVectorSDNode *ShiftVec;
+ SDValue SplatVal;
+ if (!(ShiftVec = dyn_cast<BuildVectorSDNode>(Op.getOperand(1).getNode())) ||
+ !(SplatVal = ShiftVec->getSplatValue()))
+ return UnrollVectorShift(Op, DAG);
+
+ // All splats except i64x2 const splats are handled by patterns
+ ConstantSDNode *SplatConst = dyn_cast<ConstantSDNode>(SplatVal);
+ if (!SplatConst || Op.getSimpleValueType() != MVT::v2i64)
+ return Op;
+
+ // i64x2 const splats are custom lowered to avoid unnecessary wraps
+ unsigned Opcode;
+ switch (Op.getOpcode()) {
+ case ISD::SHL:
+ Opcode = WebAssemblyISD::VEC_SHL;
+ break;
+ case ISD::SRA:
+ Opcode = WebAssemblyISD::VEC_SHR_S;
+ break;
+ case ISD::SRL:
+ Opcode = WebAssemblyISD::VEC_SHR_U;
+ break;
+ default:
+ llvm_unreachable("unexpected opcode");
}
+ APInt Shift = SplatConst->getAPIntValue().zextOrTrunc(32);
+ return DAG.getNode(Opcode, DL, Op.getValueType(), Op.getOperand(0),
+ DAG.getConstant(Shift, DL, MVT::i32));
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/lib/Target/WebAssembly/WebAssemblyISelLowering.h
index 79819493ac6a..59f4230ed889 100644
--- a/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -29,21 +29,22 @@ enum NodeType : unsigned {
#undef HANDLE_NODETYPE
};
-} // end namespace WebAssemblyISD
+} // end namespace WebAssemblyISD
class WebAssemblySubtarget;
class WebAssemblyTargetMachine;
class WebAssemblyTargetLowering final : public TargetLowering {
- public:
+public:
WebAssemblyTargetLowering(const TargetMachine &TM,
const WebAssemblySubtarget &STI);
- private:
+private:
/// Keep a pointer to the WebAssemblySubtarget around so that we can make the
/// right decision when generating code for different targets.
const WebAssemblySubtarget *Subtarget;
+ AtomicExpansionKind shouldExpandAtomicRMWInIR(AtomicRMWInst *) const override;
FastISel *createFastISel(FunctionLoweringInfo &FuncInfo,
const TargetLibraryInfo *LibInfo) const override;
bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override;
@@ -52,9 +53,9 @@ class WebAssemblyTargetLowering final : public TargetLowering {
EmitInstrWithCustomInserter(MachineInstr &MI,
MachineBasicBlock *MBB) const override;
const char *getTargetNodeName(unsigned Opcode) const override;
- std::pair<unsigned, const TargetRegisterClass *> getRegForInlineAsmConstraint(
- const TargetRegisterInfo *TRI, StringRef Constraint,
- MVT VT) const override;
+ std::pair<unsigned, const TargetRegisterClass *>
+ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint, MVT VT) const override;
bool isCheapToSpeculateCttz() const override;
bool isCheapToSpeculateCtlz() const override;
bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty,
@@ -66,6 +67,9 @@ class WebAssemblyTargetLowering final : public TargetLowering {
EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context,
EVT VT) const override;
+ bool getTgtMemIntrinsic(IntrinsicInfo &Info, const CallInst &I,
+ MachineFunction &MF,
+ unsigned Intrinsic) const override;
SDValue LowerCall(CallLoweringInfo &CLI,
SmallVectorImpl<SDValue> &InVals) const override;
@@ -94,13 +98,18 @@ class WebAssemblyTargetLowering final : public TargetLowering {
SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerSIGN_EXTEND_INREG(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerAccessVectorElement(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerShift(SDValue Op, SelectionDAG &DAG) const;
};
namespace WebAssembly {
FastISel *createFastISel(FunctionLoweringInfo &funcInfo,
const TargetLibraryInfo *libInfo);
-} // end namespace WebAssembly
+} // end namespace WebAssembly
-} // end namespace llvm
+} // end namespace llvm
#endif
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td b/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td
index d879932b3232..5fb8ef90bc43 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td
@@ -16,10 +16,16 @@
// Atomic loads
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
+multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
+ list<dag> pattern_r, string asmstr_r = "",
+ string asmstr_s = "", bits<32> inst = -1> {
+ defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
+ inst>,
+ Requires<[HasAtomics]>;
+}
+
defm ATOMIC_LOAD_I32 : WebAssemblyLoad<I32, "i32.atomic.load", 0xfe10>;
defm ATOMIC_LOAD_I64 : WebAssemblyLoad<I64, "i64.atomic.load", 0xfe11>;
-} // Defs = [ARGUMENTS]
// Select loads with no constant offset.
let Predicates = [HasAtomics] in {
@@ -54,13 +60,11 @@ def : LoadPatExternSymOffOnly<i64, atomic_load_64, ATOMIC_LOAD_I64>;
// Extending loads. Note that there are only zero-extending atomic loads, no
// sign-extending loads.
-let Defs = [ARGUMENTS] in {
defm ATOMIC_LOAD8_U_I32 : WebAssemblyLoad<I32, "i32.atomic.load8_u", 0xfe12>;
defm ATOMIC_LOAD16_U_I32 : WebAssemblyLoad<I32, "i32.atomic.load16_u", 0xfe13>;
defm ATOMIC_LOAD8_U_I64 : WebAssemblyLoad<I64, "i64.atomic.load8_u", 0xfe14>;
defm ATOMIC_LOAD16_U_I64 : WebAssemblyLoad<I64, "i64.atomic.load16_u", 0xfe15>;
defm ATOMIC_LOAD32_U_I64 : WebAssemblyLoad<I64, "i64.atomic.load32_u", 0xfe16>;
-} // Defs = [ARGUMENTS]
// Fragments for extending loads. These are different from regular loads because
// the SDNodes are derived from AtomicSDNode rather than LoadSDNode and
@@ -110,7 +114,7 @@ def : LoadPatNoOffset<i32, atomic_load_8, ATOMIC_LOAD8_U_I32>;
def : LoadPatNoOffset<i32, atomic_load_16, ATOMIC_LOAD16_U_I32>;
def : LoadPatNoOffset<i64, sext_aload_8_64, ATOMIC_LOAD8_U_I64>;
def : LoadPatNoOffset<i64, sext_aload_16_64, ATOMIC_LOAD16_U_I64>;
-// 32->64 sext load gets selected as i32.atomic.load, i64.extend_s/i32
+// 32->64 sext load gets selected as i32.atomic.load, i64.extend_i32_s
// Zero-extending loads with constant offset
def : LoadPatImmOff<i32, zext_aload_8_32, regPlusImm, ATOMIC_LOAD8_U_I32>;
@@ -192,10 +196,8 @@ def : LoadPatExternSymOffOnly<i64, sext_aload_16_64, ATOMIC_LOAD16_U_I64>;
// Atomic stores
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
defm ATOMIC_STORE_I32 : WebAssemblyStore<I32, "i32.atomic.store", 0xfe17>;
defm ATOMIC_STORE_I64 : WebAssemblyStore<I64, "i64.atomic.store", 0xfe18>;
-} // Defs = [ARGUMENTS]
// We need an 'atomic' version of store patterns because store and atomic_store
// nodes have different operand orders:
@@ -255,13 +257,11 @@ def : AStorePatExternSymOffOnly<i64, atomic_store_64, ATOMIC_STORE_I64>;
} // Predicates = [HasAtomics]
// Truncating stores.
-let Defs = [ARGUMENTS] in {
defm ATOMIC_STORE8_I32 : WebAssemblyStore<I32, "i32.atomic.store8", 0xfe19>;
defm ATOMIC_STORE16_I32 : WebAssemblyStore<I32, "i32.atomic.store16", 0xfe1a>;
defm ATOMIC_STORE8_I64 : WebAssemblyStore<I64, "i64.atomic.store8", 0xfe1b>;
defm ATOMIC_STORE16_I64 : WebAssemblyStore<I64, "i64.atomic.store16", 0xfe1c>;
defm ATOMIC_STORE32_I64 : WebAssemblyStore<I64, "i64.atomic.store32", 0xfe1d>;
-} // Defs = [ARGUMENTS]
// Fragments for truncating stores.
@@ -333,8 +333,6 @@ def : AStorePatExternSymOffOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
// Atomic binary read-modify-writes
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
-
multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string Name, int Opcode> {
defm "" : I<(outs rc:$dst),
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
@@ -346,83 +344,82 @@ multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string Name, int Opcode> {
defm ATOMIC_RMW_ADD_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.add", 0xfe1e>;
defm ATOMIC_RMW_ADD_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.add", 0xfe1f>;
defm ATOMIC_RMW8_U_ADD_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.add", 0xfe20>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw8.add_u", 0xfe20>;
defm ATOMIC_RMW16_U_ADD_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.add", 0xfe21>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw16.add_u", 0xfe21>;
defm ATOMIC_RMW8_U_ADD_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.add", 0xfe22>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw8.add_u", 0xfe22>;
defm ATOMIC_RMW16_U_ADD_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.add", 0xfe23>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw16.add_u", 0xfe23>;
defm ATOMIC_RMW32_U_ADD_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.add", 0xfe24>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw32.add_u", 0xfe24>;
defm ATOMIC_RMW_SUB_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.sub", 0xfe25>;
defm ATOMIC_RMW_SUB_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.sub", 0xfe26>;
defm ATOMIC_RMW8_U_SUB_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.sub", 0xfe27>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw8.sub_u", 0xfe27>;
defm ATOMIC_RMW16_U_SUB_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.sub", 0xfe28>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw16.sub_u", 0xfe28>;
defm ATOMIC_RMW8_U_SUB_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.sub", 0xfe29>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw8.sub_u", 0xfe29>;
defm ATOMIC_RMW16_U_SUB_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.sub", 0xfe2a>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw16.sub_u", 0xfe2a>;
defm ATOMIC_RMW32_U_SUB_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.sub", 0xfe2b>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw32.sub_u", 0xfe2b>;
defm ATOMIC_RMW_AND_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.and", 0xfe2c>;
defm ATOMIC_RMW_AND_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.and", 0xfe2d>;
defm ATOMIC_RMW8_U_AND_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.and", 0xfe2e>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw8.and_u", 0xfe2e>;
defm ATOMIC_RMW16_U_AND_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.and", 0xfe2f>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw16.and_u", 0xfe2f>;
defm ATOMIC_RMW8_U_AND_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.and", 0xfe30>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw8.and_u", 0xfe30>;
defm ATOMIC_RMW16_U_AND_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.and", 0xfe31>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw16.and_u", 0xfe31>;
defm ATOMIC_RMW32_U_AND_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.and", 0xfe32>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw32.and_u", 0xfe32>;
defm ATOMIC_RMW_OR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.or", 0xfe33>;
defm ATOMIC_RMW_OR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.or", 0xfe34>;
defm ATOMIC_RMW8_U_OR_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.or", 0xfe35>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw8.or_u", 0xfe35>;
defm ATOMIC_RMW16_U_OR_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.or", 0xfe36>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw16.or_u", 0xfe36>;
defm ATOMIC_RMW8_U_OR_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.or", 0xfe37>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw8.or_u", 0xfe37>;
defm ATOMIC_RMW16_U_OR_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.or", 0xfe38>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw16.or_u", 0xfe38>;
defm ATOMIC_RMW32_U_OR_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.or", 0xfe39>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw32.or_u", 0xfe39>;
defm ATOMIC_RMW_XOR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.xor", 0xfe3a>;
defm ATOMIC_RMW_XOR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.xor", 0xfe3b>;
defm ATOMIC_RMW8_U_XOR_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.xor", 0xfe3c>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw8.xor_u", 0xfe3c>;
defm ATOMIC_RMW16_U_XOR_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.xor", 0xfe3d>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw16.xor_u", 0xfe3d>;
defm ATOMIC_RMW8_U_XOR_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.xor", 0xfe3e>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw8.xor_u", 0xfe3e>;
defm ATOMIC_RMW16_U_XOR_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.xor", 0xfe3f>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw16.xor_u", 0xfe3f>;
defm ATOMIC_RMW32_U_XOR_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.xor", 0xfe40>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xor_u", 0xfe40>;
defm ATOMIC_RMW_XCHG_I32 :
WebAssemblyBinRMW<I32, "i32.atomic.rmw.xchg", 0xfe41>;
defm ATOMIC_RMW_XCHG_I64 :
WebAssemblyBinRMW<I64, "i64.atomic.rmw.xchg", 0xfe42>;
defm ATOMIC_RMW8_U_XCHG_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.xchg", 0xfe43>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw8.xchg_u", 0xfe43>;
defm ATOMIC_RMW16_U_XCHG_I32 :
- WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.xchg", 0xfe44>;
+ WebAssemblyBinRMW<I32, "i32.atomic.rmw16.xchg_u", 0xfe44>;
defm ATOMIC_RMW8_U_XCHG_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.xchg", 0xfe45>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw8.xchg_u", 0xfe45>;
defm ATOMIC_RMW16_U_XCHG_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.xchg", 0xfe46>;
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw16.xchg_u", 0xfe46>;
defm ATOMIC_RMW32_U_XCHG_I64 :
- WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.xchg", 0xfe47>;
-}
+ WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xchg_u", 0xfe47>;
// Select binary RMWs with no constant offset.
class BinRMWPatNoOffset<ValueType ty, PatFrag kind, NI inst> :
@@ -533,7 +530,7 @@ class sext_bin_rmw_8_64<PatFrag kind> :
PatFrag<(ops node:$addr, node:$val),
(anyext (i32 (kind node:$addr, (i32 (trunc (i64 node:$val))))))>;
class sext_bin_rmw_16_64<PatFrag kind> : sext_bin_rmw_8_64<kind>;
-// 32->64 sext RMW gets selected as i32.atomic.rmw.***, i64.extend_s/i32
+// 32->64 sext RMW gets selected as i32.atomic.rmw.***, i64.extend_i32_s
// Patterns for various addressing modes for truncating-extending binary RMWs.
multiclass BinRMWTruncExtPattern<
@@ -655,3 +652,368 @@ defm : BinRMWTruncExtPattern<
ATOMIC_RMW8_U_XCHG_I32, ATOMIC_RMW16_U_XCHG_I32,
ATOMIC_RMW8_U_XCHG_I64, ATOMIC_RMW16_U_XCHG_I64, ATOMIC_RMW32_U_XCHG_I64>;
} // Predicates = [HasAtomics]
+
+//===----------------------------------------------------------------------===//
+// Atomic ternary read-modify-writes
+//===----------------------------------------------------------------------===//
+
+// TODO LLVM IR's cmpxchg instruction returns a pair of {loaded value, success
+// flag}. When we use the success flag or both values, we can't make use of i64
+// truncate/extend versions of instructions for now, which is suboptimal.
+// Consider adding a pass after instruction selection that optimizes this case
+// if it is frequent.
+
+multiclass WebAssemblyTerRMW<WebAssemblyRegClass rc, string Name, int Opcode> {
+ defm "" : I<(outs rc:$dst),
+ (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$exp,
+ rc:$new),
+ (outs), (ins P2Align:$p2align, offset32_op:$off), [],
+ !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new"),
+ !strconcat(Name, "\t${off}, ${p2align}"), Opcode>;
+}
+
+defm ATOMIC_RMW_CMPXCHG_I32 :
+ WebAssemblyTerRMW<I32, "i32.atomic.rmw.cmpxchg", 0xfe48>;
+defm ATOMIC_RMW_CMPXCHG_I64 :
+ WebAssemblyTerRMW<I64, "i64.atomic.rmw.cmpxchg", 0xfe49>;
+defm ATOMIC_RMW8_U_CMPXCHG_I32 :
+ WebAssemblyTerRMW<I32, "i32.atomic.rmw8.cmpxchg_u", 0xfe4a>;
+defm ATOMIC_RMW16_U_CMPXCHG_I32 :
+ WebAssemblyTerRMW<I32, "i32.atomic.rmw16.cmpxchg_u", 0xfe4b>;
+defm ATOMIC_RMW8_U_CMPXCHG_I64 :
+ WebAssemblyTerRMW<I64, "i64.atomic.rmw8.cmpxchg_u", 0xfe4c>;
+defm ATOMIC_RMW16_U_CMPXCHG_I64 :
+ WebAssemblyTerRMW<I64, "i64.atomic.rmw16.cmpxchg_u", 0xfe4d>;
+defm ATOMIC_RMW32_U_CMPXCHG_I64 :
+ WebAssemblyTerRMW<I64, "i64.atomic.rmw32.cmpxchg_u", 0xfe4e>;
+
+// Select ternary RMWs with no constant offset.
+class TerRMWPatNoOffset<ValueType ty, PatFrag kind, NI inst> :
+ Pat<(ty (kind I32:$addr, ty:$exp, ty:$new)),
+ (inst 0, 0, I32:$addr, ty:$exp, ty:$new)>;
+
+// Select ternary RMWs with a constant offset.
+
+// Pattern with address + immediate offset
+class TerRMWPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> :
+ Pat<(ty (kind (operand I32:$addr, imm:$off), ty:$exp, ty:$new)),
+ (inst 0, imm:$off, I32:$addr, ty:$exp, ty:$new)>;
+
+class TerRMWPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
+ Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
+ ty:$exp, ty:$new)),
+ (inst 0, tglobaladdr:$off, I32:$addr, ty:$exp, ty:$new)>;
+
+class TerRMWPatExternalSym<ValueType ty, PatFrag kind, NI inst> :
+ Pat<(ty (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
+ ty:$exp, ty:$new)),
+ (inst 0, texternalsym:$off, I32:$addr, ty:$exp, ty:$new)>;
+
+// Select ternary RMWs with just a constant offset.
+class TerRMWPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> :
+ Pat<(ty (kind imm:$off, ty:$exp, ty:$new)),
+ (inst 0, imm:$off, (CONST_I32 0), ty:$exp, ty:$new)>;
+
+class TerRMWPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
+ Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off), ty:$exp, ty:$new)),
+ (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$exp, ty:$new)>;
+
+class TerRMWPatExternSymOffOnly<ValueType ty, PatFrag kind, NI inst> :
+ Pat<(ty (kind (WebAssemblywrapper texternalsym:$off), ty:$exp, ty:$new)),
+ (inst 0, texternalsym:$off, (CONST_I32 0), ty:$exp, ty:$new)>;
+
+// Patterns for various addressing modes.
+multiclass TerRMWPattern<PatFrag rmw_32, PatFrag rmw_64, NI inst_32,
+ NI inst_64> {
+ def : TerRMWPatNoOffset<i32, rmw_32, inst_32>;
+ def : TerRMWPatNoOffset<i64, rmw_64, inst_64>;
+
+ def : TerRMWPatImmOff<i32, rmw_32, regPlusImm, inst_32>;
+ def : TerRMWPatImmOff<i64, rmw_64, regPlusImm, inst_64>;
+ def : TerRMWPatImmOff<i32, rmw_32, or_is_add, inst_32>;
+ def : TerRMWPatImmOff<i64, rmw_64, or_is_add, inst_64>;
+
+ def : TerRMWPatGlobalAddr<i32, rmw_32, inst_32>;
+ def : TerRMWPatGlobalAddr<i64, rmw_64, inst_64>;
+
+ def : TerRMWPatExternalSym<i32, rmw_32, inst_32>;
+ def : TerRMWPatExternalSym<i64, rmw_64, inst_64>;
+
+ def : TerRMWPatOffsetOnly<i32, rmw_32, inst_32>;
+ def : TerRMWPatOffsetOnly<i64, rmw_64, inst_64>;
+
+ def : TerRMWPatGlobalAddrOffOnly<i32, rmw_32, inst_32>;
+ def : TerRMWPatGlobalAddrOffOnly<i64, rmw_64, inst_64>;
+
+ def : TerRMWPatExternSymOffOnly<i32, rmw_32, inst_32>;
+ def : TerRMWPatExternSymOffOnly<i64, rmw_64, inst_64>;
+}
+
+let Predicates = [HasAtomics] in {
+defm : TerRMWPattern<atomic_cmp_swap_32, atomic_cmp_swap_64,
+ ATOMIC_RMW_CMPXCHG_I32, ATOMIC_RMW_CMPXCHG_I64>;
+} // Predicates = [HasAtomics]
+
+// Truncating & zero-extending ternary RMW patterns.
+// DAG legalization & optimization before instruction selection may introduce
+// additional nodes such as anyext or assertzext depending on operand types.
+class zext_ter_rmw_8_32<PatFrag kind> :
+ PatFrag<(ops node:$addr, node:$exp, node:$new),
+ (and (i32 (kind node:$addr, node:$exp, node:$new)), 255)>;
+class zext_ter_rmw_16_32<PatFrag kind> :
+ PatFrag<(ops node:$addr, node:$exp, node:$new),
+ (and (i32 (kind node:$addr, node:$exp, node:$new)), 65535)>;
+class zext_ter_rmw_8_64<PatFrag kind> :
+ PatFrag<(ops node:$addr, node:$exp, node:$new),
+ (zext (i32 (assertzext (i32 (kind node:$addr,
+ (i32 (trunc (i64 node:$exp))),
+ (i32 (trunc (i64 node:$new))))))))>;
+class zext_ter_rmw_16_64<PatFrag kind> : zext_ter_rmw_8_64<kind>;
+class zext_ter_rmw_32_64<PatFrag kind> :
+ PatFrag<(ops node:$addr, node:$exp, node:$new),
+ (zext (i32 (kind node:$addr,
+ (i32 (trunc (i64 node:$exp))),
+ (i32 (trunc (i64 node:$new))))))>;
+
+// Truncating & sign-extending ternary RMW patterns.
+// We match subword RMWs (for 32-bit) and anyext RMWs (for 64-bit) and select a
+// zext RMW; the next instruction will be sext_inreg which is selected by
+// itself.
+class sext_ter_rmw_8_32<PatFrag kind> :
+ PatFrag<(ops node:$addr, node:$exp, node:$new),
+ (kind node:$addr, node:$exp, node:$new)>;
+class sext_ter_rmw_16_32<PatFrag kind> : sext_ter_rmw_8_32<kind>;
+class sext_ter_rmw_8_64<PatFrag kind> :
+ PatFrag<(ops node:$addr, node:$exp, node:$new),
+ (anyext (i32 (assertzext (i32
+ (kind node:$addr,
+ (i32 (trunc (i64 node:$exp))),
+ (i32 (trunc (i64 node:$new))))))))>;
+class sext_ter_rmw_16_64<PatFrag kind> : sext_ter_rmw_8_64<kind>;
+// 32->64 sext RMW gets selected as i32.atomic.rmw.***, i64.extend_i32_s
+
+// Patterns for various addressing modes for truncating-extending ternary RMWs.
+multiclass TerRMWTruncExtPattern<
+ PatFrag rmw_8, PatFrag rmw_16, PatFrag rmw_32, PatFrag rmw_64,
+ NI inst8_32, NI inst16_32, NI inst8_64, NI inst16_64, NI inst32_64> {
+ // Truncating-extending ternary RMWs with no constant offset
+ def : TerRMWPatNoOffset<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatNoOffset<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatNoOffset<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatNoOffset<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+ def : TerRMWPatNoOffset<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+ def : TerRMWPatNoOffset<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatNoOffset<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatNoOffset<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatNoOffset<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+ // Truncating-extending ternary RMWs with a constant offset
+ def : TerRMWPatImmOff<i32, zext_ter_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
+ def : TerRMWPatImmOff<i32, zext_ter_rmw_16_32<rmw_16>, regPlusImm, inst16_32>;
+ def : TerRMWPatImmOff<i64, zext_ter_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
+ def : TerRMWPatImmOff<i64, zext_ter_rmw_16_64<rmw_16>, regPlusImm, inst16_64>;
+ def : TerRMWPatImmOff<i64, zext_ter_rmw_32_64<rmw_32>, regPlusImm, inst32_64>;
+ def : TerRMWPatImmOff<i32, zext_ter_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
+ def : TerRMWPatImmOff<i32, zext_ter_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
+ def : TerRMWPatImmOff<i64, zext_ter_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
+ def : TerRMWPatImmOff<i64, zext_ter_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
+ def : TerRMWPatImmOff<i64, zext_ter_rmw_32_64<rmw_32>, or_is_add, inst32_64>;
+
+ def : TerRMWPatImmOff<i32, sext_ter_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
+ def : TerRMWPatImmOff<i32, sext_ter_rmw_16_32<rmw_16>, regPlusImm, inst16_32>;
+ def : TerRMWPatImmOff<i64, sext_ter_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
+ def : TerRMWPatImmOff<i64, sext_ter_rmw_16_64<rmw_16>, regPlusImm, inst16_64>;
+ def : TerRMWPatImmOff<i32, sext_ter_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
+ def : TerRMWPatImmOff<i32, sext_ter_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
+ def : TerRMWPatImmOff<i64, sext_ter_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
+ def : TerRMWPatImmOff<i64, sext_ter_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
+
+ def : TerRMWPatGlobalAddr<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatGlobalAddr<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatGlobalAddr<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatGlobalAddr<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+ def : TerRMWPatGlobalAddr<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+ def : TerRMWPatGlobalAddr<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatGlobalAddr<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatGlobalAddr<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatGlobalAddr<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+ def : TerRMWPatExternalSym<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatExternalSym<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatExternalSym<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatExternalSym<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+ def : TerRMWPatExternalSym<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+ def : TerRMWPatExternalSym<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatExternalSym<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatExternalSym<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatExternalSym<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+ // Truncating-extending ternary RMWs with just a constant offset
+ def : TerRMWPatOffsetOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatOffsetOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatOffsetOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatOffsetOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+ def : TerRMWPatOffsetOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+ def : TerRMWPatOffsetOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatOffsetOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatOffsetOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatOffsetOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+ def : TerRMWPatGlobalAddrOffOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatGlobalAddrOffOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+ def : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+ def : TerRMWPatGlobalAddrOffOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatGlobalAddrOffOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatGlobalAddrOffOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatGlobalAddrOffOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+ def : TerRMWPatExternSymOffOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatExternSymOffOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatExternSymOffOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatExternSymOffOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+ def : TerRMWPatExternSymOffOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+ def : TerRMWPatExternSymOffOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+ def : TerRMWPatExternSymOffOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+ def : TerRMWPatExternSymOffOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+ def : TerRMWPatExternSymOffOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+}
+
+let Predicates = [HasAtomics] in {
+defm : TerRMWTruncExtPattern<
+ atomic_cmp_swap_8, atomic_cmp_swap_16, atomic_cmp_swap_32, atomic_cmp_swap_64,
+ ATOMIC_RMW8_U_CMPXCHG_I32, ATOMIC_RMW16_U_CMPXCHG_I32,
+ ATOMIC_RMW8_U_CMPXCHG_I64, ATOMIC_RMW16_U_CMPXCHG_I64,
+ ATOMIC_RMW32_U_CMPXCHG_I64>;
+}
+
+//===----------------------------------------------------------------------===//
+// Atomic wait / notify
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 1 in {
+defm ATOMIC_NOTIFY :
+ I<(outs I32:$dst),
+ (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$count),
+ (outs), (ins P2Align:$p2align, offset32_op:$off), [],
+ "atomic.notify \t$dst, ${off}(${addr})${p2align}, $count",
+ "atomic.notify \t${off}, ${p2align}", 0xfe00>;
+let mayLoad = 1 in {
+defm ATOMIC_WAIT_I32 :
+ I<(outs I32:$dst),
+ (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$exp, I64:$timeout),
+ (outs), (ins P2Align:$p2align, offset32_op:$off), [],
+ "i32.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
+ "i32.atomic.wait \t${off}, ${p2align}", 0xfe01>;
+defm ATOMIC_WAIT_I64 :
+ I<(outs I32:$dst),
+ (ins P2Align:$p2align, offset32_op:$off, I32:$addr, I64:$exp, I64:$timeout),
+ (outs), (ins P2Align:$p2align, offset32_op:$off), [],
+ "i64.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
+ "i64.atomic.wait \t${off}, ${p2align}", 0xfe02>;
+} // mayLoad = 1
+} // hasSideEffects = 1
+
+let Predicates = [HasAtomics] in {
+// Select notifys with no constant offset.
+class NotifyPatNoOffset<Intrinsic kind> :
+ Pat<(i32 (kind I32:$addr, I32:$count)),
+ (ATOMIC_NOTIFY 0, 0, I32:$addr, I32:$count)>;
+def : NotifyPatNoOffset<int_wasm_atomic_notify>;
+
+// Select notifys with a constant offset.
+
+// Pattern with address + immediate offset
+class NotifyPatImmOff<Intrinsic kind, PatFrag operand> :
+ Pat<(i32 (kind (operand I32:$addr, imm:$off), I32:$count)),
+ (ATOMIC_NOTIFY 0, imm:$off, I32:$addr, I32:$count)>;
+def : NotifyPatImmOff<int_wasm_atomic_notify, regPlusImm>;
+def : NotifyPatImmOff<int_wasm_atomic_notify, or_is_add>;
+
+class NotifyPatGlobalAddr<Intrinsic kind> :
+ Pat<(i32 (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
+ I32:$count)),
+ (ATOMIC_NOTIFY 0, tglobaladdr:$off, I32:$addr, I32:$count)>;
+def : NotifyPatGlobalAddr<int_wasm_atomic_notify>;
+
+class NotifyPatExternalSym<Intrinsic kind> :
+ Pat<(i32 (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
+ I32:$count)),
+ (ATOMIC_NOTIFY 0, texternalsym:$off, I32:$addr, I32:$count)>;
+def : NotifyPatExternalSym<int_wasm_atomic_notify>;
+
+// Select notifys with just a constant offset.
+class NotifyPatOffsetOnly<Intrinsic kind> :
+ Pat<(i32 (kind imm:$off, I32:$count)),
+ (ATOMIC_NOTIFY 0, imm:$off, (CONST_I32 0), I32:$count)>;
+def : NotifyPatOffsetOnly<int_wasm_atomic_notify>;
+
+class NotifyPatGlobalAddrOffOnly<Intrinsic kind> :
+ Pat<(i32 (kind (WebAssemblywrapper tglobaladdr:$off), I32:$count)),
+ (ATOMIC_NOTIFY 0, tglobaladdr:$off, (CONST_I32 0), I32:$count)>;
+def : NotifyPatGlobalAddrOffOnly<int_wasm_atomic_notify>;
+
+class NotifyPatExternSymOffOnly<Intrinsic kind> :
+ Pat<(i32 (kind (WebAssemblywrapper texternalsym:$off), I32:$count)),
+ (ATOMIC_NOTIFY 0, texternalsym:$off, (CONST_I32 0), I32:$count)>;
+def : NotifyPatExternSymOffOnly<int_wasm_atomic_notify>;
+
+// Select waits with no constant offset.
+class WaitPatNoOffset<ValueType ty, Intrinsic kind, NI inst> :
+ Pat<(i32 (kind I32:$addr, ty:$exp, I64:$timeout)),
+ (inst 0, 0, I32:$addr, ty:$exp, I64:$timeout)>;
+def : WaitPatNoOffset<i32, int_wasm_atomic_wait_i32, ATOMIC_WAIT_I32>;
+def : WaitPatNoOffset<i64, int_wasm_atomic_wait_i64, ATOMIC_WAIT_I64>;
+
+// Select waits with a constant offset.
+
+// Pattern with address + immediate offset
+class WaitPatImmOff<ValueType ty, Intrinsic kind, PatFrag operand, NI inst> :
+ Pat<(i32 (kind (operand I32:$addr, imm:$off), ty:$exp, I64:$timeout)),
+ (inst 0, imm:$off, I32:$addr, ty:$exp, I64:$timeout)>;
+def : WaitPatImmOff<i32, int_wasm_atomic_wait_i32, regPlusImm, ATOMIC_WAIT_I32>;
+def : WaitPatImmOff<i32, int_wasm_atomic_wait_i32, or_is_add, ATOMIC_WAIT_I32>;
+def : WaitPatImmOff<i64, int_wasm_atomic_wait_i64, regPlusImm, ATOMIC_WAIT_I64>;
+def : WaitPatImmOff<i64, int_wasm_atomic_wait_i64, or_is_add, ATOMIC_WAIT_I64>;
+
+class WaitPatGlobalAddr<ValueType ty, Intrinsic kind, NI inst> :
+ Pat<(i32 (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
+ ty:$exp, I64:$timeout)),
+ (inst 0, tglobaladdr:$off, I32:$addr, ty:$exp, I64:$timeout)>;
+def : WaitPatGlobalAddr<i32, int_wasm_atomic_wait_i32, ATOMIC_WAIT_I32>;
+def : WaitPatGlobalAddr<i64, int_wasm_atomic_wait_i64, ATOMIC_WAIT_I64>;
+
+class WaitPatExternalSym<ValueType ty, Intrinsic kind, NI inst> :
+ Pat<(i32 (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
+ ty:$exp, I64:$timeout)),
+ (inst 0, texternalsym:$off, I32:$addr, ty:$exp, I64:$timeout)>;
+def : WaitPatExternalSym<i32, int_wasm_atomic_wait_i32, ATOMIC_WAIT_I32>;
+def : WaitPatExternalSym<i64, int_wasm_atomic_wait_i64, ATOMIC_WAIT_I64>;
+
+// Select wait_i32, ATOMIC_WAIT_I32s with just a constant offset.
+class WaitPatOffsetOnly<ValueType ty, Intrinsic kind, NI inst> :
+ Pat<(i32 (kind imm:$off, ty:$exp, I64:$timeout)),
+ (inst 0, imm:$off, (CONST_I32 0), ty:$exp, I64:$timeout)>;
+def : WaitPatOffsetOnly<i32, int_wasm_atomic_wait_i32, ATOMIC_WAIT_I32>;
+def : WaitPatOffsetOnly<i64, int_wasm_atomic_wait_i64, ATOMIC_WAIT_I64>;
+
+class WaitPatGlobalAddrOffOnly<ValueType ty, Intrinsic kind, NI inst> :
+ Pat<(i32 (kind (WebAssemblywrapper tglobaladdr:$off), ty:$exp, I64:$timeout)),
+ (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$exp, I64:$timeout)>;
+def : WaitPatGlobalAddrOffOnly<i32, int_wasm_atomic_wait_i32, ATOMIC_WAIT_I32>;
+def : WaitPatGlobalAddrOffOnly<i64, int_wasm_atomic_wait_i64, ATOMIC_WAIT_I64>;
+
+class WaitPatExternSymOffOnly<ValueType ty, Intrinsic kind, NI inst> :
+ Pat<(i32 (kind (WebAssemblywrapper texternalsym:$off), ty:$exp,
+ I64:$timeout)),
+ (inst 0, texternalsym:$off, (CONST_I32 0), ty:$exp, I64:$timeout)>;
+def : WaitPatExternSymOffOnly<i32, int_wasm_atomic_wait_i32, ATOMIC_WAIT_I32>;
+def : WaitPatExternSymOffOnly<i64, int_wasm_atomic_wait_i64, ATOMIC_WAIT_I64>;
+} // Predicates = [HasAtomics]
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/lib/Target/WebAssembly/WebAssemblyInstrCall.td
index 34262752430c..07839b790114 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrCall.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrCall.td
@@ -15,8 +15,6 @@
// TODO: addr64: These currently assume the callee address is 32-bit.
// FIXME: add $type to first call_indirect asmstr (and maybe $flags)
-let Defs = [ARGUMENTS] in {
-
// Call sequence markers. These have an immediate which represents the amount of
// stack space to allocate or free, which is used for varargs lowering.
let Uses = [SP32, SP64], Defs = [SP32, SP64], isCodeGenOnly = 1 in {
@@ -52,34 +50,35 @@ multiclass CALL<WebAssemblyRegClass vt, string prefix> {
}
multiclass SIMD_CALL<ValueType vt, string prefix> {
- defm CALL_#vt : SIMD_I<(outs V128:$dst), (ins function32_op:$callee,
- variable_ops),
- (outs), (ins function32_op:$callee),
- [(set (vt V128:$dst),
- (WebAssemblycall1 (i32 imm:$callee)))],
- !strconcat(prefix, "call\t$dst, $callee"),
- !strconcat(prefix, "call\t$callee"),
- 0x10>;
+
+ defm CALL_#vt : I<(outs V128:$dst), (ins function32_op:$callee, variable_ops),
+ (outs), (ins function32_op:$callee),
+ [(set (vt V128:$dst),
+ (WebAssemblycall1 (i32 imm:$callee)))],
+ !strconcat(prefix, "call\t$dst, $callee"),
+ !strconcat(prefix, "call\t$callee"),
+ 0x10>,
+ Requires<[HasSIMD128]>;
let isCodeGenOnly = 1 in {
- defm PCALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst),
- (ins I32:$callee, variable_ops),
- (outs), (ins I32:$callee),
- [(set (vt V128:$dst),
- (WebAssemblycall1 I32:$callee))],
- "PSEUDO CALL INDIRECT\t$callee",
- "PSEUDO CALL INDIRECT\t$callee">;
+ defm PCALL_INDIRECT_#vt : I<(outs V128:$dst),
+ (ins I32:$callee, variable_ops),
+ (outs), (ins I32:$callee),
+ [(set (vt V128:$dst),
+ (WebAssemblycall1 I32:$callee))],
+ "PSEUDO CALL INDIRECT\t$callee",
+ "PSEUDO CALL INDIRECT\t$callee">,
+ Requires<[HasSIMD128]>;
} // isCodeGenOnly = 1
- defm CALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst),
- (ins TypeIndex:$type, i32imm:$flags,
- variable_ops),
- (outs), (ins TypeIndex:$type, i32imm:$flags),
- [],
- !strconcat(prefix,
- "call_indirect\t$dst"),
- !strconcat(prefix, "call_indirect\t$type"),
- 0x11>;
+ defm CALL_INDIRECT_#vt : I<(outs V128:$dst),
+ (ins TypeIndex:$type, i32imm:$flags, variable_ops),
+ (outs), (ins TypeIndex:$type, i32imm:$flags),
+ [],
+ !strconcat(prefix, "call_indirect\t$dst"),
+ !strconcat(prefix, "call_indirect\t$type"),
+ 0x11>,
+ Requires<[HasSIMD128]>;
}
let Uses = [SP32, SP64], isCall = 1 in {
@@ -88,10 +87,12 @@ let Uses = [SP32, SP64], isCall = 1 in {
defm "" : CALL<F32, "f32.">;
defm "" : CALL<F64, "f64.">;
defm "" : CALL<EXCEPT_REF, "except_ref.">;
- defm "" : SIMD_CALL<v16i8, "i8x16.">;
- defm "" : SIMD_CALL<v8i16, "i16x8.">;
- defm "" : SIMD_CALL<v4i32, "i32x4.">;
- defm "" : SIMD_CALL<v4f32, "f32x4.">;
+ defm "" : SIMD_CALL<v16i8, "v128.">;
+ defm "" : SIMD_CALL<v8i16, "v128.">;
+ defm "" : SIMD_CALL<v4i32, "v128.">;
+ defm "" : SIMD_CALL<v2i64, "v128.">;
+ defm "" : SIMD_CALL<v4f32, "v128.">;
+ defm "" : SIMD_CALL<v2f64, "v128.">;
defm CALL_VOID : I<(outs), (ins function32_op:$callee, variable_ops),
(outs), (ins function32_op:$callee),
@@ -115,8 +116,6 @@ let Uses = [SP32, SP64], isCall = 1 in {
0x11>;
} // Uses = [SP32,SP64], isCall = 1
-} // Defs = [ARGUMENTS]
-
// Patterns for matching a direct call to a global address.
def : Pat<(i32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
(CALL_I32 tglobaladdr:$callee)>;
@@ -132,8 +131,12 @@ def : Pat<(v8i16 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
(CALL_v8i16 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
def : Pat<(v4i32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
(CALL_v4i32 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
+def : Pat<(v2i64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
+ (CALL_v2i64 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
def : Pat<(v4f32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
(CALL_v4f32 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
+def : Pat<(v2f64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
+ (CALL_v2f64 tglobaladdr:$callee)>, Requires<[HasSIMD128]>;
def : Pat<(ExceptRef
(WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))),
(CALL_EXCEPT_REF tglobaladdr:$callee)>;
@@ -155,8 +158,12 @@ def : Pat<(v8i16 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
(CALL_v8i16 texternalsym:$callee)>, Requires<[HasSIMD128]>;
def : Pat<(v4i32 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
(CALL_v4i32 texternalsym:$callee)>, Requires<[HasSIMD128]>;
+def : Pat<(v2i64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
+ (CALL_v2i64 texternalsym:$callee)>, Requires<[HasSIMD128]>;
def : Pat<(v4f32 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
(CALL_v4f32 texternalsym:$callee)>, Requires<[HasSIMD128]>;
+def : Pat<(v2f64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
+ (CALL_v2f64 texternalsym:$callee)>, Requires<[HasSIMD128]>;
def : Pat<(ExceptRef
(WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))),
(CALL_EXCEPT_REF texternalsym:$callee)>;
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/lib/Target/WebAssembly/WebAssemblyInstrControl.td
index d90244b90662..7eb6cbf4d249 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrControl.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrControl.td
@@ -12,8 +12,6 @@
///
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
-
let isBranch = 1, isTerminator = 1, hasCtrlDep = 1 in {
// The condition operand is a boolean value which WebAssembly represents as i32.
defm BR_IF : I<(outs), (ins bb_op:$dst, I32:$cond),
@@ -30,47 +28,37 @@ defm BR : NRI<(outs), (ins bb_op:$dst),
} // isBarrier = 1
} // isBranch = 1, isTerminator = 1, hasCtrlDep = 1
-} // Defs = [ARGUMENTS]
-
def : Pat<(brcond (i32 (setne I32:$cond, 0)), bb:$dst),
(BR_IF bb_op:$dst, I32:$cond)>;
def : Pat<(brcond (i32 (seteq I32:$cond, 0)), bb:$dst),
(BR_UNLESS bb_op:$dst, I32:$cond)>;
-let Defs = [ARGUMENTS] in {
+// A list of branch targets enclosed in {} and separated by comma.
+// Used by br_table only.
+def BrListAsmOperand : AsmOperandClass { let Name = "BrList"; }
+let OperandNamespace = "WebAssembly" in {
+let OperandType = "OPERAND_BRLIST" in {
+def brlist : Operand<i32> {
+ let ParserMatchClass = BrListAsmOperand;
+ let PrintMethod = "printBrList";
+}
+} // OPERAND_BRLIST
+} // OperandNamespace = "WebAssembly"
// TODO: SelectionDAG's lowering insists on using a pointer as the index for
// jump tables, so in practice we don't ever use BR_TABLE_I64 in wasm32 mode
// currently.
-// Set TSFlags{0} to 1 to indicate that the variable_ops are immediates.
-// Set TSFlags{1} to 1 to indicate that the immediates represent labels.
-// FIXME: this can't inherit from I<> since there is no way to inherit from a
-// multiclass and still have the let statements.
let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in {
-def BR_TABLE_I32 : NI<(outs), (ins I32:$index, variable_ops),
- [(WebAssemblybr_table I32:$index)], 0,
- "br_table \t$index", 0x0e> {
- let TSFlags{0} = 1;
- let TSFlags{1} = 1;
-}
-def BR_TABLE_I32_S : NI<(outs), (ins I32:$index),
- [], 1,
- "br_table \t$index", 0x0e> {
- let TSFlags{0} = 1;
- let TSFlags{1} = 1;
-}
-def BR_TABLE_I64 : NI<(outs), (ins I64:$index, variable_ops),
- [(WebAssemblybr_table I64:$index)], 0,
- "br_table \t$index"> {
- let TSFlags{0} = 1;
- let TSFlags{1} = 1;
-}
-def BR_TABLE_I64_S : NI<(outs), (ins I64:$index),
- [], 1,
- "br_table \t$index"> {
- let TSFlags{0} = 1;
- let TSFlags{1} = 1;
-}
+defm BR_TABLE_I32 : I<(outs), (ins I32:$index, variable_ops),
+ (outs), (ins brlist:$brl),
+ [(WebAssemblybr_table I32:$index)],
+ "br_table \t$index", "br_table \t$brl",
+ 0x0e>;
+defm BR_TABLE_I64 : I<(outs), (ins I64:$index, variable_ops),
+ (outs), (ins brlist:$brl),
+ [(WebAssemblybr_table I64:$index)],
+ "br_table \t$index", "br_table \t$brl",
+ 0x0e>;
} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
// This is technically a control-flow instruction, since all it affects is the
@@ -81,13 +69,19 @@ defm NOP : NRI<(outs), (ins), [], "nop", 0x01>;
// These use/clobber VALUE_STACK to prevent them from being moved into the
// middle of an expression tree.
let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in {
-defm BLOCK : NRI<(outs), (ins Signature:$sig), [], "block \t$sig", 0x02>;
-defm LOOP : NRI<(outs), (ins Signature:$sig), [], "loop \t$sig", 0x03>;
+defm BLOCK : NRI<(outs), (ins Signature:$sig), [], "block \t$sig", 0x02>;
+defm LOOP : NRI<(outs), (ins Signature:$sig), [], "loop \t$sig", 0x03>;
+
+defm IF : I<(outs), (ins Signature:$sig, I32:$cond),
+ (outs), (ins Signature:$sig),
+ [], "if \t$sig, $cond", "if \t$sig", 0x04>;
+defm ELSE : NRI<(outs), (ins), [], "else", 0x05>;
-// END_BLOCK, END_LOOP, and END_FUNCTION are represented with the same opcode in
-// wasm.
+// END_BLOCK, END_LOOP, END_IF and END_FUNCTION are represented with the same
+// opcode in wasm.
defm END_BLOCK : NRI<(outs), (ins), [], "end_block", 0x0b>;
defm END_LOOP : NRI<(outs), (ins), [], "end_loop", 0x0b>;
+defm END_IF : NRI<(outs), (ins), [], "end_if", 0x0b>;
let isTerminator = 1, isBarrier = 1 in
defm END_FUNCTION : NRI<(outs), (ins), [], "end_function", 0x0b>;
} // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
@@ -103,14 +97,16 @@ multiclass RETURN<WebAssemblyRegClass vt> {
}
multiclass SIMD_RETURN<ValueType vt> {
- defm RETURN_#vt : SIMD_I<(outs), (ins V128:$val), (outs), (ins),
- [(WebAssemblyreturn (vt V128:$val))],
- "return \t$val", "return", 0x0f>;
+ defm RETURN_#vt : I<(outs), (ins V128:$val), (outs), (ins),
+ [(WebAssemblyreturn (vt V128:$val))],
+ "return \t$val", "return", 0x0f>,
+ Requires<[HasSIMD128]>;
// Equivalent to RETURN_#vt, for use at the end of a function when wasm
// semantics return by falling off the end of the block.
let isCodeGenOnly = 1 in
- defm FALLTHROUGH_RETURN_#vt : SIMD_I<(outs), (ins V128:$val), (outs), (ins),
- []>;
+ defm FALLTHROUGH_RETURN_#vt : I<(outs), (ins V128:$val), (outs), (ins),
+ []>,
+ Requires<[HasSIMD128]>;
}
let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in {
@@ -124,7 +120,9 @@ let isReturn = 1 in {
defm "": SIMD_RETURN<v16i8>;
defm "": SIMD_RETURN<v8i16>;
defm "": SIMD_RETURN<v4i32>;
+ defm "": SIMD_RETURN<v2i64>;
defm "": SIMD_RETURN<v4f32>;
+ defm "": SIMD_RETURN<v2f64>;
defm RETURN_VOID : NRI<(outs), (ins), [(WebAssemblyreturn)], "return", 0x0f>;
@@ -144,14 +142,16 @@ let Predicates = [HasExceptionHandling] in {
// Throwing an exception: throw / rethrow
let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in {
-defm THROW_I32 : I<(outs), (ins i32imm:$tag, I32:$val),
- (outs), (ins i32imm:$tag),
- [(int_wasm_throw imm:$tag, I32:$val)],
+defm THROW_I32 : I<(outs), (ins event_op:$tag, I32:$val),
+ (outs), (ins event_op:$tag),
+ [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag),
+ I32:$val)],
"throw \t$tag, $val", "throw \t$tag",
0x08>;
-defm THROW_I64 : I<(outs), (ins i32imm:$tag, I64:$val),
- (outs), (ins i32imm:$tag),
- [(int_wasm_throw imm:$tag, I64:$val)],
+defm THROW_I64 : I<(outs), (ins event_op:$tag, I64:$val),
+ (outs), (ins event_op:$tag),
+ [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag),
+ I64:$val)],
"throw \t$tag, $val", "throw \t$tag",
0x08>;
defm RETHROW : NRI<(outs), (ins bb_op:$dst), [], "rethrow \t$dst", 0x09>;
@@ -168,7 +168,7 @@ defm END_TRY : NRI<(outs), (ins), [], "end_try", 0x0b>;
} // Uses = [VALUE_STACK], Defs = [VALUE_STACK]
// Catching an exception: catch / catch_all
-let hasCtrlDep = 1 in {
+let hasCtrlDep = 1, hasSideEffects = 1 in {
defm CATCH_I32 : I<(outs I32:$dst), (ins i32imm:$tag),
(outs), (ins i32imm:$tag),
[(set I32:$dst, (int_wasm_catch imm:$tag))],
@@ -181,14 +181,10 @@ defm CATCH_ALL : NRI<(outs), (ins), [], "catch_all", 0x05>;
}
// Pseudo instructions: cleanupret / catchret
-// They are not return instructions in wasm, but setting 'isReturn' to true as
-// in X86 is necessary for computing EH scope membership.
let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1,
- isCodeGenOnly = 1, isReturn = 1 in {
+ isCodeGenOnly = 1, isEHScopeReturn = 1 in {
defm CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "", 0>;
defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from),
[(catchret bb:$dst, bb:$from)], "", 0>;
}
}
-
-} // Defs = [ARGUMENTS]
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrConv.td b/lib/Target/WebAssembly/WebAssemblyInstrConv.td
index c89c1b549816..e128656a142c 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrConv.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrConv.td
@@ -13,19 +13,17 @@
///
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
-
defm I32_WRAP_I64 : I<(outs I32:$dst), (ins I64:$src), (outs), (ins),
[(set I32:$dst, (trunc I64:$src))],
- "i32.wrap/i64\t$dst, $src", "i32.wrap/i64", 0xa7>;
+ "i32.wrap_i64\t$dst, $src", "i32.wrap_i64", 0xa7>;
defm I64_EXTEND_S_I32 : I<(outs I64:$dst), (ins I32:$src), (outs), (ins),
[(set I64:$dst, (sext I32:$src))],
- "i64.extend_s/i32\t$dst, $src", "i64.extend_s/i32",
+ "i64.extend_i32_s\t$dst, $src", "i64.extend_i32_s",
0xac>;
defm I64_EXTEND_U_I32 : I<(outs I64:$dst), (ins I32:$src), (outs), (ins),
[(set I64:$dst, (zext I32:$src))],
- "i64.extend_u/i32\t$dst, $src", "i64.extend_u/i32",
+ "i64.extend_i32_u\t$dst, $src", "i64.extend_i32_u",
0xad>;
let Predicates = [HasSignExt] in {
@@ -51,58 +49,72 @@ defm I64_EXTEND32_S_I64 : I<(outs I64:$dst), (ins I64:$src), (outs), (ins),
0xc4>;
} // Predicates = [HasSignExt]
-} // defs = [ARGUMENTS]
-
// Expand a "don't care" extend into zero-extend (chosen over sign-extend
// somewhat arbitrarily, although it favors popular hardware architectures
// and is conceptually a simpler operation).
def : Pat<(i64 (anyext I32:$src)), (I64_EXTEND_U_I32 I32:$src)>;
-let Defs = [ARGUMENTS] in {
-
// Conversion from floating point to integer instructions which don't trap on
// overflow or invalid.
defm I32_TRUNC_S_SAT_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins),
[(set I32:$dst, (fp_to_sint F32:$src))],
- "i32.trunc_s:sat/f32\t$dst, $src",
- "i32.trunc_s:sat/f32", 0xfc00>,
+ "i32.trunc_sat_f32_s\t$dst, $src",
+ "i32.trunc_sat_f32_s", 0xfc00>,
Requires<[HasNontrappingFPToInt]>;
defm I32_TRUNC_U_SAT_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins),
[(set I32:$dst, (fp_to_uint F32:$src))],
- "i32.trunc_u:sat/f32\t$dst, $src",
- "i32.trunc_u:sat/f32", 0xfc01>,
+ "i32.trunc_sat_f32_u\t$dst, $src",
+ "i32.trunc_sat_f32_u", 0xfc01>,
Requires<[HasNontrappingFPToInt]>;
defm I64_TRUNC_S_SAT_F32 : I<(outs I64:$dst), (ins F32:$src), (outs), (ins),
[(set I64:$dst, (fp_to_sint F32:$src))],
- "i64.trunc_s:sat/f32\t$dst, $src",
- "i64.trunc_s:sat/f32", 0xfc04>,
+ "i64.trunc_sat_f32_s\t$dst, $src",
+ "i64.trunc_sat_f32_s", 0xfc04>,
Requires<[HasNontrappingFPToInt]>;
defm I64_TRUNC_U_SAT_F32 : I<(outs I64:$dst), (ins F32:$src), (outs), (ins),
[(set I64:$dst, (fp_to_uint F32:$src))],
- "i64.trunc_u:sat/f32\t$dst, $src",
- "i64.trunc_u:sat/f32", 0xfc05>,
+ "i64.trunc_sat_f32_u\t$dst, $src",
+ "i64.trunc_sat_f32_u", 0xfc05>,
Requires<[HasNontrappingFPToInt]>;
defm I32_TRUNC_S_SAT_F64 : I<(outs I32:$dst), (ins F64:$src), (outs), (ins),
[(set I32:$dst, (fp_to_sint F64:$src))],
- "i32.trunc_s:sat/f64\t$dst, $src",
- "i32.trunc_s:sat/f64", 0xfc02>,
+ "i32.trunc_sat_f64_s\t$dst, $src",
+ "i32.trunc_sat_f64_s", 0xfc02>,
Requires<[HasNontrappingFPToInt]>;
defm I32_TRUNC_U_SAT_F64 : I<(outs I32:$dst), (ins F64:$src), (outs), (ins),
[(set I32:$dst, (fp_to_uint F64:$src))],
- "i32.trunc_u:sat/f64\t$dst, $src",
- "i32.trunc_u:sat/f64", 0xfc03>,
+ "i32.trunc_sat_f64_u\t$dst, $src",
+ "i32.trunc_sat_f64_u", 0xfc03>,
Requires<[HasNontrappingFPToInt]>;
defm I64_TRUNC_S_SAT_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
[(set I64:$dst, (fp_to_sint F64:$src))],
- "i64.trunc_s:sat/f64\t$dst, $src",
- "i64.trunc_s:sat/f64", 0xfc06>,
+ "i64.trunc_sat_f64_s\t$dst, $src",
+ "i64.trunc_sat_f64_s", 0xfc06>,
Requires<[HasNontrappingFPToInt]>;
defm I64_TRUNC_U_SAT_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
[(set I64:$dst, (fp_to_uint F64:$src))],
- "i64.trunc_u:sat/f64\t$dst, $src",
- "i64.trunc_u:sat/f64", 0xfc07>,
+ "i64.trunc_sat_f64_u\t$dst, $src",
+ "i64.trunc_sat_f64_u", 0xfc07>,
Requires<[HasNontrappingFPToInt]>;
+// Lower llvm.wasm.trunc.saturate.* to saturating instructions
+def : Pat<(int_wasm_trunc_saturate_signed F32:$src),
+ (I32_TRUNC_S_SAT_F32 F32:$src)>;
+def : Pat<(int_wasm_trunc_saturate_unsigned F32:$src),
+ (I32_TRUNC_U_SAT_F32 F32:$src)>;
+def : Pat<(int_wasm_trunc_saturate_signed F64:$src),
+ (I32_TRUNC_S_SAT_F64 F64:$src)>;
+def : Pat<(int_wasm_trunc_saturate_unsigned F64:$src),
+ (I32_TRUNC_U_SAT_F64 F64:$src)>;
+def : Pat<(int_wasm_trunc_saturate_signed F32:$src),
+ (I64_TRUNC_S_SAT_F32 F32:$src)>;
+def : Pat<(int_wasm_trunc_saturate_unsigned F32:$src),
+ (I64_TRUNC_U_SAT_F32 F32:$src)>;
+def : Pat<(int_wasm_trunc_saturate_signed F64:$src),
+ (I64_TRUNC_S_SAT_F64 F64:$src)>;
+def : Pat<(int_wasm_trunc_saturate_unsigned F64:$src),
+ (I64_TRUNC_U_SAT_F64 F64:$src)>;
+
// Conversion from floating point to integer pseudo-instructions which don't
// trap on overflow or invalid.
let usesCustomInserter = 1, isCodeGenOnly = 1 in {
@@ -135,88 +147,86 @@ defm FP_TO_UINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
// Conversion from floating point to integer traps on overflow and invalid.
let hasSideEffects = 1 in {
defm I32_TRUNC_S_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins),
- [], "i32.trunc_s/f32\t$dst, $src", "i32.trunc_s/f32",
+ [], "i32.trunc_f32_s\t$dst, $src", "i32.trunc_f32_s",
0xa8>;
defm I32_TRUNC_U_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins),
- [], "i32.trunc_u/f32\t$dst, $src", "i32.trunc_u/f32",
+ [], "i32.trunc_f32_u\t$dst, $src", "i32.trunc_f32_u",
0xa9>;
defm I64_TRUNC_S_F32 : I<(outs I64:$dst), (ins F32:$src), (outs), (ins),
- [], "i64.trunc_s/f32\t$dst, $src", "i64.trunc_s/f32",
+ [], "i64.trunc_f32_s\t$dst, $src", "i64.trunc_f32_s",
0xae>;
defm I64_TRUNC_U_F32 : I<(outs I64:$dst), (ins F32:$src), (outs), (ins),
- [], "i64.trunc_u/f32\t$dst, $src", "i64.trunc_u/f32",
+ [], "i64.trunc_f32_u\t$dst, $src", "i64.trunc_f32_u",
0xaf>;
defm I32_TRUNC_S_F64 : I<(outs I32:$dst), (ins F64:$src), (outs), (ins),
- [], "i32.trunc_s/f64\t$dst, $src", "i32.trunc_s/f64",
+ [], "i32.trunc_f64_s\t$dst, $src", "i32.trunc_f64_s",
0xaa>;
defm I32_TRUNC_U_F64 : I<(outs I32:$dst), (ins F64:$src), (outs), (ins),
- [], "i32.trunc_u/f64\t$dst, $src", "i32.trunc_u/f64",
+ [], "i32.trunc_f64_u\t$dst, $src", "i32.trunc_f64_u",
0xab>;
defm I64_TRUNC_S_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
- [], "i64.trunc_s/f64\t$dst, $src", "i64.trunc_s/f64",
+ [], "i64.trunc_f64_s\t$dst, $src", "i64.trunc_f64_s",
0xb0>;
defm I64_TRUNC_U_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
- [], "i64.trunc_u/f64\t$dst, $src", "i64.trunc_u/f64",
+ [], "i64.trunc_f64_u\t$dst, $src", "i64.trunc_f64_u",
0xb1>;
} // hasSideEffects = 1
defm F32_CONVERT_S_I32 : I<(outs F32:$dst), (ins I32:$src), (outs), (ins),
[(set F32:$dst, (sint_to_fp I32:$src))],
- "f32.convert_s/i32\t$dst, $src", "f32.convert_s/i32",
+ "f32.convert_i32_s\t$dst, $src", "f32.convert_i32_s",
0xb2>;
defm F32_CONVERT_U_I32 : I<(outs F32:$dst), (ins I32:$src), (outs), (ins),
[(set F32:$dst, (uint_to_fp I32:$src))],
- "f32.convert_u/i32\t$dst, $src", "f32.convert_u/i32",
+ "f32.convert_i32_u\t$dst, $src", "f32.convert_i32_u",
0xb3>;
defm F64_CONVERT_S_I32 : I<(outs F64:$dst), (ins I32:$src), (outs), (ins),
[(set F64:$dst, (sint_to_fp I32:$src))],
- "f64.convert_s/i32\t$dst, $src", "f64.convert_s/i32",
+ "f64.convert_i32_s\t$dst, $src", "f64.convert_i32_s",
0xb7>;
defm F64_CONVERT_U_I32 : I<(outs F64:$dst), (ins I32:$src), (outs), (ins),
[(set F64:$dst, (uint_to_fp I32:$src))],
- "f64.convert_u/i32\t$dst, $src", "f64.convert_u/i32",
+ "f64.convert_i32_u\t$dst, $src", "f64.convert_i32_u",
0xb8>;
defm F32_CONVERT_S_I64 : I<(outs F32:$dst), (ins I64:$src), (outs), (ins),
[(set F32:$dst, (sint_to_fp I64:$src))],
- "f32.convert_s/i64\t$dst, $src", "f32.convert_s/i64",
+ "f32.convert_i64_s\t$dst, $src", "f32.convert_i64_s",
0xb4>;
defm F32_CONVERT_U_I64 : I<(outs F32:$dst), (ins I64:$src), (outs), (ins),
[(set F32:$dst, (uint_to_fp I64:$src))],
- "f32.convert_u/i64\t$dst, $src", "f32.convert_u/i64",
+ "f32.convert_i64_u\t$dst, $src", "f32.convert_i64_u",
0xb5>;
defm F64_CONVERT_S_I64 : I<(outs F64:$dst), (ins I64:$src), (outs), (ins),
[(set F64:$dst, (sint_to_fp I64:$src))],
- "f64.convert_s/i64\t$dst, $src", "f64.convert_s/i64",
+ "f64.convert_i64_s\t$dst, $src", "f64.convert_i64_s",
0xb9>;
defm F64_CONVERT_U_I64 : I<(outs F64:$dst), (ins I64:$src), (outs), (ins),
[(set F64:$dst, (uint_to_fp I64:$src))],
- "f64.convert_u/i64\t$dst, $src", "f64.convert_u/i64",
+ "f64.convert_i64_u\t$dst, $src", "f64.convert_i64_u",
0xba>;
defm F64_PROMOTE_F32 : I<(outs F64:$dst), (ins F32:$src), (outs), (ins),
[(set F64:$dst, (fpextend F32:$src))],
- "f64.promote/f32\t$dst, $src", "f64.promote/f32",
+ "f64.promote_f32\t$dst, $src", "f64.promote_f32",
0xbb>;
defm F32_DEMOTE_F64 : I<(outs F32:$dst), (ins F64:$src), (outs), (ins),
[(set F32:$dst, (fpround F64:$src))],
- "f32.demote/f64\t$dst, $src", "f32.demote/f64",
+ "f32.demote_f64\t$dst, $src", "f32.demote_f64",
0xb6>;
defm I32_REINTERPRET_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins),
[(set I32:$dst, (bitconvert F32:$src))],
- "i32.reinterpret/f32\t$dst, $src",
- "i32.reinterpret/f32", 0xbc>;
+ "i32.reinterpret_f32\t$dst, $src",
+ "i32.reinterpret_f32", 0xbc>;
defm F32_REINTERPRET_I32 : I<(outs F32:$dst), (ins I32:$src), (outs), (ins),
[(set F32:$dst, (bitconvert I32:$src))],
- "f32.reinterpret/i32\t$dst, $src",
- "f32.reinterpret/i32", 0xbe>;
+ "f32.reinterpret_i32\t$dst, $src",
+ "f32.reinterpret_i32", 0xbe>;
defm I64_REINTERPRET_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
[(set I64:$dst, (bitconvert F64:$src))],
- "i64.reinterpret/f64\t$dst, $src",
- "i64.reinterpret/f64", 0xbd>;
+ "i64.reinterpret_f64\t$dst, $src",
+ "i64.reinterpret_f64", 0xbd>;
defm F64_REINTERPRET_I64 : I<(outs F64:$dst), (ins I64:$src), (outs), (ins),
[(set F64:$dst, (bitconvert I64:$src))],
- "f64.reinterpret/i64\t$dst, $src",
- "f64.reinterpret/i64", 0xbf>;
-
-} // Defs = [ARGUMENTS]
+ "f64.reinterpret_i64\t$dst, $src",
+ "f64.reinterpret_i64", 0xbf>;
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrExceptRef.td b/lib/Target/WebAssembly/WebAssemblyInstrExceptRef.td
index 41b39f69e51c..a251d60b89ee 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrExceptRef.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrExceptRef.td
@@ -12,8 +12,6 @@
///
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
-
defm SELECT_EXCEPT_REF : I<(outs EXCEPT_REF:$dst),
(ins EXCEPT_REF:$lhs, EXCEPT_REF:$rhs, I32:$cond),
(outs), (ins),
@@ -23,8 +21,6 @@ defm SELECT_EXCEPT_REF : I<(outs EXCEPT_REF:$dst),
"except_ref.select\t$dst, $lhs, $rhs, $cond",
"except_ref.select", 0x1b>;
-} // Defs = [ARGUMENTS]
-
def : Pat<(select (i32 (setne I32:$cond, 0)), EXCEPT_REF:$lhs, EXCEPT_REF:$rhs),
(SELECT_EXCEPT_REF EXCEPT_REF:$lhs, EXCEPT_REF:$rhs, I32:$cond)>;
def : Pat<(select (i32 (seteq I32:$cond, 0)), EXCEPT_REF:$lhs, EXCEPT_REF:$rhs),
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrFloat.td b/lib/Target/WebAssembly/WebAssemblyInstrFloat.td
index 8db75d38942b..c5290f00b431 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrFloat.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrFloat.td
@@ -12,7 +12,38 @@
///
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
+multiclass UnaryFP<SDNode node, string name, bits<32> f32Inst,
+ bits<32> f64Inst> {
+ defm _F32 : I<(outs F32:$dst), (ins F32:$src), (outs), (ins),
+ [(set F32:$dst, (node F32:$src))],
+ !strconcat("f32.", !strconcat(name, "\t$dst, $src")),
+ !strconcat("f32.", name), f32Inst>;
+ defm _F64 : I<(outs F64:$dst), (ins F64:$src), (outs), (ins),
+ [(set F64:$dst, (node F64:$src))],
+ !strconcat("f64.", !strconcat(name, "\t$dst, $src")),
+ !strconcat("f64.", name), f64Inst>;
+}
+multiclass BinaryFP<SDNode node, string name, bits<32> f32Inst,
+ bits<32> f64Inst> {
+ defm _F32 : I<(outs F32:$dst), (ins F32:$lhs, F32:$rhs), (outs), (ins),
+ [(set F32:$dst, (node F32:$lhs, F32:$rhs))],
+ !strconcat("f32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("f32.", name), f32Inst>;
+ defm _F64 : I<(outs F64:$dst), (ins F64:$lhs, F64:$rhs), (outs), (ins),
+ [(set F64:$dst, (node F64:$lhs, F64:$rhs))],
+ !strconcat("f64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("f64.", name), f64Inst>;
+}
+multiclass ComparisonFP<CondCode cond, string name, bits<32> f32Inst, bits<32> f64Inst> {
+ defm _F32 : I<(outs I32:$dst), (ins F32:$lhs, F32:$rhs), (outs), (ins),
+ [(set I32:$dst, (setcc F32:$lhs, F32:$rhs, cond))],
+ !strconcat("f32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("f32.", name), f32Inst>;
+ defm _F64 : I<(outs I32:$dst), (ins F64:$lhs, F64:$rhs), (outs), (ins),
+ [(set I32:$dst, (setcc F64:$lhs, F64:$rhs, cond))],
+ !strconcat("f64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("f64.", name), f64Inst>;
+}
let isCommutable = 1 in
defm ADD : BinaryFP<fadd, "add ", 0x92, 0xa0>;
@@ -27,8 +58,8 @@ defm NEG : UnaryFP<fneg, "neg ", 0x8c, 0x9a>;
defm COPYSIGN : BinaryFP<fcopysign, "copysign", 0x98, 0xa6>;
let isCommutable = 1 in {
-defm MIN : BinaryFP<fminnan, "min ", 0x96, 0xa4>;
-defm MAX : BinaryFP<fmaxnan, "max ", 0x97, 0xa5>;
+defm MIN : BinaryFP<fminimum, "min ", 0x96, 0xa4>;
+defm MAX : BinaryFP<fmaximum, "max ", 0x97, 0xa5>;
} // isCommutable = 1
defm CEIL : UnaryFP<fceil, "ceil", 0x8d, 0x9b>;
@@ -36,8 +67,6 @@ defm FLOOR : UnaryFP<ffloor, "floor", 0x8e, 0x9c>;
defm TRUNC : UnaryFP<ftrunc, "trunc", 0x8f, 0x9d>;
defm NEAREST : UnaryFP<fnearbyint, "nearest", 0x90, 0x9e>;
-} // Defs = [ARGUMENTS]
-
// DAGCombine oddly folds casts into the rhs of copysign. Unfold them.
def : Pat<(fcopysign F64:$lhs, F32:$rhs),
(COPYSIGN_F64 F64:$lhs, (F64_PROMOTE_F32 F32:$rhs))>;
@@ -48,8 +77,6 @@ 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)>;
-let Defs = [ARGUMENTS] in {
-
let isCommutable = 1 in {
defm EQ : ComparisonFP<SETOEQ, "eq ", 0x5b, 0x61>;
defm NE : ComparisonFP<SETUNE, "ne ", 0x5c, 0x62>;
@@ -59,8 +86,6 @@ defm LE : ComparisonFP<SETOLE, "le ", 0x5f, 0x65>;
defm GT : ComparisonFP<SETOGT, "gt ", 0x5e, 0x64>;
defm GE : ComparisonFP<SETOGE, "ge ", 0x60, 0x66>;
-} // Defs = [ARGUMENTS]
-
// Don't care floating-point comparisons, supported via other comparisons.
def : Pat<(seteq f32:$lhs, f32:$rhs), (EQ_F32 f32:$lhs, f32:$rhs)>;
def : Pat<(setne f32:$lhs, f32:$rhs), (NE_F32 f32:$lhs, f32:$rhs)>;
@@ -75,8 +100,6 @@ def : Pat<(setle f64:$lhs, f64:$rhs), (LE_F64 f64:$lhs, f64:$rhs)>;
def : Pat<(setgt f64:$lhs, f64:$rhs), (GT_F64 f64:$lhs, f64:$rhs)>;
def : Pat<(setge f64:$lhs, f64:$rhs), (GE_F64 f64:$lhs, f64:$rhs)>;
-let Defs = [ARGUMENTS] in {
-
defm SELECT_F32 : I<(outs F32:$dst), (ins F32:$lhs, F32:$rhs, I32:$cond),
(outs), (ins),
[(set F32:$dst, (select I32:$cond, F32:$lhs, F32:$rhs))],
@@ -86,8 +109,6 @@ defm SELECT_F64 : I<(outs F64:$dst), (ins F64:$lhs, F64:$rhs, I32:$cond),
[(set F64:$dst, (select I32:$cond, F64:$lhs, F64:$rhs))],
"f64.select\t$dst, $lhs, $rhs, $cond", "f64.select", 0x1b>;
-} // Defs = [ARGUMENTS]
-
// ISD::SELECT requires its operand to conform to getBooleanContents, but
// WebAssembly's select interprets any non-zero value as true, so we can fold
// a setne with 0 into a select.
@@ -101,3 +122,10 @@ def : Pat<(select (i32 (seteq I32:$cond, 0)), F32:$lhs, F32:$rhs),
(SELECT_F32 F32:$rhs, F32:$lhs, I32:$cond)>;
def : Pat<(select (i32 (seteq I32:$cond, 0)), F64:$lhs, F64:$rhs),
(SELECT_F64 F64:$rhs, F64:$lhs, I32:$cond)>;
+
+// The legalizer inserts an unnecessary `and 1` to make input conform
+// to getBooleanContents, which we can lower away.
+def : Pat<(select (i32 (and I32:$cond, 1)), F32:$lhs, F32:$rhs),
+ (SELECT_F32 F32:$lhs, F32:$rhs, I32:$cond)>;
+def : Pat<(select (i32 (and I32:$cond, 1)), F64:$lhs, F64:$rhs),
+ (SELECT_F64 F64:$lhs, F64:$rhs, I32:$cond)>;
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrFormats.td b/lib/Target/WebAssembly/WebAssemblyInstrFormats.td
index 403152c80660..15a9714a55a1 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrFormats.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrFormats.td
@@ -15,21 +15,24 @@
// WebAssembly Instruction Format.
// We instantiate 2 of these for every actual instruction (register based
// and stack based), see below.
-class WebAssemblyInst<bits<32> inst, string asmstr, bit stack> : Instruction {
- field bits<32> Inst = inst; // Instruction encoding.
- field bit StackBased = stack;
+class WebAssemblyInst<bits<32> inst, string asmstr, string stack> : StackRel,
+ Instruction {
+ bits<32> Inst = inst; // Instruction encoding.
+ string StackBased = stack;
+ string BaseName = NAME;
let Namespace = "WebAssembly";
let Pattern = [];
let AsmString = asmstr;
}
// Normal instructions. Default instantiation of a WebAssemblyInst.
-class NI<dag oops, dag iops, list<dag> pattern, bit stack, string asmstr = "",
- bits<32> inst = -1>
+class NI<dag oops, dag iops, list<dag> pattern, string stack,
+ string asmstr = "", bits<32> inst = -1>
: WebAssemblyInst<inst, asmstr, stack> {
dag OutOperandList = oops;
dag InOperandList = iops;
let Pattern = pattern;
+ let Defs = [ARGUMENTS];
}
// Generates both register and stack based versions of one actual instruction.
@@ -37,10 +40,10 @@ class NI<dag oops, dag iops, list<dag> pattern, bit stack, string asmstr = "",
// based version of this instruction, as well as the corresponding asmstr.
// The register versions have virtual-register operands which correspond to wasm
// locals or stack locations. Each use and def of the register corresponds to an
-// implicit get_local / set_local or access of stack operands in wasm. These
+// implicit local.get / local.set or access of stack operands in wasm. These
// instructions are used for ISel and all MI passes. The stack versions of the
// instructions do not have register operands (they implicitly operate on the
-// stack), and get_locals and set_locals are explicit. The register instructions
+// stack), and local.gets and local.sets are explicit. The register instructions
// are converted to their corresponding stack instructions before lowering to
// MC.
// Every instruction should want to be based on this multi-class to guarantee
@@ -48,8 +51,10 @@ class NI<dag oops, dag iops, list<dag> pattern, bit stack, string asmstr = "",
multiclass I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
list<dag> pattern_r, string asmstr_r = "", string asmstr_s = "",
bits<32> inst = -1> {
- def "" : NI<oops_r, iops_r, pattern_r, 0, asmstr_r, inst>;
- def _S : NI<oops_s, iops_s, [], 1, asmstr_s, inst>;
+ let isCodeGenOnly = 1 in
+ def "" : NI<oops_r, iops_r, pattern_r, "false", asmstr_r, inst>;
+ let BaseName = NAME in
+ def _S : NI<oops_s, iops_s, [], "true", asmstr_s, inst>;
}
// For instructions that have no register ops, so both sets are the same.
@@ -57,111 +62,3 @@ multiclass NRI<dag oops, dag iops, list<dag> pattern, string asmstr = "",
bits<32> inst = -1> {
defm "": I<oops, iops, oops, iops, pattern, asmstr, asmstr, inst>;
}
-
-multiclass SIMD_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
- list<dag> pattern_r, string asmstr_r = "",
- string asmstr_s = "", bits<32> inst = -1> {
- defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
- inst>,
- Requires<[HasSIMD128]>;
-}
-
-multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
- list<dag> pattern_r, string asmstr_r = "",
- string asmstr_s = "", bits<32> inst = -1> {
- defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
- inst>,
- Requires<[HasAtomics]>;
-}
-
-// Unary and binary instructions, for the local types that WebAssembly supports.
-multiclass UnaryInt<SDNode node, string name, bits<32> i32Inst,
- bits<32> i64Inst> {
- defm _I32 : I<(outs I32:$dst), (ins I32:$src), (outs), (ins),
- [(set I32:$dst, (node I32:$src))],
- !strconcat("i32.", !strconcat(name, "\t$dst, $src")),
- !strconcat("i32.", name), i32Inst>;
- defm _I64 : I<(outs I64:$dst), (ins I64:$src), (outs), (ins),
- [(set I64:$dst, (node I64:$src))],
- !strconcat("i64.", !strconcat(name, "\t$dst, $src")),
- !strconcat("i64.", name), i64Inst>;
-}
-multiclass BinaryInt<SDNode node, string name, bits<32> i32Inst,
- bits<32> i64Inst> {
- defm _I32 : I<(outs I32:$dst), (ins I32:$lhs, I32:$rhs), (outs), (ins),
- [(set I32:$dst, (node I32:$lhs, I32:$rhs))],
- !strconcat("i32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i32.", name), i32Inst>;
- defm _I64 : I<(outs I64:$dst), (ins I64:$lhs, I64:$rhs), (outs), (ins),
- [(set I64:$dst, (node I64:$lhs, I64:$rhs))],
- !strconcat("i64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i64.", name), i64Inst>;
-}
-multiclass UnaryFP<SDNode node, string name, bits<32> f32Inst,
- bits<32> f64Inst> {
- defm _F32 : I<(outs F32:$dst), (ins F32:$src), (outs), (ins),
- [(set F32:$dst, (node F32:$src))],
- !strconcat("f32.", !strconcat(name, "\t$dst, $src")),
- !strconcat("f32.", name), f32Inst>;
- defm _F64 : I<(outs F64:$dst), (ins F64:$src), (outs), (ins),
- [(set F64:$dst, (node F64:$src))],
- !strconcat("f64.", !strconcat(name, "\t$dst, $src")),
- !strconcat("f64.", name), f64Inst>;
-}
-multiclass BinaryFP<SDNode node, string name, bits<32> f32Inst,
- bits<32> f64Inst> {
- defm _F32 : I<(outs F32:$dst), (ins F32:$lhs, F32:$rhs), (outs), (ins),
- [(set F32:$dst, (node F32:$lhs, F32:$rhs))],
- !strconcat("f32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("f32.", name), f32Inst>;
- defm _F64 : I<(outs F64:$dst), (ins F64:$lhs, F64:$rhs), (outs), (ins),
- [(set F64:$dst, (node F64:$lhs, F64:$rhs))],
- !strconcat("f64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("f64.", name), f64Inst>;
-}
-multiclass SIMDBinary<SDNode node, SDNode fnode, string name> {
- defm _I8x16 : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs),
- (outs), (ins),
- [(set (v16i8 V128:$dst), (node V128:$lhs, V128:$rhs))],
- !strconcat("i8x16.",
- !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i8x16.", name)>;
- defm _I16x8 : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs),
- (outs), (ins),
- [(set (v8i16 V128:$dst), (node V128:$lhs, V128:$rhs))],
- !strconcat("i16x8.",
- !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i16x8.", name)>;
- defm _I32x4 : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs),
- (outs), (ins),
- [(set (v4i32 V128:$dst), (node V128:$lhs, V128:$rhs))],
- !strconcat("i32x4.",
- !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i32x4.", name)>;
- defm _F32x4 : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs),
- (outs), (ins),
- [(set (v4f32 V128:$dst), (fnode V128:$lhs, V128:$rhs))],
- !strconcat("f32x4.",
- !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("f32x4.", name)>;
-}
-multiclass ComparisonInt<CondCode cond, string name, bits<32> i32Inst, bits<32> i64Inst> {
- defm _I32 : I<(outs I32:$dst), (ins I32:$lhs, I32:$rhs), (outs), (ins),
- [(set I32:$dst, (setcc I32:$lhs, I32:$rhs, cond))],
- !strconcat("i32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i32.", name), i32Inst>;
- defm _I64 : I<(outs I32:$dst), (ins I64:$lhs, I64:$rhs), (outs), (ins),
- [(set I32:$dst, (setcc I64:$lhs, I64:$rhs, cond))],
- !strconcat("i64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("i64.", name), i64Inst>;
-}
-multiclass ComparisonFP<CondCode cond, string name, bits<32> f32Inst, bits<32> f64Inst> {
- defm _F32 : I<(outs I32:$dst), (ins F32:$lhs, F32:$rhs), (outs), (ins),
- [(set I32:$dst, (setcc F32:$lhs, F32:$rhs, cond))],
- !strconcat("f32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("f32.", name), f32Inst>;
- defm _F64 : I<(outs I32:$dst), (ins F64:$lhs, F64:$rhs), (outs), (ins),
- [(set I32:$dst, (setcc F64:$lhs, F64:$rhs, cond))],
- !strconcat("f64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
- !strconcat("f64.", name), f64Inst>;
-}
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp
index cd49bd1682ad..5efff32d6167 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp
@@ -70,6 +70,8 @@ void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
CopyOpcode = WebAssembly::COPY_F32;
else if (RC == &WebAssembly::F64RegClass)
CopyOpcode = WebAssembly::COPY_F64;
+ else if (RC == &WebAssembly::V128RegClass)
+ CopyOpcode = WebAssembly::COPY_V128;
else
llvm_unreachable("Unexpected register class");
@@ -77,10 +79,8 @@ void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
.addReg(SrcReg, KillSrc ? RegState::Kill : 0);
}
-MachineInstr *
-WebAssemblyInstrInfo::commuteInstructionImpl(MachineInstr &MI, bool NewMI,
- unsigned OpIdx1,
- unsigned OpIdx2) const {
+MachineInstr *WebAssemblyInstrInfo::commuteInstructionImpl(
+ MachineInstr &MI, bool NewMI, unsigned OpIdx1, unsigned OpIdx2) const {
// If the operands are stackified, we can't reorder them.
WebAssemblyFunctionInfo &MFI =
*MI.getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>();
@@ -165,12 +165,9 @@ unsigned WebAssemblyInstrInfo::removeBranch(MachineBasicBlock &MBB,
return Count;
}
-unsigned WebAssemblyInstrInfo::insertBranch(MachineBasicBlock &MBB,
- MachineBasicBlock *TBB,
- MachineBasicBlock *FBB,
- ArrayRef<MachineOperand> Cond,
- const DebugLoc &DL,
- int *BytesAdded) const {
+unsigned WebAssemblyInstrInfo::insertBranch(
+ MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
+ ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
assert(!BytesAdded && "code size not handled");
if (Cond.empty()) {
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index aeb282a7febb..e3d795f2aab1 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -20,6 +20,9 @@ def HasAddr32 : Predicate<"!Subtarget->hasAddr64()">;
def HasAddr64 : Predicate<"Subtarget->hasAddr64()">;
def HasSIMD128 : Predicate<"Subtarget->hasSIMD128()">,
AssemblerPredicate<"FeatureSIMD128", "simd128">;
+def HasUnimplementedSIMD128 :
+ Predicate<"Subtarget->hasUnimplementedSIMD128()">,
+ AssemblerPredicate<"FeatureUnimplementedSIMD128", "unimplemented-simd128">;
def HasAtomics : Predicate<"Subtarget->hasAtomics()">,
AssemblerPredicate<"FeatureAtomics", "atomics">;
def HasNontrappingFPToInt :
@@ -64,6 +67,7 @@ def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>;
def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>;
def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyThrow : SDTypeProfile<0, 2, [SDTCisPtrTy<0>]>;
//===----------------------------------------------------------------------===//
// WebAssembly-specific DAG Nodes.
@@ -90,6 +94,8 @@ def WebAssemblyreturn : SDNode<"WebAssemblyISD::RETURN",
SDT_WebAssemblyReturn, [SDNPHasChain]>;
def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper",
SDT_WebAssemblyWrapper>;
+def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
+ [SDNPHasChain]>;
//===----------------------------------------------------------------------===//
// WebAssembly-specific Operands.
@@ -118,6 +124,18 @@ def f32imm_op : Operand<f32>;
let OperandType = "OPERAND_F64IMM" in
def f64imm_op : Operand<f64>;
+let OperandType = "OPERAND_VEC_I8IMM" in
+def vec_i8imm_op : Operand<i32>;
+
+let OperandType = "OPERAND_VEC_I16IMM" in
+def vec_i16imm_op : Operand<i32>;
+
+let OperandType = "OPERAND_VEC_I32IMM" in
+def vec_i32imm_op : Operand<i32>;
+
+let OperandType = "OPERAND_VEC_I64IMM" in
+def vec_i64imm_op : Operand<i64>;
+
let OperandType = "OPERAND_FUNCTION32" in
def function32_op : Operand<i32>;
@@ -128,6 +146,10 @@ let OperandType = "OPERAND_P2ALIGN" in {
def P2Align : Operand<i32> {
let PrintMethod = "printWebAssemblyP2AlignOperand";
}
+
+let OperandType = "OPERAND_EVENT" in
+def event_op : Operand<i32>;
+
} // OperandType = "OPERAND_P2ALIGN"
let OperandType = "OPERAND_SIGNATURE" in {
@@ -142,6 +164,19 @@ def TypeIndex : Operand<i32>;
} // OperandNamespace = "WebAssembly"
//===----------------------------------------------------------------------===//
+// WebAssembly Register to Stack instruction mapping
+//===----------------------------------------------------------------------===//
+
+class StackRel;
+def getStackOpcode : InstrMapping {
+ let FilterClass = "StackRel";
+ let RowFields = ["BaseName"];
+ let ColFields = ["StackBased"];
+ let KeyCol = ["false"];
+ let ValueCols = [["true"]];
+}
+
+//===----------------------------------------------------------------------===//
// WebAssembly Instruction Format Definitions.
//===----------------------------------------------------------------------===//
@@ -151,74 +186,62 @@ include "WebAssemblyInstrFormats.td"
// Additional instructions.
//===----------------------------------------------------------------------===//
-multiclass ARGUMENT<WebAssemblyRegClass vt> {
- let hasSideEffects = 1, Uses = [ARGUMENTS], isCodeGenOnly = 1 in
- defm ARGUMENT_#vt : I<(outs vt:$res), (ins i32imm:$argno),
- (outs), (ins i32imm:$argno),
- [(set vt:$res, (WebAssemblyargument timm:$argno))]>;
+multiclass ARGUMENT<WebAssemblyRegClass reg, ValueType vt> {
+ let hasSideEffects = 1, isCodeGenOnly = 1,
+ Defs = []<Register>, Uses = [ARGUMENTS] in
+ defm ARGUMENT_#vt :
+ I<(outs reg:$res), (ins i32imm:$argno), (outs), (ins i32imm:$argno),
+ [(set (vt reg:$res), (WebAssemblyargument timm:$argno))]>;
}
-multiclass SIMD_ARGUMENT<ValueType vt> {
- let hasSideEffects = 1, Uses = [ARGUMENTS], isCodeGenOnly = 1 in
- defm ARGUMENT_#vt : SIMD_I<(outs V128:$res), (ins i32imm:$argno),
- (outs), (ins i32imm:$argno),
- [(set (vt V128:$res),
- (WebAssemblyargument timm:$argno))]>;
-}
-defm "": ARGUMENT<I32>;
-defm "": ARGUMENT<I64>;
-defm "": ARGUMENT<F32>;
-defm "": ARGUMENT<F64>;
-defm "": ARGUMENT<EXCEPT_REF>;
-defm "": SIMD_ARGUMENT<v16i8>;
-defm "": SIMD_ARGUMENT<v8i16>;
-defm "": SIMD_ARGUMENT<v4i32>;
-defm "": SIMD_ARGUMENT<v4f32>;
-
-let Defs = [ARGUMENTS] in {
+defm "": ARGUMENT<I32, i32>;
+defm "": ARGUMENT<I64, i64>;
+defm "": ARGUMENT<F32, f32>;
+defm "": ARGUMENT<F64, f64>;
+defm "": ARGUMENT<EXCEPT_REF, ExceptRef>;
-// get_local and set_local are not generated by instruction selection; they
+// local.get and local.set are not generated by instruction selection; they
// are implied by virtual register uses and defs.
multiclass LOCAL<WebAssemblyRegClass vt> {
let hasSideEffects = 0 in {
- // COPY is not an actual instruction in wasm, but since we allow get_local and
- // set_local to be implicit during most of codegen, we can have a COPY which
- // is actually a no-op because all the work is done in the implied get_local
- // and set_local. COPYs are eliminated (and replaced with
- // get_local/set_local) in the ExplicitLocals pass.
+ // COPY is not an actual instruction in wasm, but since we allow local.get and
+ // local.set to be implicit during most of codegen, we can have a COPY which
+ // is actually a no-op because all the work is done in the implied local.get
+ // and local.set. COPYs are eliminated (and replaced with
+ // local.get/local.set) in the ExplicitLocals pass.
let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
defm COPY_#vt : I<(outs vt:$res), (ins vt:$src), (outs), (ins), [],
- "copy_local\t$res, $src", "copy_local">;
+ "local.copy\t$res, $src", "local.copy">;
// TEE is similar to COPY, but writes two copies of its result. Typically
// this would be used to stackify one result and write the other result to a
// local.
let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
defm TEE_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), (outs), (ins), [],
- "tee_local\t$res, $also, $src", "tee_local">;
+ "local.tee\t$res, $also, $src", "local.tee">;
- // This is the actual get_local instruction in wasm. These are made explicit
+ // This is the actual local.get instruction in wasm. These are made explicit
// by the ExplicitLocals pass. It has mayLoad because it reads from a wasm
// local, which is a side effect not otherwise modeled in LLVM.
let mayLoad = 1, isAsCheapAsAMove = 1 in
- defm GET_LOCAL_#vt : I<(outs vt:$res), (ins local_op:$local),
+ defm LOCAL_GET_#vt : I<(outs vt:$res), (ins local_op:$local),
(outs), (ins local_op:$local), [],
- "get_local\t$res, $local", "get_local\t$local", 0x20>;
+ "local.get\t$res, $local", "local.get\t$local", 0x20>;
- // This is the actual set_local instruction in wasm. These are made explicit
+ // This is the actual local.set instruction in wasm. These are made explicit
// by the ExplicitLocals pass. It has mayStore because it writes to a wasm
// local, which is a side effect not otherwise modeled in LLVM.
let mayStore = 1, isAsCheapAsAMove = 1 in
- defm SET_LOCAL_#vt : I<(outs), (ins local_op:$local, vt:$src),
+ defm LOCAL_SET_#vt : I<(outs), (ins local_op:$local, vt:$src),
(outs), (ins local_op:$local), [],
- "set_local\t$local, $src", "set_local\t$local", 0x21>;
+ "local.set\t$local, $src", "local.set\t$local", 0x21>;
- // This is the actual tee_local instruction in wasm. TEEs are turned into
- // TEE_LOCALs by the ExplicitLocals pass. It has mayStore for the same reason
- // as SET_LOCAL.
+ // This is the actual local.tee instruction in wasm. TEEs are turned into
+ // LOCAL_TEEs by the ExplicitLocals pass. It has mayStore for the same reason
+ // as LOCAL_SET.
let mayStore = 1, isAsCheapAsAMove = 1 in
- defm TEE_LOCAL_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src),
+ defm LOCAL_TEE_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src),
(outs), (ins local_op:$local), [],
- "tee_local\t$res, $local, $src", "tee_local\t$local",
+ "local.tee\t$res, $local, $src", "local.tee\t$local",
0x22>;
// Unused values must be dropped in some contexts.
@@ -226,15 +249,15 @@ let hasSideEffects = 0 in {
"drop\t$src", "drop", 0x1a>;
let mayLoad = 1 in
- defm GET_GLOBAL_#vt : I<(outs vt:$res), (ins global_op:$local),
+ defm GLOBAL_GET_#vt : I<(outs vt:$res), (ins global_op:$local),
(outs), (ins global_op:$local), [],
- "get_global\t$res, $local", "get_global\t$local",
+ "global.get\t$res, $local", "global.get\t$local",
0x23>;
let mayStore = 1 in
- defm SET_GLOBAL_#vt : I<(outs), (ins global_op:$local, vt:$src),
+ defm GLOBAL_SET_#vt : I<(outs), (ins global_op:$local, vt:$src),
(outs), (ins global_op:$local), [],
- "set_global\t$local, $src", "set_global\t$local",
+ "global.set\t$local, $src", "global.set\t$local",
0x24>;
} // hasSideEffects = 0
@@ -265,12 +288,12 @@ defm CONST_F64 : I<(outs F64:$res), (ins f64imm_op:$imm),
"f64.const\t$res, $imm", "f64.const\t$imm", 0x44>;
} // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1
-} // Defs = [ARGUMENTS]
-
def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)),
(CONST_I32 tglobaladdr:$addr)>;
def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)),
(CONST_I32 texternalsym:$addr)>;
+def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>;
+def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>;
//===----------------------------------------------------------------------===//
// Additional sets of instructions.
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrInteger.td b/lib/Target/WebAssembly/WebAssemblyInstrInteger.td
index f9f21fd1d754..d5b63d643697 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrInteger.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrInteger.td
@@ -12,7 +12,38 @@
///
//===----------------------------------------------------------------------===//
-let Defs = [ARGUMENTS] in {
+multiclass UnaryInt<SDNode node, string name, bits<32> i32Inst,
+ bits<32> i64Inst> {
+ defm _I32 : I<(outs I32:$dst), (ins I32:$src), (outs), (ins),
+ [(set I32:$dst, (node I32:$src))],
+ !strconcat("i32.", !strconcat(name, "\t$dst, $src")),
+ !strconcat("i32.", name), i32Inst>;
+ defm _I64 : I<(outs I64:$dst), (ins I64:$src), (outs), (ins),
+ [(set I64:$dst, (node I64:$src))],
+ !strconcat("i64.", !strconcat(name, "\t$dst, $src")),
+ !strconcat("i64.", name), i64Inst>;
+}
+multiclass BinaryInt<SDNode node, string name, bits<32> i32Inst,
+ bits<32> i64Inst> {
+ defm _I32 : I<(outs I32:$dst), (ins I32:$lhs, I32:$rhs), (outs), (ins),
+ [(set I32:$dst, (node I32:$lhs, I32:$rhs))],
+ !strconcat("i32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("i32.", name), i32Inst>;
+ defm _I64 : I<(outs I64:$dst), (ins I64:$lhs, I64:$rhs), (outs), (ins),
+ [(set I64:$dst, (node I64:$lhs, I64:$rhs))],
+ !strconcat("i64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("i64.", name), i64Inst>;
+}
+multiclass ComparisonInt<CondCode cond, string name, bits<32> i32Inst, bits<32> i64Inst> {
+ defm _I32 : I<(outs I32:$dst), (ins I32:$lhs, I32:$rhs), (outs), (ins),
+ [(set I32:$dst, (setcc I32:$lhs, I32:$rhs, cond))],
+ !strconcat("i32.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("i32.", name), i32Inst>;
+ defm _I64 : I<(outs I32:$dst), (ins I64:$lhs, I64:$rhs), (outs), (ins),
+ [(set I32:$dst, (setcc I64:$lhs, I64:$rhs, cond))],
+ !strconcat("i64.", !strconcat(name, "\t$dst, $lhs, $rhs")),
+ !strconcat("i64.", name), i64Inst>;
+}
// The spaces after the names are for aesthetic purposes only, to make
// operands line up vertically after tab expansion.
@@ -63,16 +94,12 @@ defm EQZ_I64 : I<(outs I32:$dst), (ins I64:$src), (outs), (ins),
[(set I32:$dst, (setcc I64:$src, 0, SETEQ))],
"i64.eqz \t$dst, $src", "i64.eqz", 0x50>;
-} // Defs = [ARGUMENTS]
-
// Optimize away an explicit mask on a rotate count.
def : Pat<(rotl I32:$lhs, (and I32:$rhs, 31)), (ROTL_I32 I32:$lhs, I32:$rhs)>;
def : Pat<(rotr I32:$lhs, (and I32:$rhs, 31)), (ROTR_I32 I32:$lhs, I32:$rhs)>;
def : Pat<(rotl I64:$lhs, (and I64:$rhs, 63)), (ROTL_I64 I64:$lhs, I64:$rhs)>;
def : Pat<(rotr I64:$lhs, (and I64:$rhs, 63)), (ROTR_I64 I64:$lhs, I64:$rhs)>;
-let Defs = [ARGUMENTS] in {
-
defm SELECT_I32 : I<(outs I32:$dst), (ins I32:$lhs, I32:$rhs, I32:$cond),
(outs), (ins),
[(set I32:$dst, (select I32:$cond, I32:$lhs, I32:$rhs))],
@@ -82,8 +109,6 @@ defm SELECT_I64 : I<(outs I64:$dst), (ins I64:$lhs, I64:$rhs, I32:$cond),
[(set I64:$dst, (select I32:$cond, I64:$lhs, I64:$rhs))],
"i64.select\t$dst, $lhs, $rhs, $cond", "i64.select", 0x1b>;
-} // Defs = [ARGUMENTS]
-
// ISD::SELECT requires its operand to conform to getBooleanContents, but
// WebAssembly's select interprets any non-zero value as true, so we can fold
// a setne with 0 into a select.
@@ -97,3 +122,10 @@ def : Pat<(select (i32 (seteq I32:$cond, 0)), I32:$lhs, I32:$rhs),
(SELECT_I32 I32:$rhs, I32:$lhs, I32:$cond)>;
def : Pat<(select (i32 (seteq I32:$cond, 0)), I64:$lhs, I64:$rhs),
(SELECT_I64 I64:$rhs, I64:$lhs, I32:$cond)>;
+
+// The legalizer inserts an unnecessary `and 1` to make input conform
+// to getBooleanContents, which we can lower away.
+def : Pat<(select (i32 (and I32:$cond, 1)), I32:$lhs, I32:$rhs),
+ (SELECT_I32 I32:$lhs, I32:$rhs, I32:$cond)>;
+def : Pat<(select (i32 (and I32:$cond, 1)), I64:$lhs, I64:$rhs),
+ (SELECT_I64 I64:$lhs, I64:$rhs, I32:$cond)>;
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
index 8a49325af2bd..518f81c61dc4 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
@@ -33,10 +33,8 @@ def or_is_add : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{
if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N->getOperand(1)))
return CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());
- KnownBits Known0;
- CurDAG->computeKnownBits(N->getOperand(0), Known0, 0);
- KnownBits Known1;
- CurDAG->computeKnownBits(N->getOperand(1), Known1, 0);
+ KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);
+ KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);
return (~Known0.Zero & ~Known1.Zero) == 0;
}]>;
@@ -53,15 +51,14 @@ def regPlusGA : PatFrag<(ops node:$addr, node:$off),
// We don't need a regPlusES because external symbols never have constant
// offsets folded into them, so we can just use add.
-let Defs = [ARGUMENTS] in {
-
// Defines atomic and non-atomic loads, regular and extending.
multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode> {
+ let mayLoad = 1 in
defm "": I<(outs rc:$dst),
(ins P2Align:$p2align, offset32_op:$off, I32:$addr),
(outs), (ins P2Align:$p2align, offset32_op:$off),
[], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"),
- !strconcat(Name, "\t${off}, ${p2align}"), Opcode>;
+ !strconcat(Name, "\t${off}${p2align}"), Opcode>;
}
// Basic load.
@@ -72,8 +69,6 @@ defm LOAD_I64 : WebAssemblyLoad<I64, "i64.load", 0x29>;
defm LOAD_F32 : WebAssemblyLoad<F32, "f32.load", 0x2a>;
defm LOAD_F64 : WebAssemblyLoad<F64, "f64.load", 0x2b>;
-} // Defs = [ARGUMENTS]
-
// Select loads with no constant offset.
class LoadPatNoOffset<ValueType ty, PatFrag kind, NI inst> :
Pat<(ty (kind I32:$addr)), (inst 0, 0, I32:$addr)>;
@@ -143,8 +138,6 @@ def : LoadPatExternSymOffOnly<i64, load, LOAD_I64>;
def : LoadPatExternSymOffOnly<f32, load, LOAD_F32>;
def : LoadPatExternSymOffOnly<f64, load, LOAD_F64>;
-let Defs = [ARGUMENTS] in {
-
// Extending load.
defm LOAD8_S_I32 : WebAssemblyLoad<I32, "i32.load8_s", 0x2c>;
defm LOAD8_U_I32 : WebAssemblyLoad<I32, "i32.load8_u", 0x2d>;
@@ -157,8 +150,6 @@ defm LOAD16_U_I64 : WebAssemblyLoad<I64, "i64.load16_u", 0x33>;
defm LOAD32_S_I64 : WebAssemblyLoad<I64, "i64.load32_s", 0x34>;
defm LOAD32_U_I64 : WebAssemblyLoad<I64, "i64.load32_u", 0x35>;
-} // Defs = [ARGUMENTS]
-
// Select extending loads with no constant offset.
def : LoadPatNoOffset<i32, sextloadi8, LOAD8_S_I32>;
def : LoadPatNoOffset<i32, zextloadi8, LOAD8_U_I32>;
@@ -302,17 +293,15 @@ def : LoadPatExternSymOffOnly<i64, extloadi8, LOAD8_U_I64>;
def : LoadPatExternSymOffOnly<i64, extloadi16, LOAD16_U_I64>;
def : LoadPatExternSymOffOnly<i64, extloadi32, LOAD32_U_I64>;
-
-let Defs = [ARGUMENTS] in {
-
// Defines atomic and non-atomic stores, regular and truncating
multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode> {
+ let mayStore = 1 in
defm "" : I<(outs),
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
(outs),
(ins P2Align:$p2align, offset32_op:$off), [],
!strconcat(Name, "\t${off}(${addr})${p2align}, $val"),
- !strconcat(Name, "\t${off}, ${p2align}"), Opcode>;
+ !strconcat(Name, "\t${off}${p2align}"), Opcode>;
}
// Basic store.
// Note: WebAssembly inverts SelectionDAG's usual operand order.
@@ -321,8 +310,6 @@ defm STORE_I64 : WebAssemblyStore<I64, "i64.store", 0x37>;
defm STORE_F32 : WebAssemblyStore<F32, "f32.store", 0x38>;
defm STORE_F64 : WebAssemblyStore<F64, "f64.store", 0x39>;
-} // Defs = [ARGUMENTS]
-
// Select stores with no constant offset.
class StorePatNoOffset<ValueType ty, PatFrag node, NI inst> :
Pat<(node ty:$val, I32:$addr), (inst 0, 0, I32:$addr, ty:$val)>;
@@ -387,9 +374,6 @@ def : StorePatExternSymOffOnly<i64, store, STORE_I64>;
def : StorePatExternSymOffOnly<f32, store, STORE_F32>;
def : StorePatExternSymOffOnly<f64, store, STORE_F64>;
-
-let Defs = [ARGUMENTS] in {
-
// Truncating store.
defm STORE8_I32 : WebAssemblyStore<I32, "i32.store8", 0x3a>;
defm STORE16_I32 : WebAssemblyStore<I32, "i32.store16", 0x3b>;
@@ -397,8 +381,6 @@ defm STORE8_I64 : WebAssemblyStore<I64, "i64.store8", 0x3c>;
defm STORE16_I64 : WebAssemblyStore<I64, "i64.store16", 0x3d>;
defm STORE32_I64 : WebAssemblyStore<I64, "i64.store32", 0x3e>;
-} // Defs = [ARGUMENTS]
-
// Select truncating stores with no constant offset.
def : StorePatNoOffset<i32, truncstorei8, STORE8_I32>;
def : StorePatNoOffset<i32, truncstorei16, STORE16_I32>;
@@ -446,8 +428,6 @@ def : StorePatExternSymOffOnly<i64, truncstorei8, STORE8_I64>;
def : StorePatExternSymOffOnly<i64, truncstorei16, STORE16_I64>;
def : StorePatExternSymOffOnly<i64, truncstorei32, STORE32_I64>;
-let Defs = [ARGUMENTS] in {
-
// Current memory size.
defm MEMORY_SIZE_I32 : I<(outs I32:$dst), (ins i32imm:$flags),
(outs), (ins i32imm:$flags),
@@ -456,44 +436,13 @@ defm MEMORY_SIZE_I32 : I<(outs I32:$dst), (ins i32imm:$flags),
"memory.size\t$dst, $flags", "memory.size\t$flags",
0x3f>,
Requires<[HasAddr32]>;
-defm MEM_SIZE_I32 : I<(outs I32:$dst), (ins i32imm:$flags),
- (outs), (ins i32imm:$flags),
- [(set I32:$dst, (int_wasm_mem_size (i32 imm:$flags)))],
- "mem.size\t$dst, $flags", "mem.size\t$flags", 0x3f>,
- Requires<[HasAddr32]>;
-defm CURRENT_MEMORY_I32 : I<(outs I32:$dst), (ins i32imm:$flags),
- (outs), (ins i32imm:$flags),
- [],
- "current_memory\t$dst",
- "current_memory\t$flags", 0x3f>,
- Requires<[HasAddr32]>;
// Grow memory.
defm MEMORY_GROW_I32 : I<(outs I32:$dst), (ins i32imm:$flags, I32:$delta),
- (outs), (ins i32imm:$flags, I32:$delta),
+ (outs), (ins i32imm:$flags),
[(set I32:$dst,
(int_wasm_memory_grow (i32 imm:$flags),
I32:$delta))],
"memory.grow\t$dst, $flags, $delta",
- "memory.grow\t$flags, $delta", 0x3f>,
+ "memory.grow\t$flags", 0x40>,
Requires<[HasAddr32]>;
-defm MEM_GROW_I32 : I<(outs I32:$dst), (ins i32imm:$flags, I32:$delta),
- (outs), (ins i32imm:$flags),
- [(set I32:$dst,
- (int_wasm_mem_grow (i32 imm:$flags), I32:$delta))],
- "mem.grow\t$dst, $flags, $delta", "mem.grow\t$flags",
- 0x3f>,
- Requires<[HasAddr32]>;
-defm GROW_MEMORY_I32 : I<(outs I32:$dst), (ins i32imm:$flags, I32:$delta),
- (outs), (ins i32imm:$flags),
- [],
- "grow_memory\t$dst, $delta", "grow_memory\t$flags",
- 0x40>,
- Requires<[HasAddr32]>;
-
-} // Defs = [ARGUMENTS]
-
-def : Pat<(int_wasm_current_memory),
- (CURRENT_MEMORY_I32 0)>;
-def : Pat<(int_wasm_grow_memory I32:$delta),
- (GROW_MEMORY_I32 0, $delta)>;
diff --git a/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
index 7d1edccdeb3c..587515c5b299 100644
--- a/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
+++ b/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td
@@ -12,8 +12,796 @@
///
//===----------------------------------------------------------------------===//
+// Instructions requiring HasSIMD128 and the simd128 prefix byte
+multiclass SIMD_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
+ list<dag> pattern_r, string asmstr_r = "",
+ string asmstr_s = "", bits<32> simdop = -1> {
+ defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
+ !or(0xfd00, !and(0xff, simdop))>,
+ Requires<[HasSIMD128]>;
+}
+
+defm "" : ARGUMENT<V128, v16i8>;
+defm "" : ARGUMENT<V128, v8i16>;
+defm "" : ARGUMENT<V128, v4i32>;
+defm "" : ARGUMENT<V128, v2i64>;
+defm "" : ARGUMENT<V128, v4f32>;
+defm "" : ARGUMENT<V128, v2f64>;
+
+// Constrained immediate argument types
+foreach SIZE = [8, 16] in
+def ImmI#SIZE : ImmLeaf<i32,
+ "return ((uint64_t)Imm & ((1UL << "#SIZE#") - 1)) == (uint64_t)Imm;"
+>;
+foreach SIZE = [2, 4, 8, 16, 32] in
+def LaneIdx#SIZE : ImmLeaf<i32, "return 0 <= Imm && Imm < "#SIZE#";">;
+
+//===----------------------------------------------------------------------===//
+// Load and store
+//===----------------------------------------------------------------------===//
+
+// Load: v128.load
+multiclass SIMDLoad<ValueType vec_t> {
+ let mayLoad = 1 in
+ defm LOAD_#vec_t :
+ SIMD_I<(outs V128:$dst), (ins P2Align:$align, offset32_op:$off, I32:$addr),
+ (outs), (ins P2Align:$align, offset32_op:$off), [],
+ "v128.load\t$dst, ${off}(${addr})$align",
+ "v128.load\t$off$align", 0>;
+}
+
+foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in {
+defm "" : SIMDLoad<vec_t>;
+
+// Def load and store patterns from WebAssemblyInstrMemory.td for vector types
+def : LoadPatNoOffset<vec_t, load, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatImmOff<vec_t, load, regPlusImm, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatImmOff<vec_t, load, or_is_add, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatGlobalAddr<vec_t, load, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatExternalSym<vec_t, load, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatOffsetOnly<vec_t, load, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatGlobalAddrOffOnly<vec_t, load, !cast<NI>("LOAD_"#vec_t)>;
+def : LoadPatExternSymOffOnly<vec_t, load, !cast<NI>("LOAD_"#vec_t)>;
+}
+
+// Store: v128.store
+multiclass SIMDStore<ValueType vec_t> {
+ let mayStore = 1 in
+ defm STORE_#vec_t :
+ SIMD_I<(outs), (ins P2Align:$align, offset32_op:$off, I32:$addr, V128:$vec),
+ (outs), (ins P2Align:$align, offset32_op:$off), [],
+ "v128.store\t${off}(${addr})$align, $vec",
+ "v128.store\t$off$align", 1>;
+}
+
+foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in {
+defm "" : SIMDStore<vec_t>;
+
+// Def load and store patterns from WebAssemblyInstrMemory.td for vector types
+def : StorePatNoOffset<vec_t, store, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatImmOff<vec_t, store, regPlusImm, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatImmOff<vec_t, store, or_is_add, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatGlobalAddr<vec_t, store, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatExternalSym<vec_t, store, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatOffsetOnly<vec_t, store, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatGlobalAddrOffOnly<vec_t, store, !cast<NI>("STORE_"#vec_t)>;
+def : StorePatExternSymOffOnly<vec_t, store, !cast<NI>("STORE_"#vec_t)>;
+}
+
+//===----------------------------------------------------------------------===//
+// Constructing SIMD values
+//===----------------------------------------------------------------------===//
+
+// Constant: v128.const
+multiclass ConstVec<ValueType vec_t, dag ops, dag pat, string args> {
+ let isMoveImm = 1, isReMaterializable = 1,
+ Predicates = [HasSIMD128, HasUnimplementedSIMD128] in
+ defm CONST_V128_#vec_t : SIMD_I<(outs V128:$dst), ops, (outs), ops,
+ [(set V128:$dst, (vec_t pat))],
+ "v128.const\t$dst, "#args,
+ "v128.const\t"#args, 2>;
+}
+
+defm "" : ConstVec<v16i8,
+ (ins vec_i8imm_op:$i0, vec_i8imm_op:$i1,
+ vec_i8imm_op:$i2, vec_i8imm_op:$i3,
+ vec_i8imm_op:$i4, vec_i8imm_op:$i5,
+ vec_i8imm_op:$i6, vec_i8imm_op:$i7,
+ vec_i8imm_op:$i8, vec_i8imm_op:$i9,
+ vec_i8imm_op:$iA, vec_i8imm_op:$iB,
+ vec_i8imm_op:$iC, vec_i8imm_op:$iD,
+ vec_i8imm_op:$iE, vec_i8imm_op:$iF),
+ (build_vector ImmI8:$i0, ImmI8:$i1, ImmI8:$i2, ImmI8:$i3,
+ ImmI8:$i4, ImmI8:$i5, ImmI8:$i6, ImmI8:$i7,
+ ImmI8:$i8, ImmI8:$i9, ImmI8:$iA, ImmI8:$iB,
+ ImmI8:$iC, ImmI8:$iD, ImmI8:$iE, ImmI8:$iF),
+ !strconcat("$i0, $i1, $i2, $i3, $i4, $i5, $i6, $i7, ",
+ "$i8, $i9, $iA, $iB, $iC, $iD, $iE, $iF")>;
+defm "" : ConstVec<v8i16,
+ (ins vec_i16imm_op:$i0, vec_i16imm_op:$i1,
+ vec_i16imm_op:$i2, vec_i16imm_op:$i3,
+ vec_i16imm_op:$i4, vec_i16imm_op:$i5,
+ vec_i16imm_op:$i6, vec_i16imm_op:$i7),
+ (build_vector
+ ImmI16:$i0, ImmI16:$i1, ImmI16:$i2, ImmI16:$i3,
+ ImmI16:$i4, ImmI16:$i5, ImmI16:$i6, ImmI16:$i7),
+ "$i0, $i1, $i2, $i3, $i4, $i5, $i6, $i7">;
+defm "" : ConstVec<v4i32,
+ (ins vec_i32imm_op:$i0, vec_i32imm_op:$i1,
+ vec_i32imm_op:$i2, vec_i32imm_op:$i3),
+ (build_vector (i32 imm:$i0), (i32 imm:$i1),
+ (i32 imm:$i2), (i32 imm:$i3)),
+ "$i0, $i1, $i2, $i3">;
+defm "" : ConstVec<v2i64,
+ (ins vec_i64imm_op:$i0, vec_i64imm_op:$i1),
+ (build_vector (i64 imm:$i0), (i64 imm:$i1)),
+ "$i0, $i1">;
+defm "" : ConstVec<v4f32,
+ (ins f32imm_op:$i0, f32imm_op:$i1,
+ f32imm_op:$i2, f32imm_op:$i3),
+ (build_vector (f32 fpimm:$i0), (f32 fpimm:$i1),
+ (f32 fpimm:$i2), (f32 fpimm:$i3)),
+ "$i0, $i1, $i2, $i3">;
+defm "" : ConstVec<v2f64,
+ (ins f64imm_op:$i0, f64imm_op:$i1),
+ (build_vector (f64 fpimm:$i0), (f64 fpimm:$i1)),
+ "$i0, $i1">;
+
+// Shuffle lanes: shuffle
+defm SHUFFLE :
+ SIMD_I<(outs V128:$dst),
+ (ins V128:$x, V128:$y,
+ vec_i8imm_op:$m0, vec_i8imm_op:$m1,
+ vec_i8imm_op:$m2, vec_i8imm_op:$m3,
+ vec_i8imm_op:$m4, vec_i8imm_op:$m5,
+ vec_i8imm_op:$m6, vec_i8imm_op:$m7,
+ vec_i8imm_op:$m8, vec_i8imm_op:$m9,
+ vec_i8imm_op:$mA, vec_i8imm_op:$mB,
+ vec_i8imm_op:$mC, vec_i8imm_op:$mD,
+ vec_i8imm_op:$mE, vec_i8imm_op:$mF),
+ (outs),
+ (ins
+ vec_i8imm_op:$m0, vec_i8imm_op:$m1,
+ vec_i8imm_op:$m2, vec_i8imm_op:$m3,
+ vec_i8imm_op:$m4, vec_i8imm_op:$m5,
+ vec_i8imm_op:$m6, vec_i8imm_op:$m7,
+ vec_i8imm_op:$m8, vec_i8imm_op:$m9,
+ vec_i8imm_op:$mA, vec_i8imm_op:$mB,
+ vec_i8imm_op:$mC, vec_i8imm_op:$mD,
+ vec_i8imm_op:$mE, vec_i8imm_op:$mF),
+ [],
+ "v8x16.shuffle\t$dst, $x, $y, "#
+ "$m0, $m1, $m2, $m3, $m4, $m5, $m6, $m7, "#
+ "$m8, $m9, $mA, $mB, $mC, $mD, $mE, $mF",
+ "v8x16.shuffle\t"#
+ "$m0, $m1, $m2, $m3, $m4, $m5, $m6, $m7, "#
+ "$m8, $m9, $mA, $mB, $mC, $mD, $mE, $mF",
+ 3>;
+
+// Shuffles after custom lowering
+def wasm_shuffle_t : SDTypeProfile<1, 18, []>;
+def wasm_shuffle : SDNode<"WebAssemblyISD::SHUFFLE", wasm_shuffle_t>;
+foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in {
+def : Pat<(vec_t (wasm_shuffle (vec_t V128:$x), (vec_t V128:$y),
+ (i32 LaneIdx32:$m0), (i32 LaneIdx32:$m1),
+ (i32 LaneIdx32:$m2), (i32 LaneIdx32:$m3),
+ (i32 LaneIdx32:$m4), (i32 LaneIdx32:$m5),
+ (i32 LaneIdx32:$m6), (i32 LaneIdx32:$m7),
+ (i32 LaneIdx32:$m8), (i32 LaneIdx32:$m9),
+ (i32 LaneIdx32:$mA), (i32 LaneIdx32:$mB),
+ (i32 LaneIdx32:$mC), (i32 LaneIdx32:$mD),
+ (i32 LaneIdx32:$mE), (i32 LaneIdx32:$mF))),
+ (vec_t (SHUFFLE (vec_t V128:$x), (vec_t V128:$y),
+ (i32 LaneIdx32:$m0), (i32 LaneIdx32:$m1),
+ (i32 LaneIdx32:$m2), (i32 LaneIdx32:$m3),
+ (i32 LaneIdx32:$m4), (i32 LaneIdx32:$m5),
+ (i32 LaneIdx32:$m6), (i32 LaneIdx32:$m7),
+ (i32 LaneIdx32:$m8), (i32 LaneIdx32:$m9),
+ (i32 LaneIdx32:$mA), (i32 LaneIdx32:$mB),
+ (i32 LaneIdx32:$mC), (i32 LaneIdx32:$mD),
+ (i32 LaneIdx32:$mE), (i32 LaneIdx32:$mF)))>;
+}
+
+// Create vector with identical lanes: splat
+def splat2 : PatFrag<(ops node:$x), (build_vector node:$x, node:$x)>;
+def splat4 : PatFrag<(ops node:$x), (build_vector
+ node:$x, node:$x, node:$x, node:$x)>;
+def splat8 : PatFrag<(ops node:$x), (build_vector
+ node:$x, node:$x, node:$x, node:$x,
+ node:$x, node:$x, node:$x, node:$x)>;
+def splat16 : PatFrag<(ops node:$x), (build_vector
+ node:$x, node:$x, node:$x, node:$x,
+ node:$x, node:$x, node:$x, node:$x,
+ node:$x, node:$x, node:$x, node:$x,
+ node:$x, node:$x, node:$x, node:$x)>;
+
+multiclass Splat<ValueType vec_t, string vec, WebAssemblyRegClass reg_t,
+ PatFrag splat_pat, bits<32> simdop> {
+ // Prefer splats over v128.const for const splats (65 is lowest that works)
+ let AddedComplexity = 65 in
+ defm SPLAT_#vec_t : SIMD_I<(outs V128:$dst), (ins reg_t:$x), (outs), (ins),
+ [(set (vec_t V128:$dst), (splat_pat reg_t:$x))],
+ vec#".splat\t$dst, $x", vec#".splat", simdop>;
+}
+
+defm "" : Splat<v16i8, "i8x16", I32, splat16, 4>;
+defm "" : Splat<v8i16, "i16x8", I32, splat8, 8>;
+defm "" : Splat<v4i32, "i32x4", I32, splat4, 12>;
+defm "" : Splat<v2i64, "i64x2", I64, splat2, 15>;
+defm "" : Splat<v4f32, "f32x4", F32, splat4, 18>;
+defm "" : Splat<v2f64, "f64x2", F64, splat2, 21>;
+
+//===----------------------------------------------------------------------===//
+// Accessing lanes
+//===----------------------------------------------------------------------===//
+
+// Extract lane as a scalar: extract_lane / extract_lane_s / extract_lane_u
+multiclass ExtractLane<ValueType vec_t, string vec, ImmLeaf imm_t,
+ WebAssemblyRegClass reg_t, bits<32> simdop,
+ string suffix = "", SDNode extract = vector_extract> {
+ defm EXTRACT_LANE_#vec_t#suffix :
+ SIMD_I<(outs reg_t:$dst), (ins V128:$vec, vec_i8imm_op:$idx),
+ (outs), (ins vec_i8imm_op:$idx),
+ [(set reg_t:$dst, (extract (vec_t V128:$vec), (i32 imm_t:$idx)))],
+ vec#".extract_lane"#suffix#"\t$dst, $vec, $idx",
+ vec#".extract_lane"#suffix#"\t$idx", simdop>;
+}
+
+multiclass ExtractPat<ValueType lane_t, int mask> {
+ def _s : PatFrag<(ops node:$vec, node:$idx),
+ (i32 (sext_inreg
+ (i32 (vector_extract
+ node:$vec,
+ node:$idx
+ )),
+ lane_t
+ ))>;
+ def _u : PatFrag<(ops node:$vec, node:$idx),
+ (i32 (and
+ (i32 (vector_extract
+ node:$vec,
+ node:$idx
+ )),
+ (i32 mask)
+ ))>;
+}
+
+defm extract_i8x16 : ExtractPat<i8, 0xff>;
+defm extract_i16x8 : ExtractPat<i16, 0xffff>;
+
+multiclass ExtractLaneExtended<string sign, bits<32> baseInst> {
+ defm "" : ExtractLane<v16i8, "i8x16", LaneIdx16, I32, baseInst, sign,
+ !cast<PatFrag>("extract_i8x16"#sign)>;
+ defm "" : ExtractLane<v8i16, "i16x8", LaneIdx8, I32, !add(baseInst, 4), sign,
+ !cast<PatFrag>("extract_i16x8"#sign)>;
+}
+
+defm "" : ExtractLaneExtended<"_s", 5>;
+let Predicates = [HasSIMD128, HasUnimplementedSIMD128] in
+defm "" : ExtractLaneExtended<"_u", 6>;
+defm "" : ExtractLane<v4i32, "i32x4", LaneIdx4, I32, 13>;
+defm "" : ExtractLane<v2i64, "i64x2", LaneIdx2, I64, 16>;
+defm "" : ExtractLane<v4f32, "f32x4", LaneIdx4, F32, 19>;
+defm "" : ExtractLane<v2f64, "f64x2", LaneIdx2, F64, 22>;
+
+// It would be more conventional to use unsigned extracts, but v8
+// doesn't implement them yet
+def : Pat<(i32 (vector_extract (v16i8 V128:$vec), (i32 LaneIdx16:$idx))),
+ (EXTRACT_LANE_v16i8_s V128:$vec, (i32 LaneIdx16:$idx))>;
+def : Pat<(i32 (vector_extract (v8i16 V128:$vec), (i32 LaneIdx8:$idx))),
+ (EXTRACT_LANE_v8i16_s V128:$vec, (i32 LaneIdx8:$idx))>;
+
+// Lower undef lane indices to zero
+def : Pat<(and (i32 (vector_extract (v16i8 V128:$vec), undef)), (i32 0xff)),
+ (EXTRACT_LANE_v16i8_u V128:$vec, 0)>;
+def : Pat<(and (i32 (vector_extract (v8i16 V128:$vec), undef)), (i32 0xffff)),
+ (EXTRACT_LANE_v8i16_u V128:$vec, 0)>;
+def : Pat<(i32 (vector_extract (v16i8 V128:$vec), undef)),
+ (EXTRACT_LANE_v16i8_u V128:$vec, 0)>;
+def : Pat<(i32 (vector_extract (v8i16 V128:$vec), undef)),
+ (EXTRACT_LANE_v8i16_u V128:$vec, 0)>;
+def : Pat<(sext_inreg (i32 (vector_extract (v16i8 V128:$vec), undef)), i8),
+ (EXTRACT_LANE_v16i8_s V128:$vec, 0)>;
+def : Pat<(sext_inreg (i32 (vector_extract (v8i16 V128:$vec), undef)), i16),
+ (EXTRACT_LANE_v8i16_s V128:$vec, 0)>;
+def : Pat<(vector_extract (v4i32 V128:$vec), undef),
+ (EXTRACT_LANE_v4i32 V128:$vec, 0)>;
+def : Pat<(vector_extract (v2i64 V128:$vec), undef),
+ (EXTRACT_LANE_v2i64 V128:$vec, 0)>;
+def : Pat<(vector_extract (v4f32 V128:$vec), undef),
+ (EXTRACT_LANE_v4f32 V128:$vec, 0)>;
+def : Pat<(vector_extract (v2f64 V128:$vec), undef),
+ (EXTRACT_LANE_v2f64 V128:$vec, 0)>;
+
+// Replace lane value: replace_lane
+multiclass ReplaceLane<ValueType vec_t, string vec, ImmLeaf imm_t,
+ WebAssemblyRegClass reg_t, ValueType lane_t,
+ bits<32> simdop> {
+ defm REPLACE_LANE_#vec_t :
+ SIMD_I<(outs V128:$dst), (ins V128:$vec, vec_i8imm_op:$idx, reg_t:$x),
+ (outs), (ins vec_i8imm_op:$idx),
+ [(set V128:$dst, (vector_insert
+ (vec_t V128:$vec), (lane_t reg_t:$x), (i32 imm_t:$idx)))],
+ vec#".replace_lane\t$dst, $vec, $idx, $x",
+ vec#".replace_lane\t$idx", simdop>;
+}
+
+defm "" : ReplaceLane<v16i8, "i8x16", LaneIdx16, I32, i32, 7>;
+defm "" : ReplaceLane<v8i16, "i16x8", LaneIdx8, I32, i32, 11>;
+defm "" : ReplaceLane<v4i32, "i32x4", LaneIdx4, I32, i32, 14>;
+defm "" : ReplaceLane<v2i64, "i64x2", LaneIdx2, I64, i64, 17>;
+defm "" : ReplaceLane<v4f32, "f32x4", LaneIdx4, F32, f32, 20>;
+defm "" : ReplaceLane<v2f64, "f64x2", LaneIdx2, F64, f64, 23>;
+
+// Lower undef lane indices to zero
+def : Pat<(vector_insert (v16i8 V128:$vec), I32:$x, undef),
+ (REPLACE_LANE_v16i8 V128:$vec, 0, I32:$x)>;
+def : Pat<(vector_insert (v8i16 V128:$vec), I32:$x, undef),
+ (REPLACE_LANE_v8i16 V128:$vec, 0, I32:$x)>;
+def : Pat<(vector_insert (v4i32 V128:$vec), I32:$x, undef),
+ (REPLACE_LANE_v4i32 V128:$vec, 0, I32:$x)>;
+def : Pat<(vector_insert (v2i64 V128:$vec), I64:$x, undef),
+ (REPLACE_LANE_v2i64 V128:$vec, 0, I64:$x)>;
+def : Pat<(vector_insert (v4f32 V128:$vec), F32:$x, undef),
+ (REPLACE_LANE_v4f32 V128:$vec, 0, F32:$x)>;
+def : Pat<(vector_insert (v2f64 V128:$vec), F64:$x, undef),
+ (REPLACE_LANE_v2f64 V128:$vec, 0, F64:$x)>;
+
+// Arbitrary other BUILD_VECTOR patterns
+def : Pat<(v16i8 (build_vector
+ (i32 I32:$x0), (i32 I32:$x1), (i32 I32:$x2), (i32 I32:$x3),
+ (i32 I32:$x4), (i32 I32:$x5), (i32 I32:$x6), (i32 I32:$x7),
+ (i32 I32:$x8), (i32 I32:$x9), (i32 I32:$x10), (i32 I32:$x11),
+ (i32 I32:$x12), (i32 I32:$x13), (i32 I32:$x14), (i32 I32:$x15)
+ )),
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (REPLACE_LANE_v16i8
+ (v16i8 (SPLAT_v16i8 (i32 I32:$x0))),
+ 1, I32:$x1
+ )),
+ 2, I32:$x2
+ )),
+ 3, I32:$x3
+ )),
+ 4, I32:$x4
+ )),
+ 5, I32:$x5
+ )),
+ 6, I32:$x6
+ )),
+ 7, I32:$x7
+ )),
+ 8, I32:$x8
+ )),
+ 9, I32:$x9
+ )),
+ 10, I32:$x10
+ )),
+ 11, I32:$x11
+ )),
+ 12, I32:$x12
+ )),
+ 13, I32:$x13
+ )),
+ 14, I32:$x14
+ )),
+ 15, I32:$x15
+ ))>;
+def : Pat<(v8i16 (build_vector
+ (i32 I32:$x0), (i32 I32:$x1), (i32 I32:$x2), (i32 I32:$x3),
+ (i32 I32:$x4), (i32 I32:$x5), (i32 I32:$x6), (i32 I32:$x7)
+ )),
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (REPLACE_LANE_v8i16
+ (v8i16 (SPLAT_v8i16 (i32 I32:$x0))),
+ 1, I32:$x1
+ )),
+ 2, I32:$x2
+ )),
+ 3, I32:$x3
+ )),
+ 4, I32:$x4
+ )),
+ 5, I32:$x5
+ )),
+ 6, I32:$x6
+ )),
+ 7, I32:$x7
+ ))>;
+def : Pat<(v4i32 (build_vector
+ (i32 I32:$x0), (i32 I32:$x1), (i32 I32:$x2), (i32 I32:$x3)
+ )),
+ (v4i32 (REPLACE_LANE_v4i32
+ (v4i32 (REPLACE_LANE_v4i32
+ (v4i32 (REPLACE_LANE_v4i32
+ (v4i32 (SPLAT_v4i32 (i32 I32:$x0))),
+ 1, I32:$x1
+ )),
+ 2, I32:$x2
+ )),
+ 3, I32:$x3
+ ))>;
+def : Pat<(v2i64 (build_vector (i64 I64:$x0), (i64 I64:$x1))),
+ (v2i64 (REPLACE_LANE_v2i64
+ (v2i64 (SPLAT_v2i64 (i64 I64:$x0))), 1, I64:$x1))>;
+def : Pat<(v4f32 (build_vector
+ (f32 F32:$x0), (f32 F32:$x1), (f32 F32:$x2), (f32 F32:$x3)
+ )),
+ (v4f32 (REPLACE_LANE_v4f32
+ (v4f32 (REPLACE_LANE_v4f32
+ (v4f32 (REPLACE_LANE_v4f32
+ (v4f32 (SPLAT_v4f32 (f32 F32:$x0))),
+ 1, F32:$x1
+ )),
+ 2, F32:$x2
+ )),
+ 3, F32:$x3
+ ))>;
+def : Pat<(v2f64 (build_vector (f64 F64:$x0), (f64 F64:$x1))),
+ (v2f64 (REPLACE_LANE_v2f64
+ (v2f64 (SPLAT_v2f64 (f64 F64:$x0))), 1, F64:$x1))>;
+
+//===----------------------------------------------------------------------===//
+// Comparisons
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDCondition<ValueType vec_t, ValueType out_t, string vec,
+ string name, CondCode cond, bits<32> simdop> {
+ defm _#vec_t :
+ SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs), (outs), (ins),
+ [(set (out_t V128:$dst),
+ (setcc (vec_t V128:$lhs), (vec_t V128:$rhs), cond)
+ )],
+ vec#"."#name#"\t$dst, $lhs, $rhs", vec#"."#name, simdop>;
+}
+
+multiclass SIMDConditionInt<string name, CondCode cond, bits<32> baseInst> {
+ defm "" : SIMDCondition<v16i8, v16i8, "i8x16", name, cond, baseInst>;
+ defm "" : SIMDCondition<v8i16, v8i16, "i16x8", name, cond,
+ !add(baseInst, 10)>;
+ defm "" : SIMDCondition<v4i32, v4i32, "i32x4", name, cond,
+ !add(baseInst, 20)>;
+}
+
+multiclass SIMDConditionFP<string name, CondCode cond, bits<32> baseInst> {
+ defm "" : SIMDCondition<v4f32, v4i32, "f32x4", name, cond, baseInst>;
+ defm "" : SIMDCondition<v2f64, v2i64, "f64x2", name, cond,
+ !add(baseInst, 6)>;
+}
+
+// Equality: eq
+let isCommutable = 1 in {
+defm EQ : SIMDConditionInt<"eq", SETEQ, 24>;
+defm EQ : SIMDConditionFP<"eq", SETOEQ, 64>;
+} // isCommutable = 1
+
+// Non-equality: ne
let isCommutable = 1 in {
-defm ADD : SIMDBinary<add, fadd, "add ">;
-defm MUL: SIMDBinary<mul, fmul, "mul ">;
+defm NE : SIMDConditionInt<"ne", SETNE, 25>;
+defm NE : SIMDConditionFP<"ne", SETUNE, 65>;
} // isCommutable = 1
-defm SUB: SIMDBinary<sub, fsub, "sub ">;
+
+// Less than: lt_s / lt_u / lt
+defm LT_S : SIMDConditionInt<"lt_s", SETLT, 26>;
+defm LT_U : SIMDConditionInt<"lt_u", SETULT, 27>;
+defm LT : SIMDConditionFP<"lt", SETOLT, 66>;
+
+// Greater than: gt_s / gt_u / gt
+defm GT_S : SIMDConditionInt<"gt_s", SETGT, 28>;
+defm GT_U : SIMDConditionInt<"gt_u", SETUGT, 29>;
+defm GT : SIMDConditionFP<"gt", SETOGT, 67>;
+
+// Less than or equal: le_s / le_u / le
+defm LE_S : SIMDConditionInt<"le_s", SETLE, 30>;
+defm LE_U : SIMDConditionInt<"le_u", SETULE, 31>;
+defm LE : SIMDConditionFP<"le", SETOLE, 68>;
+
+// Greater than or equal: ge_s / ge_u / ge
+defm GE_S : SIMDConditionInt<"ge_s", SETGE, 32>;
+defm GE_U : SIMDConditionInt<"ge_u", SETUGE, 33>;
+defm GE : SIMDConditionFP<"ge", SETOGE, 69>;
+
+// Lower float comparisons that don't care about NaN to standard WebAssembly
+// float comparisons. These instructions are generated in the target-independent
+// expansion of unordered comparisons and ordered ne.
+def : Pat<(v4i32 (seteq (v4f32 V128:$lhs), (v4f32 V128:$rhs))),
+ (v4i32 (EQ_v4f32 (v4f32 V128:$lhs), (v4f32 V128:$rhs)))>;
+def : Pat<(v4i32 (setne (v4f32 V128:$lhs), (v4f32 V128:$rhs))),
+ (v4i32 (NE_v4f32 (v4f32 V128:$lhs), (v4f32 V128:$rhs)))>;
+def : Pat<(v2i64 (seteq (v2f64 V128:$lhs), (v2f64 V128:$rhs))),
+ (v2i64 (EQ_v2f64 (v2f64 V128:$lhs), (v2f64 V128:$rhs)))>;
+def : Pat<(v2i64 (setne (v2f64 V128:$lhs), (v2f64 V128:$rhs))),
+ (v2i64 (NE_v2f64 (v2f64 V128:$lhs), (v2f64 V128:$rhs)))>;
+
+//===----------------------------------------------------------------------===//
+// Bitwise operations
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDBinary<ValueType vec_t, string vec, SDNode node, string name,
+ bits<32> simdop> {
+ defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$lhs, V128:$rhs),
+ (outs), (ins),
+ [(set (vec_t V128:$dst),
+ (node (vec_t V128:$lhs), (vec_t V128:$rhs))
+ )],
+ vec#"."#name#"\t$dst, $lhs, $rhs", vec#"."#name,
+ simdop>;
+}
+
+multiclass SIMDBitwise<SDNode node, string name, bits<32> simdop> {
+ defm "" : SIMDBinary<v16i8, "v128", node, name, simdop>;
+ defm "" : SIMDBinary<v8i16, "v128", node, name, simdop>;
+ defm "" : SIMDBinary<v4i32, "v128", node, name, simdop>;
+ defm "" : SIMDBinary<v2i64, "v128", node, name, simdop>;
+}
+
+multiclass SIMDUnary<ValueType vec_t, string vec, SDNode node, string name,
+ bits<32> simdop> {
+ defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$vec), (outs), (ins),
+ [(set (vec_t V128:$dst),
+ (vec_t (node (vec_t V128:$vec)))
+ )],
+ vec#"."#name#"\t$dst, $vec", vec#"."#name, simdop>;
+}
+
+// Bitwise logic: v128.not
+foreach vec_t = [v16i8, v8i16, v4i32, v2i64] in
+defm NOT: SIMDUnary<vec_t, "v128", vnot, "not", 76>;
+
+// Bitwise logic: v128.and / v128.or / v128.xor
+let isCommutable = 1 in {
+defm AND : SIMDBitwise<and, "and", 77>;
+defm OR : SIMDBitwise<or, "or", 78>;
+defm XOR : SIMDBitwise<xor, "xor", 79>;
+} // isCommutable = 1
+
+// Bitwise select: v128.bitselect
+foreach vec_t = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in
+ defm BITSELECT_#vec_t :
+ SIMD_I<(outs V128:$dst), (ins V128:$v1, V128:$v2, V128:$c), (outs), (ins),
+ [(set (vec_t V128:$dst),
+ (vec_t (int_wasm_bitselect
+ (vec_t V128:$v1), (vec_t V128:$v2), (vec_t V128:$c)
+ ))
+ )],
+ "v128.bitselect\t$dst, $v1, $v2, $c", "v128.bitselect", 80>;
+
+// Bitselect is equivalent to (c & v1) | (~c & v2)
+foreach vec_t = [v16i8, v8i16, v4i32, v2i64] in
+ def : Pat<(vec_t (or (and (vec_t V128:$c), (vec_t V128:$v1)),
+ (and (vnot V128:$c), (vec_t V128:$v2)))),
+ (!cast<Instruction>("BITSELECT_"#vec_t)
+ V128:$v1, V128:$v2, V128:$c)>;
+
+//===----------------------------------------------------------------------===//
+// Integer unary arithmetic
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDUnaryInt<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDUnary<v16i8, "i8x16", node, name, baseInst>;
+ defm "" : SIMDUnary<v8i16, "i16x8", node, name, !add(baseInst, 17)>;
+ defm "" : SIMDUnary<v4i32, "i32x4", node, name, !add(baseInst, 34)>;
+ defm "" : SIMDUnary<v2i64, "i64x2", node, name, !add(baseInst, 51)>;
+}
+
+multiclass SIMDReduceVec<ValueType vec_t, string vec, SDNode op, string name,
+ bits<32> simdop> {
+ defm _#vec_t : SIMD_I<(outs I32:$dst), (ins V128:$vec), (outs), (ins),
+ [(set I32:$dst, (i32 (op (vec_t V128:$vec))))],
+ vec#"."#name#"\t$dst, $vec", vec#"."#name, simdop>;
+}
+
+multiclass SIMDReduce<SDNode op, string name, bits<32> baseInst> {
+ defm "" : SIMDReduceVec<v16i8, "i8x16", op, name, baseInst>;
+ defm "" : SIMDReduceVec<v8i16, "i16x8", op, name, !add(baseInst, 17)>;
+ defm "" : SIMDReduceVec<v4i32, "i32x4", op, name, !add(baseInst, 34)>;
+ defm "" : SIMDReduceVec<v2i64, "i64x2", op, name, !add(baseInst, 51)>;
+}
+
+// Integer vector negation
+def ivneg : PatFrag<(ops node:$in), (sub immAllZerosV, node:$in)>;
+
+// Integer negation: neg
+defm NEG : SIMDUnaryInt<ivneg, "neg", 81>;
+
+// Any lane true: any_true
+defm ANYTRUE : SIMDReduce<int_wasm_anytrue, "any_true", 82>;
+
+// All lanes true: all_true
+defm ALLTRUE : SIMDReduce<int_wasm_alltrue, "all_true", 83>;
+
+//===----------------------------------------------------------------------===//
+// Bit shifts
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDShift<ValueType vec_t, string vec, SDNode node, dag shift_vec,
+ string name, bits<32> simdop> {
+ defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$vec, I32:$x),
+ (outs), (ins),
+ [(set (vec_t V128:$dst),
+ (node V128:$vec, (vec_t shift_vec)))],
+ vec#"."#name#"\t$dst, $vec, $x", vec#"."#name, simdop>;
+}
+
+multiclass SIMDShiftInt<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDShift<v16i8, "i8x16", node, (splat16 I32:$x), name, baseInst>;
+ defm "" : SIMDShift<v8i16, "i16x8", node, (splat8 I32:$x), name,
+ !add(baseInst, 17)>;
+ defm "" : SIMDShift<v4i32, "i32x4", node, (splat4 I32:$x), name,
+ !add(baseInst, 34)>;
+ defm "" : SIMDShift<v2i64, "i64x2", node, (splat2 (i64 (zext I32:$x))),
+ name, !add(baseInst, 51)>;
+}
+
+// Left shift by scalar: shl
+defm SHL : SIMDShiftInt<shl, "shl", 84>;
+
+// Right shift by scalar: shr_s / shr_u
+defm SHR_S : SIMDShiftInt<sra, "shr_s", 85>;
+defm SHR_U : SIMDShiftInt<srl, "shr_u", 86>;
+
+// Truncate i64 shift operands to i32s
+foreach shifts = [[shl, SHL_v2i64], [sra, SHR_S_v2i64], [srl, SHR_U_v2i64]] in
+def : Pat<(v2i64 (shifts[0] (v2i64 V128:$vec), (v2i64 (splat2 I64:$x)))),
+ (v2i64 (shifts[1] (v2i64 V128:$vec), (I32_WRAP_I64 I64:$x)))>;
+
+// 2xi64 shifts with constant shift amounts are custom lowered to avoid wrapping
+def wasm_shift_t : SDTypeProfile<1, 2,
+ [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>]
+>;
+def wasm_shl : SDNode<"WebAssemblyISD::VEC_SHL", wasm_shift_t>;
+def wasm_shr_s : SDNode<"WebAssemblyISD::VEC_SHR_S", wasm_shift_t>;
+def wasm_shr_u : SDNode<"WebAssemblyISD::VEC_SHR_U", wasm_shift_t>;
+foreach shifts = [[wasm_shl, SHL_v2i64],
+ [wasm_shr_s, SHR_S_v2i64],
+ [wasm_shr_u, SHR_U_v2i64]] in
+def : Pat<(v2i64 (shifts[0] (v2i64 V128:$vec), I32:$x)),
+ (v2i64 (shifts[1] (v2i64 V128:$vec), I32:$x))>;
+
+//===----------------------------------------------------------------------===//
+// Integer binary arithmetic
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDBinaryIntSmall<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDBinary<v16i8, "i8x16", node, name, baseInst>;
+ defm "" : SIMDBinary<v8i16, "i16x8", node, name, !add(baseInst, 17)>;
+}
+
+multiclass SIMDBinaryIntNoI64x2<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDBinaryIntSmall<node, name, baseInst>;
+ defm "" : SIMDBinary<v4i32, "i32x4", node, name, !add(baseInst, 34)>;
+}
+
+multiclass SIMDBinaryInt<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDBinaryIntNoI64x2<node, name, baseInst>;
+ defm "" : SIMDBinary<v2i64, "i64x2", node, name, !add(baseInst, 51)>;
+}
+
+// Integer addition: add / add_saturate_s / add_saturate_u
+let isCommutable = 1 in {
+defm ADD : SIMDBinaryInt<add, "add", 87>;
+defm ADD_SAT_S : SIMDBinaryIntSmall<saddsat, "add_saturate_s", 88>;
+defm ADD_SAT_U : SIMDBinaryIntSmall<uaddsat, "add_saturate_u", 89>;
+} // isCommutable = 1
+
+// Integer subtraction: sub / sub_saturate_s / sub_saturate_u
+defm SUB : SIMDBinaryInt<sub, "sub", 90>;
+defm SUB_SAT_S :
+ SIMDBinaryIntSmall<int_wasm_sub_saturate_signed, "sub_saturate_s", 91>;
+defm SUB_SAT_U :
+ SIMDBinaryIntSmall<int_wasm_sub_saturate_unsigned, "sub_saturate_u", 92>;
+
+// Integer multiplication: mul
+defm MUL : SIMDBinaryIntNoI64x2<mul, "mul", 93>;
+
+//===----------------------------------------------------------------------===//
+// Floating-point unary arithmetic
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDUnaryFP<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDUnary<v4f32, "f32x4", node, name, baseInst>;
+ defm "" : SIMDUnary<v2f64, "f64x2", node, name, !add(baseInst, 11)>;
+}
+
+// Absolute value: abs
+defm ABS : SIMDUnaryFP<fabs, "abs", 149>;
+
+// Negation: neg
+defm NEG : SIMDUnaryFP<fneg, "neg", 150>;
+
+// Square root: sqrt
+let Predicates = [HasSIMD128, HasUnimplementedSIMD128] in
+defm SQRT : SIMDUnaryFP<fsqrt, "sqrt", 151>;
+
+//===----------------------------------------------------------------------===//
+// Floating-point binary arithmetic
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDBinaryFP<SDNode node, string name, bits<32> baseInst> {
+ defm "" : SIMDBinary<v4f32, "f32x4", node, name, baseInst>;
+ defm "" : SIMDBinary<v2f64, "f64x2", node, name, !add(baseInst, 11)>;
+}
+
+// Addition: add
+let isCommutable = 1 in
+defm ADD : SIMDBinaryFP<fadd, "add", 154>;
+
+// Subtraction: sub
+defm SUB : SIMDBinaryFP<fsub, "sub", 155>;
+
+// Multiplication: mul
+let isCommutable = 1 in
+defm MUL : SIMDBinaryFP<fmul, "mul", 156>;
+
+// Division: div
+let Predicates = [HasSIMD128, HasUnimplementedSIMD128] in
+defm DIV : SIMDBinaryFP<fdiv, "div", 157>;
+
+// NaN-propagating minimum: min
+defm MIN : SIMDBinaryFP<fminimum, "min", 158>;
+
+// NaN-propagating maximum: max
+defm MAX : SIMDBinaryFP<fmaximum, "max", 159>;
+
+//===----------------------------------------------------------------------===//
+// Conversions
+//===----------------------------------------------------------------------===//
+
+multiclass SIMDConvert<ValueType vec_t, ValueType arg_t, SDNode op,
+ string name, bits<32> simdop> {
+ defm op#_#vec_t#_#arg_t :
+ SIMD_I<(outs V128:$dst), (ins V128:$vec), (outs), (ins),
+ [(set (vec_t V128:$dst), (vec_t (op (arg_t V128:$vec))))],
+ name#"\t$dst, $vec", name, simdop>;
+}
+
+// Integer to floating point: convert
+defm "" : SIMDConvert<v4f32, v4i32, sint_to_fp, "f32x4.convert_i32x4_s", 175>;
+defm "" : SIMDConvert<v4f32, v4i32, uint_to_fp, "f32x4.convert_i32x4_u", 176>;
+defm "" : SIMDConvert<v2f64, v2i64, sint_to_fp, "f64x2.convert_i64x2_s", 177>;
+defm "" : SIMDConvert<v2f64, v2i64, uint_to_fp, "f64x2.convert_i64x2_u", 178>;
+
+// Floating point to integer with saturation: trunc_sat
+defm "" : SIMDConvert<v4i32, v4f32, fp_to_sint, "i32x4.trunc_sat_f32x4_s", 171>;
+defm "" : SIMDConvert<v4i32, v4f32, fp_to_uint, "i32x4.trunc_sat_f32x4_u", 172>;
+defm "" : SIMDConvert<v2i64, v2f64, fp_to_sint, "i64x2.trunc_sat_f64x2_s", 173>;
+defm "" : SIMDConvert<v2i64, v2f64, fp_to_uint, "i64x2.trunc_sat_f64x2_u", 174>;
+
+// Lower llvm.wasm.trunc.saturate.* to saturating instructions
+def : Pat<(v4i32 (int_wasm_trunc_saturate_signed (v4f32 V128:$src))),
+ (fp_to_sint_v4i32_v4f32 (v4f32 V128:$src))>;
+def : Pat<(v4i32 (int_wasm_trunc_saturate_unsigned (v4f32 V128:$src))),
+ (fp_to_uint_v4i32_v4f32 (v4f32 V128:$src))>;
+def : Pat<(v2i64 (int_wasm_trunc_saturate_signed (v2f64 V128:$src))),
+ (fp_to_sint_v2i64_v2f64 (v2f64 V128:$src))>;
+def : Pat<(v2i64 (int_wasm_trunc_saturate_unsigned (v2f64 V128:$src))),
+ (fp_to_uint_v2i64_v2f64 (v2f64 V128:$src))>;
+
+// Bitcasts are nops
+// Matching bitcast t1 to t1 causes strange errors, so avoid repeating types
+foreach t1 = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in
+foreach t2 = !foldl(
+ []<ValueType>, [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64],
+ acc, cur, !if(!eq(!cast<string>(t1), !cast<string>(cur)),
+ acc, !listconcat(acc, [cur])
+ )
+) in
+def : Pat<(t1 (bitconvert (t2 V128:$v))), (t1 V128:$v)>;
diff --git a/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
index e42dcbc0a8ac..ad838dfb574a 100644
--- a/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
@@ -31,6 +31,7 @@ class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
bool runOnMachineFunction(MachineFunction &MF) override;
+ bool removeUnnecessaryUnreachables(MachineFunction &MF);
bool replaceFuncletReturns(MachineFunction &MF);
bool hoistCatches(MachineFunction &MF);
bool addCatchAlls(MachineFunction &MF);
@@ -47,7 +48,7 @@ public:
char WebAssemblyLateEHPrepare::ID = 0;
INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
- "WebAssembly Exception Preparation", false, false)
+ "WebAssembly Late Exception Preparation", false, false)
FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
return new WebAssemblyLateEHPrepare();
@@ -59,7 +60,7 @@ FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
// possible search paths should be the same.
// Returns nullptr in case it does not find any EH pad in the search, or finds
// multiple different EH pads.
-MachineBasicBlock *GetMatchingEHPad(MachineInstr *MI) {
+static MachineBasicBlock *getMatchingEHPad(MachineInstr *MI) {
MachineFunction *MF = MI->getParent()->getParent();
SmallVector<MachineBasicBlock *, 2> WL;
SmallPtrSet<MachineBasicBlock *, 2> Visited;
@@ -83,29 +84,35 @@ MachineBasicBlock *GetMatchingEHPad(MachineInstr *MI) {
return EHPad;
}
-// Erases the given BB and all its children from the function. If other BBs have
-// this BB as a successor, the successor relationships will be deleted as well.
-static void EraseBBAndChildren(MachineBasicBlock *MBB) {
- SmallVector<MachineBasicBlock *, 8> WL;
- WL.push_back(MBB);
+// Erase the specified BBs if the BB does not have any remaining predecessors,
+// and also all its dead children.
+template <typename Container>
+static void eraseDeadBBsAndChildren(const Container &MBBs) {
+ SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
while (!WL.empty()) {
MachineBasicBlock *MBB = WL.pop_back_val();
- for (auto *Pred : MBB->predecessors())
- Pred->removeSuccessor(MBB);
- for (auto *Succ : MBB->successors()) {
- WL.push_back(Succ);
+ if (!MBB->pred_empty())
+ continue;
+ SmallVector<MachineBasicBlock *, 4> Succs(MBB->succ_begin(),
+ MBB->succ_end());
+ WL.append(MBB->succ_begin(), MBB->succ_end());
+ for (auto *Succ : Succs)
MBB->removeSuccessor(Succ);
- }
MBB->eraseFromParent();
}
}
bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
+ LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n"
+ "********** Function: "
+ << MF.getName() << '\n');
+
if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
ExceptionHandling::Wasm)
return false;
bool Changed = false;
+ Changed |= removeUnnecessaryUnreachables(MF);
Changed |= addRethrows(MF);
if (!MF.getFunction().hasPersonalityFn())
return Changed;
@@ -118,6 +125,31 @@ bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
return Changed;
}
+bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
+ MachineFunction &MF) {
+ bool Changed = false;
+ for (auto &MBB : MF) {
+ for (auto &MI : MBB) {
+ if (!WebAssembly::isThrow(MI))
+ continue;
+ Changed = true;
+
+ // The instruction after the throw should be an unreachable or a branch to
+ // another BB that should eventually lead to an unreachable. Delete it
+ // because throw itself is a terminator, and also delete successors if
+ // any.
+ MBB.erase(std::next(MachineBasicBlock::iterator(MI)), MBB.end());
+ SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(),
+ MBB.succ_end());
+ for (auto *Succ : Succs)
+ MBB.removeSuccessor(Succ);
+ eraseDeadBBsAndChildren(Succs);
+ }
+ }
+
+ return Changed;
+}
+
bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
bool Changed = false;
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
@@ -179,7 +211,7 @@ bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {
Catches.push_back(&MI);
for (auto *Catch : Catches) {
- MachineBasicBlock *EHPad = GetMatchingEHPad(Catch);
+ MachineBasicBlock *EHPad = getMatchingEHPad(Catch);
assert(EHPad && "No matching EH pad for catch");
if (EHPad->begin() == Catch)
continue;
@@ -238,14 +270,18 @@ bool WebAssemblyLateEHPrepare::addRethrows(MachineFunction &MF) {
Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
TII.get(WebAssembly::RETHROW_TO_CALLER));
- // Becasue __cxa_rethrow does not return, the instruction after the
+ // Because __cxa_rethrow does not return, the instruction after the
// rethrow should be an unreachable or a branch to another BB that should
// eventually lead to an unreachable. Delete it because rethrow itself is
// a terminator, and also delete non-EH pad successors if any.
MBB.erase(std::next(MachineBasicBlock::iterator(Rethrow)), MBB.end());
+ SmallVector<MachineBasicBlock *, 8> NonPadSuccessors;
for (auto *Succ : MBB.successors())
if (!Succ->isEHPad())
- EraseBBAndChildren(Succ);
+ NonPadSuccessors.push_back(Succ);
+ for (auto *Succ : NonPadSuccessors)
+ MBB.removeSuccessor(Succ);
+ eraseDeadBBsAndChildren(NonPadSuccessors);
}
return Changed;
}
@@ -255,7 +291,7 @@ bool WebAssemblyLateEHPrepare::addRethrows(MachineFunction &MF) {
// %exn = catch 0
// call @__clang_call_terminate(%exn)
// unreachable
-// (There can be set_local and get_locals before the call if we didn't run
+// (There can be local.set and local.gets before the call if we didn't run
// RegStackify)
// But code transformations can change or add more control flow, so the call to
// __clang_call_terminate() function may not be in the original EH pad anymore.
@@ -277,7 +313,7 @@ bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
bool Changed = false;
for (auto *Call : ClangCallTerminateCalls) {
- MachineBasicBlock *EHPad = GetMatchingEHPad(Call);
+ MachineBasicBlock *EHPad = getMatchingEHPad(Call);
assert(EHPad && "No matching EH pad for catch");
// If it is already the form we want, skip it
@@ -294,7 +330,7 @@ bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
// This runs after hoistCatches(), so catch instruction should be at the top
assert(WebAssembly::isCatch(*Catch));
// Takes the result register of the catch instruction as argument. There may
- // have been some other set_local/get_locals in between, but at this point
+ // have been some other local.set/local.gets in between, but at this point
// we don't care.
Call->getOperand(1).setReg(Catch->getOperand(0).getReg());
auto InsertPos = std::next(MachineBasicBlock::iterator(Catch));
@@ -302,8 +338,11 @@ bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
BuildMI(*EHPad, InsertPos, Call->getDebugLoc(),
TII.get(WebAssembly::UNREACHABLE));
EHPad->erase(InsertPos, EHPad->end());
- for (auto *Succ : EHPad->successors())
- EraseBBAndChildren(Succ);
+ SmallVector<MachineBasicBlock *, 8> Succs(EHPad->succ_begin(),
+ EHPad->succ_end());
+ for (auto *Succ : Succs)
+ EHPad->removeSuccessor(Succ);
+ eraseDeadBBsAndChildren(Succs);
}
return Changed;
}
diff --git a/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp b/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp
index 5fb97e38939a..c9a3527d3fbd 100644
--- a/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp
@@ -78,30 +78,102 @@ bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) {
MachineInstr *Def = MRI.getVRegDef(Cond);
switch (Def->getOpcode()) {
using namespace WebAssembly;
- case EQ_I32: Def->setDesc(TII.get(NE_I32)); Inverted = true; break;
- case NE_I32: Def->setDesc(TII.get(EQ_I32)); Inverted = true; break;
- case GT_S_I32: Def->setDesc(TII.get(LE_S_I32)); Inverted = true; break;
- case GE_S_I32: Def->setDesc(TII.get(LT_S_I32)); Inverted = true; break;
- case LT_S_I32: Def->setDesc(TII.get(GE_S_I32)); Inverted = true; break;
- case LE_S_I32: Def->setDesc(TII.get(GT_S_I32)); Inverted = true; break;
- case GT_U_I32: Def->setDesc(TII.get(LE_U_I32)); Inverted = true; break;
- case GE_U_I32: Def->setDesc(TII.get(LT_U_I32)); Inverted = true; break;
- case LT_U_I32: Def->setDesc(TII.get(GE_U_I32)); Inverted = true; break;
- case LE_U_I32: Def->setDesc(TII.get(GT_U_I32)); Inverted = true; break;
- case EQ_I64: Def->setDesc(TII.get(NE_I64)); Inverted = true; break;
- case NE_I64: Def->setDesc(TII.get(EQ_I64)); Inverted = true; break;
- case GT_S_I64: Def->setDesc(TII.get(LE_S_I64)); Inverted = true; break;
- case GE_S_I64: Def->setDesc(TII.get(LT_S_I64)); Inverted = true; break;
- case LT_S_I64: Def->setDesc(TII.get(GE_S_I64)); Inverted = true; break;
- case LE_S_I64: Def->setDesc(TII.get(GT_S_I64)); Inverted = true; break;
- case GT_U_I64: Def->setDesc(TII.get(LE_U_I64)); Inverted = true; break;
- case GE_U_I64: Def->setDesc(TII.get(LT_U_I64)); Inverted = true; break;
- case LT_U_I64: Def->setDesc(TII.get(GE_U_I64)); Inverted = true; break;
- case LE_U_I64: Def->setDesc(TII.get(GT_U_I64)); Inverted = true; break;
- case EQ_F32: Def->setDesc(TII.get(NE_F32)); Inverted = true; break;
- case NE_F32: Def->setDesc(TII.get(EQ_F32)); Inverted = true; break;
- case EQ_F64: Def->setDesc(TII.get(NE_F64)); Inverted = true; break;
- case NE_F64: Def->setDesc(TII.get(EQ_F64)); Inverted = true; break;
+ case EQ_I32:
+ Def->setDesc(TII.get(NE_I32));
+ Inverted = true;
+ break;
+ case NE_I32:
+ Def->setDesc(TII.get(EQ_I32));
+ Inverted = true;
+ break;
+ case GT_S_I32:
+ Def->setDesc(TII.get(LE_S_I32));
+ Inverted = true;
+ break;
+ case GE_S_I32:
+ Def->setDesc(TII.get(LT_S_I32));
+ Inverted = true;
+ break;
+ case LT_S_I32:
+ Def->setDesc(TII.get(GE_S_I32));
+ Inverted = true;
+ break;
+ case LE_S_I32:
+ Def->setDesc(TII.get(GT_S_I32));
+ Inverted = true;
+ break;
+ case GT_U_I32:
+ Def->setDesc(TII.get(LE_U_I32));
+ Inverted = true;
+ break;
+ case GE_U_I32:
+ Def->setDesc(TII.get(LT_U_I32));
+ Inverted = true;
+ break;
+ case LT_U_I32:
+ Def->setDesc(TII.get(GE_U_I32));
+ Inverted = true;
+ break;
+ case LE_U_I32:
+ Def->setDesc(TII.get(GT_U_I32));
+ Inverted = true;
+ break;
+ case EQ_I64:
+ Def->setDesc(TII.get(NE_I64));
+ Inverted = true;
+ break;
+ case NE_I64:
+ Def->setDesc(TII.get(EQ_I64));
+ Inverted = true;
+ break;
+ case GT_S_I64:
+ Def->setDesc(TII.get(LE_S_I64));
+ Inverted = true;
+ break;
+ case GE_S_I64:
+ Def->setDesc(TII.get(LT_S_I64));
+ Inverted = true;
+ break;
+ case LT_S_I64:
+ Def->setDesc(TII.get(GE_S_I64));
+ Inverted = true;
+ break;
+ case LE_S_I64:
+ Def->setDesc(TII.get(GT_S_I64));
+ Inverted = true;
+ break;
+ case GT_U_I64:
+ Def->setDesc(TII.get(LE_U_I64));
+ Inverted = true;
+ break;
+ case GE_U_I64:
+ Def->setDesc(TII.get(LT_U_I64));
+ Inverted = true;
+ break;
+ case LT_U_I64:
+ Def->setDesc(TII.get(GE_U_I64));
+ Inverted = true;
+ break;
+ case LE_U_I64:
+ Def->setDesc(TII.get(GT_U_I64));
+ Inverted = true;
+ break;
+ case EQ_F32:
+ Def->setDesc(TII.get(NE_F32));
+ Inverted = true;
+ break;
+ case NE_F32:
+ Def->setDesc(TII.get(EQ_F32));
+ Inverted = true;
+ break;
+ case EQ_F64:
+ Def->setDesc(TII.get(NE_F64));
+ Inverted = true;
+ break;
+ case NE_F64:
+ Def->setDesc(TII.get(EQ_F64));
+ Inverted = true;
+ break;
case EQZ_I32: {
// Invert an eqz by replacing it with its operand.
Cond = Def->getOperand(1).getReg();
@@ -109,7 +181,8 @@ bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) {
Inverted = true;
break;
}
- default: break;
+ default:
+ break;
}
}
diff --git a/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
index e9cb7c10113b..0491f71cea7f 100644
--- a/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
@@ -50,24 +50,21 @@
///
/// In detail, this pass does following things:
///
-/// 1) Create three global variables: __THREW__, __threwValue, and __tempRet0.
-/// __tempRet0 will be set within __cxa_find_matching_catch() function in
-/// JS library, and __THREW__ and __threwValue will be set in invoke wrappers
+/// 1) Assumes the existence of global variables: __THREW__, __threwValue
+/// __THREW__ and __threwValue will be set in invoke wrappers
/// in JS glue code. For what invoke wrappers are, refer to 3). These
/// variables are used for both exceptions and setjmp/longjmps.
/// __THREW__ indicates whether an exception or a longjmp occurred or not. 0
/// means nothing occurred, 1 means an exception occurred, and other numbers
/// mean a longjmp occurred. In the case of longjmp, __threwValue variable
/// indicates the corresponding setjmp buffer the longjmp corresponds to.
-/// In exception handling, __tempRet0 indicates the type of an exception
-/// caught, and in setjmp/longjmp, it means the second argument to longjmp
-/// function.
///
/// * Exception handling
///
-/// 2) Create setThrew and setTempRet0 functions.
-/// The global variables created in 1) will exist in wasm address space,
-/// but their values should be set in JS code, so we provide these functions
+/// 2) We assume the existence of setThrew and setTempRet0/getTempRet0 functions
+/// at link time.
+/// The global variables in 1) will exist in wasm address space,
+/// but their values should be set in JS code, so these functions
/// as interfaces to JS glue code. These functions are equivalent to the
/// following JS functions, which actually exist in asm.js version of JS
/// library.
@@ -78,10 +75,12 @@
/// __threwValue = value;
/// }
/// }
+//
+/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
///
-/// function setTempRet0(value) {
-/// __tempRet0 = value;
-/// }
+/// In exception handling, getTempRet0 indicates the type of an exception
+/// caught, and in setjmp/longjmp, it means the second argument to longjmp
+/// function.
///
/// 3) Lower
/// invoke @func(arg1, arg2) to label %invoke.cont unwind label %lpad
@@ -118,11 +117,10 @@
/// ... use %val ...
/// into
/// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...)
-/// %val = {%fmc, __tempRet0}
+/// %val = {%fmc, getTempRet0()}
/// ... use %val ...
/// Here N is a number calculated based on the number of clauses.
-/// Global variable __tempRet0 is set within __cxa_find_matching_catch() in
-/// JS glue code.
+/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code.
///
/// 5) Lower
/// resume {%a, %b}
@@ -138,7 +136,17 @@
///
/// * Setjmp / Longjmp handling
///
-/// 7) In the function entry that calls setjmp, initialize setjmpTable and
+/// In case calls to longjmp() exists
+///
+/// 1) Lower
+/// longjmp(buf, value)
+/// into
+/// emscripten_longjmp_jmpbuf(buf, value)
+/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.
+///
+/// In case calls to setjmp() exists
+///
+/// 2) In the function entry that calls setjmp, initialize setjmpTable and
/// sejmpTableSize as follows:
/// setjmpTableSize = 4;
/// setjmpTable = (int *) malloc(40);
@@ -146,27 +154,22 @@
/// setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS
/// code.
///
-/// 8) Lower
+/// 3) Lower
/// setjmp(buf)
/// into
/// setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
-/// setjmpTableSize = __tempRet0;
+/// setjmpTableSize = getTempRet0();
/// For each dynamic setjmp call, setjmpTable stores its ID (a number which
/// is incrementally assigned from 0) and its label (a unique number that
/// represents each callsite of setjmp). When we need more entries in
/// setjmpTable, it is reallocated in saveSetjmp() in JS code and it will
/// return the new table address, and assign the new table size in
-/// __tempRet0. saveSetjmp also stores the setjmp's ID into the buffer buf.
-/// A BB with setjmp is split into two after setjmp call in order to make the
-/// post-setjmp BB the possible destination of longjmp BB.
+/// setTempRet0(). saveSetjmp also stores the setjmp's ID into the buffer
+/// buf. A BB with setjmp is split into two after setjmp call in order to
+/// make the post-setjmp BB the possible destination of longjmp BB.
///
-/// 9) Lower
-/// longjmp(buf, value)
-/// into
-/// emscripten_longjmp_jmpbuf(buf, value)
-/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.
///
-/// 10) Lower every call that might longjmp into
+/// 4) Lower every call that might longjmp into
/// __THREW__ = 0;
/// call @__invoke_SIG(func, arg1, arg2)
/// %__THREW__.val = __THREW__;
@@ -176,32 +179,32 @@
/// setjmpTableSize);
/// if (%label == 0)
/// emscripten_longjmp(%__THREW__.val, __threwValue);
-/// __tempRet0 = __threwValue;
+/// setTempRet0(__threwValue);
/// } else {
/// %label = -1;
/// }
-/// longjmp_result = __tempRet0;
+/// longjmp_result = getTempRet0();
/// switch label {
/// label 1: goto post-setjmp BB 1
/// label 2: goto post-setjmp BB 2
/// ...
/// default: goto splitted next BB
/// }
-/// testSetjmp examines setjmpTable to see if there is a matching setjmp
-/// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
-/// will be the address of matching jmp_buf buffer and __threwValue be the
-/// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
-/// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
-/// each setjmp callsite. Label 0 means this longjmp buffer does not
-/// correspond to one of the setjmp callsites in this function, so in this
-/// case we just chain the longjmp to the caller. (Here we call
-/// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf.
-/// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while
-/// emscripten_longjmp takes an int. Both of them will eventually be lowered
-/// to emscripten_longjmp in s2wasm, but here we need two signatures - we
-/// can't translate an int value to a jmp_buf.)
-/// Label -1 means no longjmp occurred. Otherwise we jump to the right
-/// post-setjmp BB based on the label.
+/// testSetjmp examines setjmpTable to see if there is a matching setjmp
+/// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
+/// will be the address of matching jmp_buf buffer and __threwValue be the
+/// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
+/// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
+/// each setjmp callsite. Label 0 means this longjmp buffer does not
+/// correspond to one of the setjmp callsites in this function, so in this
+/// case we just chain the longjmp to the caller. (Here we call
+/// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf.
+/// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while
+/// emscripten_longjmp takes an int. Both of them will eventually be lowered
+/// to emscripten_longjmp in s2wasm, but here we need two signatures - we
+/// can't translate an int value to a jmp_buf.)
+/// Label -1 means no longjmp occurred. Otherwise we jump to the right
+/// post-setjmp BB based on the label.
///
///===----------------------------------------------------------------------===//
@@ -239,7 +242,8 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
GlobalVariable *ThrewGV;
GlobalVariable *ThrewValueGV;
- GlobalVariable *TempRet0GV;
+ Function *GetTempRet0Func;
+ Function *SetTempRet0Func;
Function *ResumeF;
Function *EHTypeIDF;
Function *EmLongjmpF;
@@ -272,9 +276,6 @@ class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass {
bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); }
bool canLongjmp(Module &M, const Value *Callee) const;
- void createSetThrewFunction(Module &M);
- void createSetTempRet0Function(Module &M);
-
void rebuildSSA(Function &F);
public:
@@ -282,9 +283,10 @@ public:
WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true)
: ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj),
- ThrewGV(nullptr), ThrewValueGV(nullptr), TempRet0GV(nullptr),
- ResumeF(nullptr), EHTypeIDF(nullptr), EmLongjmpF(nullptr),
- EmLongjmpJmpbufF(nullptr), SaveSetjmpF(nullptr), TestSetjmpF(nullptr) {
+ ThrewGV(nullptr), ThrewValueGV(nullptr), GetTempRet0Func(nullptr),
+ SetTempRet0Func(nullptr), ResumeF(nullptr), EHTypeIDF(nullptr),
+ EmLongjmpF(nullptr), EmLongjmpJmpbufF(nullptr), SaveSetjmpF(nullptr),
+ TestSetjmpF(nullptr) {
EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end());
}
bool runOnModule(Module &M) override;
@@ -333,13 +335,15 @@ static bool canThrow(const Value *V) {
return true;
}
-static GlobalVariable *createGlobalVariableI32(Module &M, IRBuilder<> &IRB,
- const char *Name) {
+// Get a global variable with the given name. If it doesn't exist declare it,
+// which will generate an import and asssumes that it will exist at link time.
+static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB,
+ const char *Name) {
if (M.getNamedGlobal(Name))
report_fatal_error(Twine("variable name is reserved: ") + Name);
return new GlobalVariable(M, IRB.getInt32Ty(), false,
- GlobalValue::WeakODRLinkage, IRB.getInt32(0), Name);
+ GlobalValue::ExternalLinkage, nullptr, Name);
}
// Simple function name mangler.
@@ -508,7 +512,8 @@ bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
Function *ThrowF = M.getFunction("__cxa_throw");
Function *TerminateF = M.getFunction("__clang_call_terminate");
if (Callee == BeginCatchF || Callee == EndCatchF ||
- Callee == AllocExceptionF || Callee == ThrowF || Callee == TerminateF)
+ Callee == AllocExceptionF || Callee == ThrowF || Callee == TerminateF ||
+ Callee == GetTempRet0Func || Callee == SetTempRet0Func)
return false;
// Otherwise we don't know
@@ -521,11 +526,11 @@ bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M,
// %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize);
// if (%label == 0)
// emscripten_longjmp(%__THREW__.val, threwValue);
-// __tempRet0 = threwValue;
+// setTempRet0(threwValue);
// } else {
// %label = -1;
// }
-// %longjmp_result = __tempRet0;
+// %longjmp_result = getTempRet0();
//
// As output parameters. returns %label, %longjmp_result, and the BB the last
// instruction (%longjmp_result = ...) is in.
@@ -569,15 +574,15 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue});
IRB.CreateUnreachable();
- // __tempRet0 = threwValue;
+ // setTempRet0(threwValue);
IRB.SetInsertPoint(EndBB2);
- IRB.CreateStore(ThrewValue, TempRet0GV);
+ IRB.CreateCall(SetTempRet0Func, ThrewValue);
IRB.CreateBr(EndBB1);
IRB.SetInsertPoint(ElseBB1);
IRB.CreateBr(EndBB1);
- // longjmp_result = __tempRet0;
+ // longjmp_result = getTempRet0();
IRB.SetInsertPoint(EndBB1);
PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label");
LabelPHI->addIncoming(ThenLabel, EndBB2);
@@ -587,68 +592,7 @@ void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp(
// Output parameter assignment
Label = LabelPHI;
EndBB = EndBB1;
- LongjmpResult = IRB.CreateLoad(TempRet0GV, "longjmp_result");
-}
-
-// Create setThrew function
-// function setThrew(threw, value) {
-// if (__THREW__ == 0) {
-// __THREW__ = threw;
-// __threwValue = value;
-// }
-// }
-void WebAssemblyLowerEmscriptenEHSjLj::createSetThrewFunction(Module &M) {
- LLVMContext &C = M.getContext();
- IRBuilder<> IRB(C);
-
- if (M.getNamedGlobal("setThrew"))
- report_fatal_error("setThrew already exists");
-
- Type *Params[] = {IRB.getInt32Ty(), IRB.getInt32Ty()};
- FunctionType *FTy = FunctionType::get(IRB.getVoidTy(), Params, false);
- Function *F =
- Function::Create(FTy, GlobalValue::WeakODRLinkage, "setThrew", &M);
- Argument *Arg1 = &*(F->arg_begin());
- Argument *Arg2 = &*std::next(F->arg_begin());
- Arg1->setName("threw");
- Arg2->setName("value");
- BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
- BasicBlock *ThenBB = BasicBlock::Create(C, "if.then", F);
- BasicBlock *EndBB = BasicBlock::Create(C, "if.end", F);
-
- IRB.SetInsertPoint(EntryBB);
- Value *Threw = IRB.CreateLoad(ThrewGV, ThrewGV->getName() + ".val");
- Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(0), "cmp");
- IRB.CreateCondBr(Cmp, ThenBB, EndBB);
-
- IRB.SetInsertPoint(ThenBB);
- IRB.CreateStore(Arg1, ThrewGV);
- IRB.CreateStore(Arg2, ThrewValueGV);
- IRB.CreateBr(EndBB);
-
- IRB.SetInsertPoint(EndBB);
- IRB.CreateRetVoid();
-}
-
-// Create setTempRet0 function
-// function setTempRet0(value) {
-// __tempRet0 = value;
-// }
-void WebAssemblyLowerEmscriptenEHSjLj::createSetTempRet0Function(Module &M) {
- LLVMContext &C = M.getContext();
- IRBuilder<> IRB(C);
-
- if (M.getNamedGlobal("setTempRet0"))
- report_fatal_error("setTempRet0 already exists");
- Type *Params[] = {IRB.getInt32Ty()};
- FunctionType *FTy = FunctionType::get(IRB.getVoidTy(), Params, false);
- Function *F =
- Function::Create(FTy, GlobalValue::WeakODRLinkage, "setTempRet0", &M);
- F->arg_begin()->setName("value");
- BasicBlock *EntryBB = BasicBlock::Create(C, "entry", F);
- IRB.SetInsertPoint(EntryBB);
- IRB.CreateStore(&*F->arg_begin(), TempRet0GV);
- IRB.CreateRetVoid();
+ LongjmpResult = IRB.CreateCall(GetTempRet0Func, None, "longjmp_result");
}
void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
@@ -679,6 +623,8 @@ void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
}
bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
+ LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
+
LLVMContext &C = M.getContext();
IRBuilder<> IRB(C);
@@ -688,11 +634,19 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed);
- // Create global variables __THREW__, threwValue, and __tempRet0, which are
- // used in common for both exception handling and setjmp/longjmp handling
- ThrewGV = createGlobalVariableI32(M, IRB, "__THREW__");
- ThrewValueGV = createGlobalVariableI32(M, IRB, "__threwValue");
- TempRet0GV = createGlobalVariableI32(M, IRB, "__tempRet0");
+ // Declare (or get) global variables __THREW__, __threwValue, and
+ // getTempRet0/setTempRet0 function which are used in common for both
+ // exception handling and setjmp/longjmp handling
+ ThrewGV = getGlobalVariableI32(M, IRB, "__THREW__");
+ ThrewValueGV = getGlobalVariableI32(M, IRB, "__threwValue");
+ GetTempRet0Func =
+ Function::Create(FunctionType::get(IRB.getInt32Ty(), false),
+ GlobalValue::ExternalLinkage, "getTempRet0", &M);
+ SetTempRet0Func = Function::Create(
+ FunctionType::get(IRB.getVoidTy(), IRB.getInt32Ty(), false),
+ GlobalValue::ExternalLinkage, "setTempRet0", &M);
+ GetTempRet0Func->setDoesNotThrow();
+ SetTempRet0Func->setDoesNotThrow();
bool Changed = false;
@@ -721,22 +675,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
if (DoSjLj) {
Changed = true; // We have setjmp or longjmp somewhere
- // Register saveSetjmp function
- FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
- SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0),
- IRB.getInt32Ty(), Type::getInt32PtrTy(C),
- IRB.getInt32Ty()};
- FunctionType *FTy =
- FunctionType::get(Type::getInt32PtrTy(C), Params, false);
- SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
- SaveSetjmpFName, &M);
-
- // Register testSetjmp function
- Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()};
- FTy = FunctionType::get(IRB.getInt32Ty(), Params, false);
- TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
- TestSetjmpFName, &M);
-
if (LongjmpF) {
// Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is
// defined in JS code
@@ -746,27 +684,43 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF);
}
- FTy = FunctionType::get(IRB.getVoidTy(),
- {IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
- EmLongjmpF =
- Function::Create(FTy, GlobalValue::ExternalLinkage, EmLongjmpFName, &M);
- // Only traverse functions that uses setjmp in order not to insert
- // unnecessary prep / cleanup code in every function
- SmallPtrSet<Function *, 8> SetjmpUsers;
- for (User *U : SetjmpF->users()) {
- auto *UI = cast<Instruction>(U);
- SetjmpUsers.insert(UI->getFunction());
+ if (SetjmpF) {
+ // Register saveSetjmp function
+ FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
+ SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0),
+ IRB.getInt32Ty(), Type::getInt32PtrTy(C),
+ IRB.getInt32Ty()};
+ FunctionType *FTy =
+ FunctionType::get(Type::getInt32PtrTy(C), Params, false);
+ SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
+ SaveSetjmpFName, &M);
+
+ // Register testSetjmp function
+ Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()};
+ FTy = FunctionType::get(IRB.getInt32Ty(), Params, false);
+ TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
+ TestSetjmpFName, &M);
+
+ FTy = FunctionType::get(IRB.getVoidTy(),
+ {IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
+ EmLongjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
+ EmLongjmpFName, &M);
+
+ // Only traverse functions that uses setjmp in order not to insert
+ // unnecessary prep / cleanup code in every function
+ SmallPtrSet<Function *, 8> SetjmpUsers;
+ for (User *U : SetjmpF->users()) {
+ auto *UI = cast<Instruction>(U);
+ SetjmpUsers.insert(UI->getFunction());
+ }
+ for (Function *F : SetjmpUsers)
+ runSjLjOnFunction(*F);
}
- for (Function *F : SetjmpUsers)
- runSjLjOnFunction(*F);
}
if (!Changed) {
// Delete unused global variables and functions
- ThrewGV->eraseFromParent();
- ThrewValueGV->eraseFromParent();
- TempRet0GV->eraseFromParent();
if (ResumeF)
ResumeF->eraseFromParent();
if (EHTypeIDF)
@@ -780,12 +734,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
return false;
}
- // If we have made any changes while doing exception handling or
- // setjmp/longjmp handling, we have to create these functions for JavaScript
- // to call.
- createSetThrewFunction(M);
- createSetTempRet0Function(M);
-
return true;
}
@@ -908,8 +856,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) {
CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc");
Value *Undef = UndefValue::get(LPI->getType());
Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0");
- Value *TempRet0 =
- IRB.CreateLoad(TempRet0GV, TempRet0GV->getName() + ".val");
+ Value *TempRet0 = IRB.CreateCall(GetTempRet0Func, None, "tempret0");
Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1");
LPI->replaceAllUsesWith(Pair1);
@@ -990,7 +937,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
Instruction *NewSetjmpTable =
IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable");
Instruction *NewSetjmpTableSize =
- IRB.CreateLoad(TempRet0GV, "setjmpTableSize");
+ IRB.CreateCall(GetTempRet0Func, None, "setjmpTableSize");
SetjmpTableInsts.push_back(NewSetjmpTable);
SetjmpTableSizeInsts.push_back(NewSetjmpTableSize);
ToErase.push_back(CI);
@@ -1098,7 +1045,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
// Free setjmpTable buffer before each return instruction
for (BasicBlock &BB : F) {
- TerminatorInst *TI = BB.getTerminator();
+ Instruction *TI = BB.getTerminator();
if (isa<ReturnInst>(TI))
CallInst::CreateFree(SetjmpTable, TI);
}
@@ -1112,7 +1059,7 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
// ...
// somebb:
// setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
- // setjmpTableSize = __tempRet0;
+ // setjmpTableSize = getTempRet0();
// So we need to make sure the SSA for these variables is valid so that every
// saveSetjmp and testSetjmp calls have the correct arguments.
SSAUpdater SetjmpTableSSA;
diff --git a/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp b/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
index ee708d637b25..84c877cb8d02 100644
--- a/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
@@ -18,15 +18,15 @@
//===----------------------------------------------------------------------===//
#include "WebAssembly.h"
+#include "llvm/ADT/MapVector.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
-#include "llvm/Transforms/Utils/ModuleUtils.h"
#include "llvm/Pass.h"
-#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
#define DEBUG_TYPE "wasm-lower-global-dtors"
@@ -59,6 +59,8 @@ ModulePass *llvm::createWebAssemblyLowerGlobalDtors() {
}
bool LowerGlobalDtors::runOnModule(Module &M) {
+ LLVM_DEBUG(dbgs() << "********** Lower Global Destructors **********\n");
+
GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors");
if (!GV)
return false;
@@ -77,18 +79,20 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
// Collect the contents of @llvm.global_dtors, collated by priority and
// associated symbol.
- std::map<uint16_t, MapVector<Constant *, std::vector<Constant *> > > DtorFuncs;
+ std::map<uint16_t, MapVector<Constant *, std::vector<Constant *>>> DtorFuncs;
for (Value *O : InitList->operands()) {
ConstantStruct *CS = dyn_cast<ConstantStruct>(O);
- if (!CS) continue; // Malformed.
+ if (!CS)
+ continue; // Malformed.
ConstantInt *Priority = dyn_cast<ConstantInt>(CS->getOperand(0));
- if (!Priority) continue; // Malformed.
+ if (!Priority)
+ continue; // Malformed.
uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX);
Constant *DtorFunc = CS->getOperand(1);
if (DtorFunc->isNullValue())
- break; // Found a null terminator, skip the rest.
+ break; // Found a null terminator, skip the rest.
Constant *Associated = CS->getOperand(2);
Associated = cast<Constant>(Associated->stripPointerCastsNoFollowAliases());
@@ -101,31 +105,23 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
// extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
LLVMContext &C = M.getContext();
PointerType *VoidStar = Type::getInt8PtrTy(C);
- Type *AtExitFuncArgs[] = { VoidStar };
- FunctionType *AtExitFuncTy = FunctionType::get(
- Type::getVoidTy(C),
- AtExitFuncArgs,
- /*isVarArg=*/false);
+ Type *AtExitFuncArgs[] = {VoidStar};
+ FunctionType *AtExitFuncTy =
+ FunctionType::get(Type::getVoidTy(C), AtExitFuncArgs,
+ /*isVarArg=*/false);
- Type *AtExitArgs[] = {
- PointerType::get(AtExitFuncTy, 0),
- VoidStar,
- VoidStar
- };
- FunctionType *AtExitTy = FunctionType::get(
- Type::getInt32Ty(C),
- AtExitArgs,
- /*isVarArg=*/false);
+ Type *AtExitArgs[] = {PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar};
+ FunctionType *AtExitTy = FunctionType::get(Type::getInt32Ty(C), AtExitArgs,
+ /*isVarArg=*/false);
Constant *AtExit = M.getOrInsertFunction("__cxa_atexit", AtExitTy);
// Declare __dso_local.
Constant *DsoHandle = M.getNamedValue("__dso_handle");
if (!DsoHandle) {
Type *DsoHandleTy = Type::getInt8Ty(C);
- GlobalVariable *Handle =
- new GlobalVariable(M, DsoHandleTy, /*isConstant=*/true,
- GlobalVariable::ExternalWeakLinkage,
- nullptr, "__dso_handle");
+ GlobalVariable *Handle = new GlobalVariable(
+ M, DsoHandleTy, /*isConstant=*/true,
+ GlobalVariable::ExternalWeakLinkage, nullptr, "__dso_handle");
Handle->setVisibility(GlobalVariable::HiddenVisibility);
DsoHandle = Handle;
}
@@ -139,13 +135,13 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
Constant *Associated = AssociatedAndMore.first;
Function *CallDtors = Function::Create(
- AtExitFuncTy, Function::PrivateLinkage,
- "call_dtors" +
- (Priority != UINT16_MAX ?
- (Twine(".") + Twine(Priority)) : Twine()) +
- (!Associated->isNullValue() ?
- (Twine(".") + Associated->getName()) : Twine()),
- &M);
+ AtExitFuncTy, Function::PrivateLinkage,
+ "call_dtors" +
+ (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority))
+ : Twine()) +
+ (!Associated->isNullValue() ? (Twine(".") + Associated->getName())
+ : Twine()),
+ &M);
BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors);
for (auto Dtor : AssociatedAndMore.second)
@@ -155,29 +151,29 @@ bool LowerGlobalDtors::runOnModule(Module &M) {
FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C),
/*isVarArg=*/false);
Function *RegisterCallDtors = Function::Create(
- VoidVoid, Function::PrivateLinkage,
- "register_call_dtors" +
- (Priority != UINT16_MAX ?
- (Twine(".") + Twine(Priority)) : Twine()) +
- (!Associated->isNullValue() ?
- (Twine(".") + Associated->getName()) : Twine()),
- &M);
+ VoidVoid, Function::PrivateLinkage,
+ "register_call_dtors" +
+ (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority))
+ : Twine()) +
+ (!Associated->isNullValue() ? (Twine(".") + Associated->getName())
+ : Twine()),
+ &M);
BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors);
BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors);
BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors);
Value *Null = ConstantPointerNull::get(VoidStar);
- Value *Args[] = { CallDtors, Null, DsoHandle };
+ Value *Args[] = {CallDtors, Null, DsoHandle};
Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB);
Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res,
Constant::getNullValue(Res->getType()));
BranchInst::Create(FailBB, RetBB, Cmp, EntryBB);
// If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave.
- // This should be very rare, because if the process is running out of memory
- // before main has even started, something is wrong.
- CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap),
- "", FailBB);
+ // This should be very rare, because if the process is running out of
+ // memory before main has even started, something is wrong.
+ CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), "",
+ FailBB);
new UnreachableInst(C, FailBB);
ReturnInst::Create(C, RetBB);
diff --git a/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index d85db14fc679..fa862fbaa634 100644
--- a/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -30,6 +30,21 @@
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
+// Defines llvm::WebAssembly::getStackOpcode to convert register instructions to
+// stack instructions
+#define GET_INSTRMAP_INFO 1
+#include "WebAssemblyGenInstrInfo.inc"
+
+// This disables the removal of registers when lowering into MC, as required
+// by some current tests.
+static cl::opt<bool>
+ WasmKeepRegisters("wasm-keep-registers", cl::Hidden,
+ cl::desc("WebAssembly: output stack registers in"
+ " instruction output for test purposes only."),
+ cl::init(false));
+
+static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
+
MCSymbol *
WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
const GlobalValue *Global = MO.getGlobal();
@@ -40,35 +55,13 @@ WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
const TargetMachine &TM = MF.getTarget();
const Function &CurrentFunc = MF.getFunction();
- SmallVector<wasm::ValType, 4> Returns;
- SmallVector<wasm::ValType, 4> Params;
-
- wasm::ValType iPTR =
- MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() ?
- wasm::ValType::I64 :
- wasm::ValType::I32;
-
- SmallVector<MVT, 4> ResultMVTs;
- ComputeLegalValueVTs(CurrentFunc, TM, FuncTy->getReturnType(), ResultMVTs);
- // WebAssembly can't currently handle returning tuples.
- if (ResultMVTs.size() <= 1)
- for (MVT ResultMVT : ResultMVTs)
- Returns.push_back(WebAssembly::toValType(ResultMVT));
- else
- Params.push_back(iPTR);
+ SmallVector<MVT, 1> ResultMVTs;
+ SmallVector<MVT, 4> ParamMVTs;
+ ComputeSignatureVTs(FuncTy, CurrentFunc, TM, ParamMVTs, ResultMVTs);
- for (Type *Ty : FuncTy->params()) {
- SmallVector<MVT, 4> ParamMVTs;
- ComputeLegalValueVTs(CurrentFunc, TM, Ty, ParamMVTs);
- for (MVT ParamMVT : ParamMVTs)
- Params.push_back(WebAssembly::toValType(ParamMVT));
- }
-
- if (FuncTy->isVarArg())
- Params.push_back(iPTR);
-
- WasmSym->setReturns(std::move(Returns));
- WasmSym->setParams(std::move(Params));
+ auto Signature = SignatureFromMVTs(ResultMVTs, ParamMVTs);
+ WasmSym->setSignature(Signature.get());
+ Printer.addSignature(std::move(Signature));
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
}
@@ -82,10 +75,10 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name));
const WebAssemblySubtarget &Subtarget = Printer.getSubtarget();
- // __stack_pointer is a global variable; all other external symbols used by
- // CodeGen are functions. It's OK to hardcode knowledge of specific symbols
- // here; this method is precisely there for fetching the signatures of known
- // Clang-provided symbols.
+ // Except for the two exceptions (__stack_pointer and __cpp_exception), all
+ // other external symbols used by CodeGen are functions. It's OK to hardcode
+ // knowledge of specific symbols here; this method is precisely there for
+ // fetching the signatures of known Clang-provided symbols.
if (strcmp(Name, "__stack_pointer") == 0) {
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
WasmSym->setGlobalType(wasm::WasmGlobalType{
@@ -97,27 +90,55 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
SmallVector<wasm::ValType, 4> Returns;
SmallVector<wasm::ValType, 4> Params;
- GetSignature(Subtarget, Name, Returns, Params);
+ if (strcmp(Name, "__cpp_exception") == 0) {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_EVENT);
+ // We can't confirm its signature index for now because there can be
+ // imported exceptions. Set it to be 0 for now.
+ WasmSym->setEventType(
+ {wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
+ // We may have multiple C++ compilation units to be linked together, each of
+ // which defines the exception symbol. To resolve them, we declare them as
+ // weak.
+ WasmSym->setWeak(true);
+ WasmSym->setExternal(true);
- WasmSym->setReturns(std::move(Returns));
- WasmSym->setParams(std::move(Params));
- WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ // All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
+ // (for wasm64) param type and void return type. The reaon is, all C++
+ // exception values are pointers, and to share the type section with
+ // functions, exceptions are assumed to have void return type.
+ Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
+ : wasm::ValType::I32);
+ } else { // Function symbols
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ GetLibcallSignature(Subtarget, Name, Returns, Params);
+ }
+ auto Signature =
+ make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params));
+ WasmSym->setSignature(Signature.get());
+ Printer.addSignature(std::move(Signature));
return WasmSym;
}
MCOperand WebAssemblyMCInstLower::LowerSymbolOperand(MCSymbol *Sym,
int64_t Offset,
- bool IsFunc) const {
+ bool IsFunc, bool IsGlob,
+ bool IsEvent) const {
MCSymbolRefExpr::VariantKind VK =
IsFunc ? MCSymbolRefExpr::VK_WebAssembly_FUNCTION
- : MCSymbolRefExpr::VK_None;
+ : IsGlob ? MCSymbolRefExpr::VK_WebAssembly_GLOBAL
+ : IsEvent ? MCSymbolRefExpr::VK_WebAssembly_EVENT
+ : MCSymbolRefExpr::VK_None;
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, VK, Ctx);
if (Offset != 0) {
if (IsFunc)
report_fatal_error("Function addresses with offsets not supported");
+ if (IsGlob)
+ report_fatal_error("Global indexes with offsets not supported");
+ if (IsEvent)
+ report_fatal_error("Event indexes with offsets not supported");
Expr =
MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Offset, Ctx), Ctx);
}
@@ -135,6 +156,8 @@ static wasm::ValType getType(const TargetRegisterClass *RC) {
return wasm::ValType::F32;
if (RC == &WebAssembly::F64RegClass)
return wasm::ValType::F64;
+ if (RC == &WebAssembly::V128RegClass)
+ return wasm::ValType::V128;
llvm_unreachable("Unexpected register class");
}
@@ -187,8 +210,10 @@ void WebAssemblyMCInstLower::Lower(const MachineInstr *MI,
Params.pop_back();
MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym);
- WasmSym->setReturns(std::move(Returns));
- WasmSym->setParams(std::move(Params));
+ auto Signature = make_unique<wasm::WasmSignature>(std::move(Returns),
+ std::move(Params));
+ WasmSym->setSignature(Signature.get());
+ Printer.addSignature(std::move(Signature));
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
const MCExpr *Expr = MCSymbolRefExpr::create(
@@ -212,21 +237,68 @@ void WebAssemblyMCInstLower::Lower(const MachineInstr *MI,
break;
}
case MachineOperand::MO_GlobalAddress:
- assert(MO.getTargetFlags() == 0 &&
+ assert(MO.getTargetFlags() == WebAssemblyII::MO_NO_FLAG &&
"WebAssembly does not use target flags on GlobalAddresses");
MCOp = LowerSymbolOperand(GetGlobalAddressSymbol(MO), MO.getOffset(),
- MO.getGlobal()->getValueType()->isFunctionTy());
+ MO.getGlobal()->getValueType()->isFunctionTy(),
+ false, false);
break;
case MachineOperand::MO_ExternalSymbol:
// The target flag indicates whether this is a symbol for a
// variable or a function.
- assert((MO.getTargetFlags() & -2) == 0 &&
- "WebAssembly uses only one target flag bit on ExternalSymbols");
- MCOp = LowerSymbolOperand(GetExternalSymbolSymbol(MO), /*Offset=*/0,
- MO.getTargetFlags() & 1);
+ assert((MO.getTargetFlags() & ~WebAssemblyII::MO_SYMBOL_MASK) == 0 &&
+ "WebAssembly uses only symbol flags on ExternalSymbols");
+ MCOp = LowerSymbolOperand(
+ GetExternalSymbolSymbol(MO), /*Offset=*/0,
+ (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_FUNCTION) != 0,
+ (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0,
+ (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_EVENT) != 0);
+ break;
+ case MachineOperand::MO_MCSymbol:
+ // This is currently used only for LSDA symbols (GCC_except_table),
+ // because global addresses or other external symbols are handled above.
+ assert(MO.getTargetFlags() == 0 &&
+ "WebAssembly does not use target flags on MCSymbol");
+ MCOp = LowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, false,
+ false);
break;
}
OutMI.addOperand(MCOp);
}
+
+ if (!WasmKeepRegisters)
+ removeRegisterOperands(MI, OutMI);
+}
+
+static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
+ // Remove all uses of stackified registers to bring the instruction format
+ // into its final stack form used thruout MC, and transition opcodes to
+ // their _S variant.
+ // We do this seperate from the above code that still may need these
+ // registers for e.g. call_indirect signatures.
+ // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for
+ // details.
+ // TODO: the code above creates new registers which are then removed here.
+ // That code could be slightly simplified by not doing that, though maybe
+ // it is simpler conceptually to keep the code above in "register mode"
+ // until this transition point.
+ // FIXME: we are not processing inline assembly, which contains register
+ // operands, because it is used by later target generic code.
+ if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm())
+ return;
+
+ // Transform to _S instruction.
+ auto RegOpcode = OutMI.getOpcode();
+ auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode);
+ assert(StackOpcode != -1 && "Failed to stackify instruction");
+ OutMI.setOpcode(StackOpcode);
+
+ // Remove register operands.
+ for (auto I = OutMI.getNumOperands(); I; --I) {
+ auto &MO = OutMI.getOperand(I - 1);
+ if (MO.isReg()) {
+ OutMI.erase(&MO);
+ }
+ }
}
diff --git a/lib/Target/WebAssembly/WebAssemblyMCInstLower.h b/lib/Target/WebAssembly/WebAssemblyMCInstLower.h
index 41b4313bb38c..fa7a0ea61b3b 100644
--- a/lib/Target/WebAssembly/WebAssemblyMCInstLower.h
+++ b/lib/Target/WebAssembly/WebAssemblyMCInstLower.h
@@ -33,8 +33,8 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower {
MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const;
MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const;
- MCOperand LowerSymbolOperand(MCSymbol *Sym, int64_t Offset,
- bool IsFunc) const;
+ MCOperand LowerSymbolOperand(MCSymbol *Sym, int64_t Offset, bool IsFunc,
+ bool IsGlob, bool IsEvent) const;
public:
WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer)
diff --git a/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp b/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
index e511e574050f..0157af0f8510 100644
--- a/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp
@@ -43,20 +43,38 @@ void llvm::ComputeLegalValueVTs(const Function &F, const TargetMachine &TM,
}
}
-void llvm::ComputeSignatureVTs(const Function &F, const TargetMachine &TM,
+void llvm::ComputeSignatureVTs(const FunctionType *Ty, const Function &F,
+ const TargetMachine &TM,
SmallVectorImpl<MVT> &Params,
SmallVectorImpl<MVT> &Results) {
- ComputeLegalValueVTs(F, TM, F.getReturnType(), Results);
+ ComputeLegalValueVTs(F, TM, Ty->getReturnType(), Results);
+ MVT PtrVT = MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits());
if (Results.size() > 1) {
// WebAssembly currently can't lower returns of multiple values without
// demoting to sret (see WebAssemblyTargetLowering::CanLowerReturn). So
// replace multiple return values with a pointer parameter.
Results.clear();
- Params.push_back(
- MVT::getIntegerVT(TM.createDataLayout().getPointerSizeInBits()));
+ Params.push_back(PtrVT);
}
- for (auto &Arg : F.args())
- ComputeLegalValueVTs(F, TM, Arg.getType(), Params);
+ for (auto *Param : Ty->params())
+ ComputeLegalValueVTs(F, TM, Param, Params);
+ if (Ty->isVarArg())
+ Params.push_back(PtrVT);
+}
+
+void llvm::ValTypesFromMVTs(const ArrayRef<MVT> &In,
+ SmallVectorImpl<wasm::ValType> &Out) {
+ for (MVT Ty : In)
+ Out.push_back(WebAssembly::toValType(Ty));
+}
+
+std::unique_ptr<wasm::WasmSignature>
+llvm::SignatureFromMVTs(const SmallVectorImpl<MVT> &Results,
+ const SmallVectorImpl<MVT> &Params) {
+ auto Sig = make_unique<wasm::WasmSignature>();
+ ValTypesFromMVTs(Results, Sig->Returns);
+ ValTypesFromMVTs(Params, Sig->Params);
+ return Sig;
}
diff --git a/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
index a60b10fc5309..4be4beb85d04 100644
--- a/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
+++ b/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
@@ -17,7 +17,9 @@
#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYMACHINEFUNCTIONINFO_H
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "llvm/BinaryFormat/Wasm.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/MC/MCSymbolWasm.h"
namespace llvm {
@@ -50,7 +52,7 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo {
// overaligned values on the user stack.
unsigned BasePtrVreg = -1U;
- public:
+public:
explicit WebAssemblyFunctionInfo(MachineFunction &MF) : MF(MF) {}
~WebAssemblyFunctionInfo() override;
@@ -60,7 +62,10 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo {
void addResult(MVT VT) { Results.push_back(VT); }
const std::vector<MVT> &getResults() const { return Results; }
- void clearParamsAndResults() { Params.clear(); Results.clear(); }
+ void clearParamsAndResults() {
+ Params.clear();
+ Results.clear();
+ }
void setNumLocals(size_t NumLocals) { Locals.resize(NumLocals, MVT::i32); }
void setLocal(size_t i, MVT VT) { Locals[i] = VT; }
@@ -115,13 +120,22 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo {
}
};
-void ComputeLegalValueVTs(const Function &F, const TargetMachine &TM,
- Type *Ty, SmallVectorImpl<MVT> &ValueVTs);
+void ComputeLegalValueVTs(const Function &F, const TargetMachine &TM, Type *Ty,
+ SmallVectorImpl<MVT> &ValueVTs);
-void ComputeSignatureVTs(const Function &F, const TargetMachine &TM,
- SmallVectorImpl<MVT> &Params,
+// Compute the signature for a given FunctionType (Ty). Note that it's not the
+// signature for F (F is just used to get varous context)
+void ComputeSignatureVTs(const FunctionType *Ty, const Function &F,
+ const TargetMachine &TM, SmallVectorImpl<MVT> &Params,
SmallVectorImpl<MVT> &Results);
+void ValTypesFromMVTs(const ArrayRef<MVT> &In,
+ SmallVectorImpl<wasm::ValType> &Out);
+
+std::unique_ptr<wasm::WasmSignature>
+SignatureFromMVTs(const SmallVectorImpl<MVT> &Results,
+ const SmallVectorImpl<MVT> &Params);
+
} // end namespace llvm
#endif
diff --git a/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp b/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp
index 893e8484c4c6..c4b5e96db0c7 100644
--- a/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp
@@ -1,4 +1,4 @@
-//===-- WebAssemblyStoreResults.cpp - Optimize using store result values --===//
+//== WebAssemblyMemIntrinsicResults.cpp - Optimize memory intrinsic results ==//
//
// The LLVM Compiler Infrastructure
//
@@ -8,19 +8,22 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// This file implements an optimization pass using store result values.
+/// This file implements an optimization pass using memory intrinsic results.
///
-/// WebAssembly's store instructions return the stored value. This is to enable
-/// an optimization wherein uses of the stored value can be replaced by uses of
-/// the store's result value, making the stored value register more likely to
-/// be single-use, thus more likely to be useful to register stackifying, and
-/// potentially also exposing the store to register stackifying. These both can
-/// reduce get_local/set_local traffic.
+/// Calls to memory intrinsics (memcpy, memmove, memset) return the destination
+/// address. They are in the form of
+/// %dst_new = call @memcpy %dst, %src, %len
+/// where %dst and %dst_new registers contain the same value.
///
-/// This pass also performs this optimization for memcpy, memmove, and memset
-/// calls, since the LLVM intrinsics for these return void so they can't use the
-/// returned attribute and consequently aren't handled by the OptimizeReturned
-/// pass.
+/// This is to enable an optimization wherein uses of the %dst register used in
+/// the parameter can be replaced by uses of the %dst_new register used in the
+/// result, making the %dst register more likely to be single-use, thus more
+/// likely to be useful to register stackifying, and potentially also exposing
+/// the call instruction itself to register stackifying. These both can reduce
+/// local.get/local.set traffic.
+///
+/// The LLVM intrinsics for these return void so they can't use the returned
+/// attribute and consequently aren't handled by the OptimizeReturned pass.
///
//===----------------------------------------------------------------------===//
@@ -38,15 +41,17 @@
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
-#define DEBUG_TYPE "wasm-store-results"
+#define DEBUG_TYPE "wasm-mem-intrinsic-results"
namespace {
-class WebAssemblyStoreResults final : public MachineFunctionPass {
+class WebAssemblyMemIntrinsicResults final : public MachineFunctionPass {
public:
static char ID; // Pass identification, replacement for typeid
- WebAssemblyStoreResults() : MachineFunctionPass(ID) {}
+ WebAssemblyMemIntrinsicResults() : MachineFunctionPass(ID) {}
- StringRef getPassName() const override { return "WebAssembly Store Results"; }
+ StringRef getPassName() const override {
+ return "WebAssembly Memory Intrinsic Results";
+ }
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
@@ -67,12 +72,13 @@ private:
};
} // end anonymous namespace
-char WebAssemblyStoreResults::ID = 0;
-INITIALIZE_PASS(WebAssemblyStoreResults, DEBUG_TYPE,
- "Optimize store result values for WebAssembly", false, false)
+char WebAssemblyMemIntrinsicResults::ID = 0;
+INITIALIZE_PASS(WebAssemblyMemIntrinsicResults, DEBUG_TYPE,
+ "Optimize memory intrinsic result values for WebAssembly",
+ false, false)
-FunctionPass *llvm::createWebAssemblyStoreResults() {
- return new WebAssemblyStoreResults();
+FunctionPass *llvm::createWebAssemblyMemIntrinsicResults() {
+ return new WebAssemblyMemIntrinsicResults();
}
// Replace uses of FromReg with ToReg if they are dominated by MI.
@@ -91,7 +97,8 @@ static bool ReplaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI,
SmallVector<SlotIndex, 4> Indices;
- for (auto I = MRI.use_nodbg_begin(FromReg), E = MRI.use_nodbg_end(); I != E;) {
+ for (auto I = MRI.use_nodbg_begin(FromReg), E = MRI.use_nodbg_end();
+ I != E;) {
MachineOperand &O = *I++;
MachineInstr *Where = O.getParent();
@@ -132,9 +139,9 @@ static bool ReplaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI,
// If we replaced all dominated uses, FromReg is now killed at MI.
if (!FromLI->liveAt(FromIdx.getDeadSlot()))
- MI.addRegisterKilled(FromReg,
- MBB.getParent()->getSubtarget<WebAssemblySubtarget>()
- .getRegisterInfo());
+ MI.addRegisterKilled(FromReg, MBB.getParent()
+ ->getSubtarget<WebAssemblySubtarget>()
+ .getRegisterInfo());
}
return Changed;
@@ -142,8 +149,7 @@ static bool ReplaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI,
static bool optimizeCall(MachineBasicBlock &MBB, MachineInstr &MI,
const MachineRegisterInfo &MRI,
- MachineDominatorTree &MDT,
- LiveIntervals &LIS,
+ MachineDominatorTree &MDT, LiveIntervals &LIS,
const WebAssemblyTargetLowering &TLI,
const TargetLibraryInfo &LibInfo) {
MachineOperand &Op1 = MI.getOperand(1);
@@ -164,14 +170,14 @@ static bool optimizeCall(MachineBasicBlock &MBB, MachineInstr &MI,
unsigned FromReg = MI.getOperand(2).getReg();
unsigned ToReg = MI.getOperand(0).getReg();
if (MRI.getRegClass(FromReg) != MRI.getRegClass(ToReg))
- report_fatal_error("Store results: call to builtin function with wrong "
- "signature, from/to mismatch");
+ report_fatal_error("Memory Intrinsic results: call to builtin function "
+ "with wrong signature, from/to mismatch");
return ReplaceDominatedUses(MBB, MI, FromReg, ToReg, MRI, MDT, LIS);
}
-bool WebAssemblyStoreResults::runOnMachineFunction(MachineFunction &MF) {
+bool WebAssemblyMemIntrinsicResults::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG({
- dbgs() << "********** Store Results **********\n"
+ dbgs() << "********** Memory Intrinsic Results **********\n"
<< "********** Function: " << MF.getName() << '\n';
});
@@ -186,7 +192,8 @@ bool WebAssemblyStoreResults::runOnMachineFunction(MachineFunction &MF) {
// We don't preserve SSA form.
MRI.leaveSSA();
- assert(MRI.tracksLiveness() && "StoreResults expects liveness tracking");
+ assert(MRI.tracksLiveness() &&
+ "MemIntrinsicResults expects liveness tracking");
for (auto &MBB : MF) {
LLVM_DEBUG(dbgs() << "Basic Block: " << MBB.getName() << '\n');
diff --git a/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp b/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp
index 04ac22a589ea..3d0a15244ee0 100644
--- a/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp
@@ -65,7 +65,8 @@ FunctionPass *llvm::createWebAssemblyOptimizeLiveIntervals() {
return new WebAssemblyOptimizeLiveIntervals();
}
-bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction(MachineFunction &MF) {
+bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction(
+ MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** Optimize LiveIntervals **********\n"
"********** Function: "
<< MF.getName() << '\n');
@@ -76,11 +77,10 @@ bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction(MachineFunction &MF)
// We don't preserve SSA form.
MRI.leaveSSA();
- assert(MRI.tracksLiveness() &&
- "OptimizeLiveIntervals expects liveness");
+ assert(MRI.tracksLiveness() && "OptimizeLiveIntervals expects liveness");
// Split multiple-VN LiveIntervals into multiple LiveIntervals.
- SmallVector<LiveInterval*, 4> SplitLIs;
+ SmallVector<LiveInterval *, 4> SplitLIs;
for (unsigned i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) {
unsigned Reg = TargetRegisterInfo::index2VirtReg(i);
if (MRI.reg_nodbg_empty(Reg))
@@ -94,7 +94,7 @@ bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction(MachineFunction &MF)
// instructions to satisfy LiveIntervals' requirement that all uses be
// dominated by defs. Now that LiveIntervals has computed which of these
// defs are actually needed and which are dead, remove the dead ones.
- for (auto MII = MF.begin()->begin(), MIE = MF.begin()->end(); MII != MIE; ) {
+ for (auto MII = MF.begin()->begin(), MIE = MF.begin()->end(); MII != MIE;) {
MachineInstr *MI = &*MII++;
if (MI->isImplicitDef() && MI->getOperand(0).isDead()) {
LiveInterval &LI = LIS.getInterval(MI->getOperand(0).getReg());
diff --git a/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp b/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp
index 113ee2532bce..2c018d0785a7 100644
--- a/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp
@@ -74,6 +74,10 @@ void OptimizeReturned::visitCallSite(CallSite CS) {
}
bool OptimizeReturned::runOnFunction(Function &F) {
+ LLVM_DEBUG(dbgs() << "********** Optimize returned Attributes **********\n"
+ "********** Function: "
+ << F.getName() << '\n');
+
DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
visit(F);
return true;
diff --git a/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
index a54484407805..2dfd85953f14 100644
--- a/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
@@ -192,11 +192,21 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4i32,
WebAssembly::COPY_V128);
break;
+ case WebAssembly::RETURN_v2i64:
+ Changed |= MaybeRewriteToFallthrough(
+ MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v2i64,
+ WebAssembly::COPY_V128);
+ break;
case WebAssembly::RETURN_v4f32:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4f32,
WebAssembly::COPY_V128);
break;
+ case WebAssembly::RETURN_v2f64:
+ Changed |= MaybeRewriteToFallthrough(
+ MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v2f64,
+ WebAssembly::COPY_V128);
+ break;
case WebAssembly::RETURN_VOID:
Changed |= MaybeRewriteToFallthrough(
MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_VOID,
diff --git a/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp b/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp
index e44e7057e233..0be0ba657830 100644
--- a/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp
@@ -70,7 +70,8 @@ static bool HasArgumentDef(unsigned Reg, const MachineRegisterInfo &MRI) {
return false;
}
-bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction(MachineFunction &MF) {
+bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction(
+ MachineFunction &MF) {
LLVM_DEBUG({
dbgs() << "********** Prepare For LiveIntervals **********\n"
<< "********** Function: " << MF.getName() << '\n';
@@ -112,7 +113,7 @@ bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction(MachineFunction &M
// Move ARGUMENT_* instructions to the top of the entry block, so that their
// liveness reflects the fact that these really are live-in values.
- for (auto MII = Entry.begin(), MIE = Entry.end(); MII != MIE; ) {
+ for (auto MII = Entry.begin(), MIE = Entry.end(); MII != MIE;) {
MachineInstr &MI = *MII++;
if (WebAssembly::isArgument(MI)) {
MI.removeFromParent();
diff --git a/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp b/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
index d69a27937105..d97b13a8d699 100644
--- a/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp
@@ -118,16 +118,15 @@ bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) {
// registers), by weight next, and then by position.
// TODO: Investigate more intelligent sorting heuristics. For starters, we
// should try to coalesce adjacent live intervals before non-adjacent ones.
- llvm::sort(SortedIntervals.begin(), SortedIntervals.end(),
- [MRI](LiveInterval *LHS, LiveInterval *RHS) {
- if (MRI->isLiveIn(LHS->reg) != MRI->isLiveIn(RHS->reg))
- return MRI->isLiveIn(LHS->reg);
- if (LHS->weight != RHS->weight)
- return LHS->weight > RHS->weight;
- if (LHS->empty() || RHS->empty())
- return !LHS->empty() && RHS->empty();
- return *LHS < *RHS;
- });
+ llvm::sort(SortedIntervals, [MRI](LiveInterval *LHS, LiveInterval *RHS) {
+ if (MRI->isLiveIn(LHS->reg) != MRI->isLiveIn(RHS->reg))
+ return MRI->isLiveIn(LHS->reg);
+ if (LHS->weight != RHS->weight)
+ return LHS->weight > RHS->weight;
+ if (LHS->empty() || RHS->empty())
+ return !LHS->empty() && RHS->empty();
+ return *LHS < *RHS;
+ });
LLVM_DEBUG(dbgs() << "Coloring register intervals:\n");
SmallVector<unsigned, 16> SlotMapping(SortedIntervals.size(), -1u);
diff --git a/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
index 9f5d5bd87831..1eb32ed64494 100644
--- a/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -22,9 +22,11 @@
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_*
#include "WebAssembly.h"
+#include "WebAssemblyDebugValueManager.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblySubtarget.h"
#include "WebAssemblyUtilities.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
@@ -97,11 +99,11 @@ static void ImposeStackOrdering(MachineInstr *MI) {
static void ConvertImplicitDefToConstZero(MachineInstr *MI,
MachineRegisterInfo &MRI,
const TargetInstrInfo *TII,
- MachineFunction &MF) {
+ MachineFunction &MF,
+ LiveIntervals &LIS) {
assert(MI->getOpcode() == TargetOpcode::IMPLICIT_DEF);
- const auto *RegClass =
- MRI.getRegClass(MI->getOperand(0).getReg());
+ const auto *RegClass = MRI.getRegClass(MI->getOperand(0).getReg());
if (RegClass == &WebAssembly::I32RegClass) {
MI->setDesc(TII->get(WebAssembly::CONST_I32));
MI->addOperand(MachineOperand::CreateImm(0));
@@ -118,6 +120,14 @@ static void ConvertImplicitDefToConstZero(MachineInstr *MI,
ConstantFP *Val = cast<ConstantFP>(Constant::getNullValue(
Type::getDoubleTy(MF.getFunction().getContext())));
MI->addOperand(MachineOperand::CreateFPImm(Val));
+ } else if (RegClass == &WebAssembly::V128RegClass) {
+ unsigned TempReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
+ MI->setDesc(TII->get(WebAssembly::SPLAT_v4i32));
+ MI->addOperand(MachineOperand::CreateReg(TempReg, false));
+ MachineInstr *Const = BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
+ TII->get(WebAssembly::CONST_I32), TempReg)
+ .addImm(0);
+ LIS.InsertMachineInstrInMaps(*Const);
} else {
llvm_unreachable("Unexpected reg class");
}
@@ -172,29 +182,24 @@ static void Query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read,
// Check for stores.
if (MI.mayStore()) {
Write = true;
-
- // Check for stores to __stack_pointer.
- for (auto MMO : MI.memoperands()) {
- const MachinePointerInfo &MPI = MMO->getPointerInfo();
- if (MPI.V.is<const PseudoSourceValue *>()) {
- auto PSV = MPI.V.get<const PseudoSourceValue *>();
- if (const ExternalSymbolPseudoSourceValue *EPSV =
- dyn_cast<ExternalSymbolPseudoSourceValue>(PSV))
- if (StringRef(EPSV->getSymbol()) == "__stack_pointer") {
- StackPointer = true;
- }
- }
- }
} else if (MI.hasOrderedMemoryRef()) {
switch (MI.getOpcode()) {
- case WebAssembly::DIV_S_I32: case WebAssembly::DIV_S_I64:
- case WebAssembly::REM_S_I32: case WebAssembly::REM_S_I64:
- case WebAssembly::DIV_U_I32: case WebAssembly::DIV_U_I64:
- case WebAssembly::REM_U_I32: case WebAssembly::REM_U_I64:
- case WebAssembly::I32_TRUNC_S_F32: case WebAssembly::I64_TRUNC_S_F32:
- case WebAssembly::I32_TRUNC_S_F64: case WebAssembly::I64_TRUNC_S_F64:
- case WebAssembly::I32_TRUNC_U_F32: case WebAssembly::I64_TRUNC_U_F32:
- case WebAssembly::I32_TRUNC_U_F64: case WebAssembly::I64_TRUNC_U_F64:
+ case WebAssembly::DIV_S_I32:
+ case WebAssembly::DIV_S_I64:
+ case WebAssembly::REM_S_I32:
+ case WebAssembly::REM_S_I64:
+ case WebAssembly::DIV_U_I32:
+ case WebAssembly::DIV_U_I64:
+ case WebAssembly::REM_U_I32:
+ case WebAssembly::REM_U_I64:
+ case WebAssembly::I32_TRUNC_S_F32:
+ case WebAssembly::I64_TRUNC_S_F32:
+ case WebAssembly::I32_TRUNC_S_F64:
+ case WebAssembly::I64_TRUNC_S_F64:
+ case WebAssembly::I32_TRUNC_U_F32:
+ case WebAssembly::I64_TRUNC_U_F32:
+ case WebAssembly::I32_TRUNC_U_F64:
+ case WebAssembly::I64_TRUNC_U_F64:
// These instruction have hasUnmodeledSideEffects() returning true
// because they trap on overflow and invalid so they can't be arbitrarily
// moved, however hasOrderedMemoryRef() interprets this plus their lack
@@ -214,14 +219,22 @@ static void Query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read,
// Check for side effects.
if (MI.hasUnmodeledSideEffects()) {
switch (MI.getOpcode()) {
- case WebAssembly::DIV_S_I32: case WebAssembly::DIV_S_I64:
- case WebAssembly::REM_S_I32: case WebAssembly::REM_S_I64:
- case WebAssembly::DIV_U_I32: case WebAssembly::DIV_U_I64:
- case WebAssembly::REM_U_I32: case WebAssembly::REM_U_I64:
- case WebAssembly::I32_TRUNC_S_F32: case WebAssembly::I64_TRUNC_S_F32:
- case WebAssembly::I32_TRUNC_S_F64: case WebAssembly::I64_TRUNC_S_F64:
- case WebAssembly::I32_TRUNC_U_F32: case WebAssembly::I64_TRUNC_U_F32:
- case WebAssembly::I32_TRUNC_U_F64: case WebAssembly::I64_TRUNC_U_F64:
+ case WebAssembly::DIV_S_I32:
+ case WebAssembly::DIV_S_I64:
+ case WebAssembly::REM_S_I32:
+ case WebAssembly::REM_S_I64:
+ case WebAssembly::DIV_U_I32:
+ case WebAssembly::DIV_U_I64:
+ case WebAssembly::REM_U_I32:
+ case WebAssembly::REM_U_I64:
+ case WebAssembly::I32_TRUNC_S_F32:
+ case WebAssembly::I64_TRUNC_S_F32:
+ case WebAssembly::I32_TRUNC_S_F64:
+ case WebAssembly::I64_TRUNC_S_F64:
+ case WebAssembly::I32_TRUNC_U_F32:
+ case WebAssembly::I64_TRUNC_U_F32:
+ case WebAssembly::I32_TRUNC_U_F64:
+ case WebAssembly::I64_TRUNC_U_F64:
// These instructions have hasUnmodeledSideEffects() returning true
// because they trap on overflow and invalid so they can't be arbitrarily
// moved, however in the specific case of register stackifying, it is safe
@@ -233,22 +246,15 @@ static void Query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read,
}
}
+ // Check for writes to __stack_pointer global.
+ if (MI.getOpcode() == WebAssembly::GLOBAL_SET_I32 &&
+ strcmp(MI.getOperand(0).getSymbolName(), "__stack_pointer") == 0)
+ StackPointer = true;
+
// Analyze calls.
if (MI.isCall()) {
- switch (MI.getOpcode()) {
- case WebAssembly::CALL_VOID:
- case WebAssembly::CALL_INDIRECT_VOID:
- QueryCallee(MI, 0, Read, Write, Effects, StackPointer);
- break;
- case WebAssembly::CALL_I32: case WebAssembly::CALL_I64:
- case WebAssembly::CALL_F32: case WebAssembly::CALL_F64:
- case WebAssembly::CALL_INDIRECT_I32: case WebAssembly::CALL_INDIRECT_I64:
- case WebAssembly::CALL_INDIRECT_F32: case WebAssembly::CALL_INDIRECT_F64:
- QueryCallee(MI, 1, Read, Write, Effects, StackPointer);
- break;
- default:
- llvm_unreachable("unexpected call opcode");
- }
+ unsigned CalleeOpNo = WebAssembly::getCalleeOpNo(MI);
+ QueryCallee(MI, CalleeOpNo, Read, Write, Effects, StackPointer);
}
}
@@ -263,8 +269,7 @@ static bool ShouldRematerialize(const MachineInstr &Def, AliasAnalysis &AA,
// LiveIntervals to handle complex cases.
static MachineInstr *GetVRegDef(unsigned Reg, const MachineInstr *Insert,
const MachineRegisterInfo &MRI,
- const LiveIntervals &LIS)
-{
+ const LiveIntervals &LIS) {
// Most registers are in SSA form here so we try a quick MRI query first.
if (MachineInstr *Def = MRI.getUniqueVRegDef(Reg))
return Def;
@@ -280,17 +285,16 @@ 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) {
+static bool HasOneUse(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))
return true;
bool HasOne = false;
const LiveInterval &LI = LIS.getInterval(Reg);
- const VNInfo *DefVNI = LI.getVNInfoAt(
- LIS.getInstructionIndex(*Def).getRegSlot());
+ const VNInfo *DefVNI =
+ LI.getVNInfoAt(LIS.getInstructionIndex(*Def).getRegSlot());
assert(DefVNI);
for (auto &I : MRI.use_nodbg_operands(Reg)) {
const auto &Result = LI.Query(LIS.getInstructionIndex(*I.getParent()));
@@ -403,7 +407,6 @@ static bool OneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse,
if (UseVNI != OneUseVNI)
continue;
- const MachineInstr *OneUseInst = OneUse.getParent();
if (UseInst == OneUseInst) {
// Another use in the same instruction. We need to ensure that the one
// selected use happens "before" it.
@@ -415,8 +418,8 @@ static bool OneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse,
// Actually, dominating is over-conservative. Test that the use would
// happen after the one selected use in the stack evaluation order.
//
- // This is needed as a consequence of using implicit get_locals for
- // uses and implicit set_locals for defs.
+ // This is needed as a consequence of using implicit local.gets for
+ // uses and implicit local.sets for defs.
if (UseInst->getDesc().getNumDefs() == 0)
return false;
const MachineOperand &MO = UseInst->getOperand(0);
@@ -426,8 +429,8 @@ static bool OneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse,
if (!TargetRegisterInfo::isVirtualRegister(DefReg) ||
!MFI.isVRegStackified(DefReg))
return false;
- assert(MRI.hasOneUse(DefReg));
- const MachineOperand &NewUse = *MRI.use_begin(DefReg);
+ assert(MRI.hasOneNonDBGUse(DefReg));
+ const MachineOperand &NewUse = *MRI.use_nodbg_begin(DefReg);
const MachineInstr *NewUseInst = NewUse.getParent();
if (NewUseInst == OneUseInst) {
if (&OneUse > &NewUse)
@@ -459,22 +462,23 @@ static unsigned GetTeeOpcode(const TargetRegisterClass *RC) {
// Shrink LI to its uses, cleaning up LI.
static void ShrinkToUses(LiveInterval &LI, LiveIntervals &LIS) {
if (LIS.shrinkToUses(&LI)) {
- SmallVector<LiveInterval*, 4> SplitLIs;
+ SmallVector<LiveInterval *, 4> SplitLIs;
LIS.splitSeparateComponents(LI, SplitLIs);
}
}
/// A single-use def in the same block with no intervening memory or register
/// dependencies; move the def down and nest it with the current instruction.
-static MachineInstr *MoveForSingleUse(unsigned Reg, MachineOperand& Op,
- MachineInstr *Def,
- MachineBasicBlock &MBB,
+static MachineInstr *MoveForSingleUse(unsigned Reg, MachineOperand &Op,
+ MachineInstr *Def, MachineBasicBlock &MBB,
MachineInstr *Insert, LiveIntervals &LIS,
WebAssemblyFunctionInfo &MFI,
MachineRegisterInfo &MRI) {
LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump());
+ WebAssemblyDebugValueManager DefDIs(Def);
MBB.splice(Insert, &MBB, Def);
+ DefDIs.move(Insert);
LIS.handleMove(*Def);
if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) {
@@ -499,6 +503,8 @@ static MachineInstr *MoveForSingleUse(unsigned Reg, MachineOperand& Op,
MFI.stackifyVReg(NewReg);
+ DefDIs.updateReg(NewReg);
+
LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump());
}
@@ -516,6 +522,8 @@ static MachineInstr *RematerializeCheapDef(
LLVM_DEBUG(dbgs() << "Rematerializing cheap def: "; Def.dump());
LLVM_DEBUG(dbgs() << " - for use in "; Op.getParent()->dump());
+ WebAssemblyDebugValueManager DefDIs(&Def);
+
unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg));
TII->reMaterialize(MBB, Insert, NewReg, 0, Def, *TRI);
Op.setReg(NewReg);
@@ -536,6 +544,7 @@ 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();
@@ -543,6 +552,11 @@ static MachineInstr *RematerializeCheapDef(
LIS.removeInterval(Reg);
LIS.RemoveMachineInstrFromMaps(Def);
Def.eraseFromParent();
+
+ DefDIs.move(&*Insert);
+ DefDIs.updateReg(NewReg);
+ } else {
+ DefDIs.clone(&*Insert, NewReg);
}
return Clone;
@@ -566,7 +580,7 @@ static MachineInstr *RematerializeCheapDef(
/// INST ..., Reg, ...
/// INST ..., Reg, ...
///
-/// with DefReg and TeeReg stackified. This eliminates a get_local from the
+/// with DefReg and TeeReg stackified. This eliminates a local.get from the
/// resulting code.
static MachineInstr *MoveAndTeeForMultiUse(
unsigned Reg, MachineOperand &Op, MachineInstr *Def, MachineBasicBlock &MBB,
@@ -574,6 +588,8 @@ static MachineInstr *MoveAndTeeForMultiUse(
MachineRegisterInfo &MRI, const WebAssemblyInstrInfo *TII) {
LLVM_DEBUG(dbgs() << "Move and tee for multi-use:"; Def->dump());
+ WebAssemblyDebugValueManager DefDIs(Def);
+
// Move Def into place.
MBB.splice(Insert, &MBB, Def);
LIS.handleMove(*Def);
@@ -592,6 +608,8 @@ static MachineInstr *MoveAndTeeForMultiUse(
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);
@@ -608,6 +626,9 @@ static MachineInstr *MoveAndTeeForMultiUse(
ImposeStackOrdering(Def);
ImposeStackOrdering(Tee);
+ DefDIs.clone(Tee, DefReg);
+ DefDIs.clone(Insert, TeeReg);
+
LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump());
LLVM_DEBUG(dbgs() << " - Tee instruction: "; Tee->dump());
return Def;
@@ -672,8 +693,8 @@ public:
/// operand in the tree that we haven't visited yet. Moving a definition of
/// Reg to a point in the tree after that would change its value.
///
- /// This is needed as a consequence of using implicit get_locals for
- /// uses and implicit set_locals for defs.
+ /// This is needed as a consequence of using implicit local.gets for
+ /// uses and implicit local.sets for defs.
bool IsOnStack(unsigned Reg) const {
for (const RangeTy &Range : Worklist)
for (const MachineOperand &MO : Range)
@@ -687,9 +708,9 @@ public:
/// tried for the current instruction and didn't work.
class CommutingState {
/// There are effectively three states: the initial state where we haven't
- /// started commuting anything and we don't know anything yet, the tenative
+ /// started commuting anything and we don't know anything yet, the tentative
/// state where we've commuted the operands of the current instruction and are
- /// revisting it, and the declined state where we've reverted the operands
+ /// revisiting it, and the declined state where we've reverted the operands
/// back to their original order and will no longer commute it further.
bool TentativelyCommuting;
bool Declined;
@@ -831,7 +852,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) {
// to a constant 0 so that the def is explicit, and the push/pop
// correspondence is maintained.
if (Insert->getOpcode() == TargetOpcode::IMPLICIT_DEF)
- ConvertImplicitDefToConstZero(Insert, MRI, TII, MF);
+ ConvertImplicitDefToConstZero(Insert, MRI, TII, MF, LIS);
// We stackified an operand. Add the defining instruction's operands to
// the worklist stack now to continue to build an ever deeper tree.
diff --git a/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp b/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
index b6481ac2d4ae..1f0870865b06 100644
--- a/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp
@@ -22,9 +22,9 @@
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
-#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
diff --git a/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td b/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td
index 29f42b96b249..a7c3d177724d 100644
--- a/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td
+++ b/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td
@@ -63,6 +63,6 @@ def I32 : WebAssemblyRegClass<[i32], 32, (add FP32, SP32, I32_0)>;
def I64 : WebAssemblyRegClass<[i64], 64, (add FP64, SP64, I64_0)>;
def F32 : WebAssemblyRegClass<[f32], 32, (add F32_0)>;
def F64 : WebAssemblyRegClass<[f64], 64, (add F64_0)>;
-def V128 : WebAssemblyRegClass<[v4f32, v4i32, v16i8, v8i16], 128, (add V128_0)>;
+def V128 : WebAssemblyRegClass<[v4f32, v2f64, v2i64, v4i32, v16i8, v8i16], 128,
+ (add V128_0)>;
def EXCEPT_REF : WebAssemblyRegClass<[ExceptRef], 0, (add EXCEPT_REF_0)>;
-
diff --git a/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp b/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp
index f432b367d156..e5a3e47a3bcd 100644
--- a/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp
@@ -54,8 +54,8 @@ private:
char WebAssemblyReplacePhysRegs::ID = 0;
INITIALIZE_PASS(WebAssemblyReplacePhysRegs, DEBUG_TYPE,
- "Replace physical registers with virtual registers",
- false, false)
+ "Replace physical registers with virtual registers", false,
+ false)
FunctionPass *llvm::createWebAssemblyReplacePhysRegs() {
return new WebAssemblyReplacePhysRegs();
@@ -86,7 +86,7 @@ bool WebAssemblyReplacePhysRegs::runOnMachineFunction(MachineFunction &MF) {
// Replace explicit uses of the physical register with a virtual register.
const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(PReg);
unsigned VReg = WebAssembly::NoRegister;
- for (auto I = MRI.reg_begin(PReg), E = MRI.reg_end(); I != E; ) {
+ for (auto I = MRI.reg_begin(PReg), E = MRI.reg_end(); I != E;) {
MachineOperand &MO = *I++;
if (!MO.isImplicit()) {
if (VReg == WebAssembly::NoRegister)
diff --git a/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp b/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
index fe8a5e4c06f1..6cf81a9d77b3 100644
--- a/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp
@@ -88,7 +88,6 @@ enum RuntimeLibcallSignature {
unsupported
};
-
struct RuntimeLibcallSignatureTable {
std::vector<RuntimeLibcallSignature> Table;
@@ -486,18 +485,17 @@ struct StaticLibcallNameMap {
} // end anonymous namespace
-
-
-void llvm::GetSignature(const WebAssemblySubtarget &Subtarget,
- RTLIB::Libcall LC, SmallVectorImpl<wasm::ValType> &Rets,
- SmallVectorImpl<wasm::ValType> &Params) {
+void llvm::GetLibcallSignature(const WebAssemblySubtarget &Subtarget,
+ RTLIB::Libcall LC,
+ SmallVectorImpl<wasm::ValType> &Rets,
+ SmallVectorImpl<wasm::ValType> &Params) {
assert(Rets.empty());
assert(Params.empty());
wasm::ValType iPTR =
Subtarget.hasAddr64() ? wasm::ValType::I64 : wasm::ValType::I32;
- auto& Table = RuntimeLibcallSignatures->Table;
+ auto &Table = RuntimeLibcallSignatures->Table;
switch (Table[LC]) {
case func:
break;
@@ -834,11 +832,12 @@ void llvm::GetSignature(const WebAssemblySubtarget &Subtarget,
static ManagedStatic<StaticLibcallNameMap> LibcallNameMap;
// TODO: If the RTLIB::Libcall-taking flavor of GetSignature remains unsed
// other than here, just roll its logic into this version.
-void llvm::GetSignature(const WebAssemblySubtarget &Subtarget, const char *Name,
- SmallVectorImpl<wasm::ValType> &Rets,
- SmallVectorImpl<wasm::ValType> &Params) {
- auto& Map = LibcallNameMap->Map;
+void llvm::GetLibcallSignature(const WebAssemblySubtarget &Subtarget,
+ const char *Name,
+ SmallVectorImpl<wasm::ValType> &Rets,
+ SmallVectorImpl<wasm::ValType> &Params) {
+ auto &Map = LibcallNameMap->Map;
auto val = Map.find(Name);
assert(val != Map.end() && "unexpected runtime library name");
- return GetSignature(Subtarget, val->second, Rets, Params);
+ return GetLibcallSignature(Subtarget, val->second, Rets, Params);
}
diff --git a/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h b/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h
index 2ba65ff5b716..7fa70bea96de 100644
--- a/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h
+++ b/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h
@@ -23,14 +23,15 @@ namespace llvm {
class WebAssemblySubtarget;
-extern void GetSignature(const WebAssemblySubtarget &Subtarget,
- RTLIB::Libcall LC,
- SmallVectorImpl<wasm::ValType> &Rets,
- SmallVectorImpl<wasm::ValType> &Params);
+extern void GetLibcallSignature(const WebAssemblySubtarget &Subtarget,
+ RTLIB::Libcall LC,
+ SmallVectorImpl<wasm::ValType> &Rets,
+ SmallVectorImpl<wasm::ValType> &Params);
-extern void GetSignature(const WebAssemblySubtarget &Subtarget,
- const char *Name, SmallVectorImpl<wasm::ValType> &Rets,
- SmallVectorImpl<wasm::ValType> &Params);
+extern void GetLibcallSignature(const WebAssemblySubtarget &Subtarget,
+ const char *Name,
+ SmallVectorImpl<wasm::ValType> &Rets,
+ SmallVectorImpl<wasm::ValType> &Params);
} // end namespace llvm
diff --git a/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp b/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
index 14221993603a..c95af88c6f43 100644
--- a/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
+++ b/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
@@ -60,8 +60,7 @@ static void RewriteP2Align(MachineInstr &MI, unsigned OperandNo) {
assert(MI.hasOneMemOperand() &&
"Load and store instructions have exactly one mem operand");
assert((*MI.memoperands_begin())->getSize() ==
- (UINT64_C(1)
- << WebAssembly::GetDefaultP2Align(MI.getOpcode())) &&
+ (UINT64_C(1) << WebAssembly::GetDefaultP2Align(MI.getOpcode())) &&
"Default p2align value should be natural");
assert(MI.getDesc().OpInfo[OperandNo].OperandType ==
WebAssembly::OPERAND_P2ALIGN &&
@@ -69,8 +68,8 @@ static void RewriteP2Align(MachineInstr &MI, unsigned OperandNo) {
uint64_t P2Align = Log2_64((*MI.memoperands_begin())->getAlignment());
// WebAssembly does not currently support supernatural alignment.
- P2Align = std::min(
- P2Align, uint64_t(WebAssembly::GetDefaultP2Align(MI.getOpcode())));
+ P2Align = std::min(P2Align,
+ uint64_t(WebAssembly::GetDefaultP2Align(MI.getOpcode())));
MI.getOperand(OperandNo).setImm(P2Align);
}
@@ -90,6 +89,12 @@ bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
case WebAssembly::LOAD_I64:
case WebAssembly::LOAD_F32:
case WebAssembly::LOAD_F64:
+ case WebAssembly::LOAD_v16i8:
+ case WebAssembly::LOAD_v8i16:
+ case WebAssembly::LOAD_v4i32:
+ case WebAssembly::LOAD_v2i64:
+ case WebAssembly::LOAD_v4f32:
+ case WebAssembly::LOAD_v2f64:
case WebAssembly::LOAD8_S_I32:
case WebAssembly::LOAD8_U_I32:
case WebAssembly::LOAD16_S_I32:
@@ -119,6 +124,8 @@ bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
case WebAssembly::ATOMIC_RMW8_U_XOR_I64:
case WebAssembly::ATOMIC_RMW8_U_XCHG_I32:
case WebAssembly::ATOMIC_RMW8_U_XCHG_I64:
+ case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I32:
+ case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I64:
case WebAssembly::ATOMIC_RMW16_U_ADD_I32:
case WebAssembly::ATOMIC_RMW16_U_ADD_I64:
case WebAssembly::ATOMIC_RMW16_U_SUB_I32:
@@ -131,6 +138,8 @@ bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
case WebAssembly::ATOMIC_RMW16_U_XOR_I64:
case WebAssembly::ATOMIC_RMW16_U_XCHG_I32:
case WebAssembly::ATOMIC_RMW16_U_XCHG_I64:
+ case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I32:
+ case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I64:
case WebAssembly::ATOMIC_RMW_ADD_I32:
case WebAssembly::ATOMIC_RMW32_U_ADD_I64:
case WebAssembly::ATOMIC_RMW_SUB_I32:
@@ -143,18 +152,30 @@ bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
case WebAssembly::ATOMIC_RMW32_U_XOR_I64:
case WebAssembly::ATOMIC_RMW_XCHG_I32:
case WebAssembly::ATOMIC_RMW32_U_XCHG_I64:
+ case WebAssembly::ATOMIC_RMW_CMPXCHG_I32:
+ case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64:
case WebAssembly::ATOMIC_RMW_ADD_I64:
case WebAssembly::ATOMIC_RMW_SUB_I64:
case WebAssembly::ATOMIC_RMW_AND_I64:
case WebAssembly::ATOMIC_RMW_OR_I64:
case WebAssembly::ATOMIC_RMW_XOR_I64:
case WebAssembly::ATOMIC_RMW_XCHG_I64:
+ case WebAssembly::ATOMIC_RMW_CMPXCHG_I64:
+ case WebAssembly::ATOMIC_NOTIFY:
+ case WebAssembly::ATOMIC_WAIT_I32:
+ case WebAssembly::ATOMIC_WAIT_I64:
RewriteP2Align(MI, WebAssembly::LoadP2AlignOperandNo);
break;
case WebAssembly::STORE_I32:
case WebAssembly::STORE_I64:
case WebAssembly::STORE_F32:
case WebAssembly::STORE_F64:
+ case WebAssembly::STORE_v16i8:
+ case WebAssembly::STORE_v8i16:
+ case WebAssembly::STORE_v4i32:
+ case WebAssembly::STORE_v2i64:
+ case WebAssembly::STORE_v4f32:
+ case WebAssembly::STORE_v2f64:
case WebAssembly::STORE8_I32:
case WebAssembly::STORE16_I32:
case WebAssembly::STORE8_I64:
diff --git a/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
index d6af0fb219d7..98133e2153a0 100644
--- a/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
+++ b/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
@@ -40,10 +40,9 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
const std::string &CPU,
const std::string &FS,
const TargetMachine &TM)
- : WebAssemblyGenSubtargetInfo(TT, CPU, FS), HasSIMD128(false),
- HasAtomics(false), HasNontrappingFPToInt(false), HasSignExt(false),
- HasExceptionHandling(false), CPUString(CPU), TargetTriple(TT),
- FrameLowering(), InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
+ : WebAssemblyGenSubtargetInfo(TT, CPU, FS), CPUString(CPU),
+ TargetTriple(TT), FrameLowering(),
+ InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
TLInfo(TM, *this) {}
bool WebAssemblySubtarget::enableMachineScheduler() const {
diff --git a/lib/Target/WebAssembly/WebAssemblySubtarget.h b/lib/Target/WebAssembly/WebAssemblySubtarget.h
index b170dbff3b32..0a0c04609ac4 100644
--- a/lib/Target/WebAssembly/WebAssemblySubtarget.h
+++ b/lib/Target/WebAssembly/WebAssemblySubtarget.h
@@ -29,11 +29,16 @@
namespace llvm {
class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
- bool HasSIMD128;
- bool HasAtomics;
- bool HasNontrappingFPToInt;
- bool HasSignExt;
- bool HasExceptionHandling;
+ enum SIMDEnum {
+ NoSIMD,
+ SIMD128,
+ UnimplementedSIMD128,
+ } SIMDLevel = NoSIMD;
+
+ bool HasAtomics = false;
+ bool HasNontrappingFPToInt = false;
+ bool HasSignExt = false;
+ bool HasExceptionHandling = false;
/// String name of used CPU.
std::string CPUString;
@@ -77,7 +82,10 @@ public:
// Predicates used by WebAssemblyInstrInfo.td.
bool hasAddr64() const { return TargetTriple.isArch64Bit(); }
- bool hasSIMD128() const { return HasSIMD128; }
+ bool hasSIMD128() const { return SIMDLevel >= SIMD128; }
+ bool hasUnimplementedSIMD128() const {
+ return SIMDLevel >= UnimplementedSIMD128;
+ }
bool hasAtomics() const { return HasAtomics; }
bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
bool hasSignExt() const { return HasSignExt; }
diff --git a/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 7c10f022cbbc..3bf8dd40892c 100644
--- a/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -58,10 +58,11 @@ extern "C" void LLVMInitializeWebAssemblyTarget() {
initializeOptimizeReturnedPass(PR);
initializeWebAssemblyArgumentMovePass(PR);
initializeWebAssemblySetP2AlignOperandsPass(PR);
+ initializeWebAssemblyEHRestoreStackPointerPass(PR);
initializeWebAssemblyReplacePhysRegsPass(PR);
initializeWebAssemblyPrepareForLiveIntervalsPass(PR);
initializeWebAssemblyOptimizeLiveIntervalsPass(PR);
- initializeWebAssemblyStoreResultsPass(PR);
+ initializeWebAssemblyMemIntrinsicResultsPass(PR);
initializeWebAssemblyRegStackifyPass(PR);
initializeWebAssemblyRegColoringPass(PR);
initializeWebAssemblyExplicitLocalsPass(PR);
@@ -81,8 +82,12 @@ extern "C" void LLVMInitializeWebAssemblyTarget() {
//===----------------------------------------------------------------------===//
static Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM) {
- if (!RM.hasValue())
- return Reloc::PIC_;
+ if (!RM.hasValue()) {
+ // Default to static relocation model. This should always be more optimial
+ // than PIC since the static linker can determine all global addresses and
+ // assume direct function calls.
+ return Reloc::Static;
+ }
return *RM;
}
@@ -96,7 +101,7 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
TT.isArch64Bit() ? "e-m:e-p:64:64-i64:64-n32:64-S128"
: "e-m:e-p:32:32-i64:64-n32:64-S128",
TT, CPU, FS, Options, getEffectiveRelocModel(RM),
- CM ? *CM : CodeModel::Large, OL),
+ getEffectiveCodeModel(CM, CodeModel::Large), OL),
TLOF(new WebAssemblyTargetObjectFile()) {
// WebAssembly type-checks instructions, but a noreturn function with a return
// type that doesn't match the context will cause a check failure. So we lower
@@ -149,7 +154,7 @@ class StripThreadLocal final : public ModulePass {
// pass just converts all GlobalVariables to NotThreadLocal
static char ID;
- public:
+public:
StripThreadLocal() : ModulePass(ID) {}
bool runOnModule(Module &M) override {
for (auto &GV : M.globals())
@@ -280,6 +285,9 @@ void WebAssemblyPassConfig::addPostRegAlloc() {
void WebAssemblyPassConfig::addPreEmitPass() {
TargetPassConfig::addPreEmitPass();
+ // Restore __stack_pointer global after an exception is thrown.
+ addPass(createWebAssemblyEHRestoreStackPointer());
+
// Now that we have a prologue and epilogue and all frame indices are
// rewritten, eliminate SP and FP. This allows them to be stackified,
// colored, and numbered with the rest of the registers.
@@ -290,6 +298,12 @@ void WebAssemblyPassConfig::addPreEmitPass() {
// order of the arguments.
addPass(createWebAssemblyCallIndirectFixup());
+ // Eliminate multiple-entry loops.
+ addPass(createWebAssemblyFixIrreducibleControlFlow());
+
+ // Do various transformations for exception handling.
+ addPass(createWebAssemblyLateEHPrepare());
+
if (getOptLevel() != CodeGenOpt::None) {
// LiveIntervals isn't commonly run this late. Re-establish preconditions.
addPass(createWebAssemblyPrepareForLiveIntervals());
@@ -297,13 +311,14 @@ void WebAssemblyPassConfig::addPreEmitPass() {
// Depend on LiveIntervals and perform some optimizations on it.
addPass(createWebAssemblyOptimizeLiveIntervals());
- // Prepare store instructions for register stackifying.
- addPass(createWebAssemblyStoreResults());
+ // Prepare memory intrinsic calls for register stackifying.
+ addPass(createWebAssemblyMemIntrinsicResults());
// Mark registers as representing wasm's value stack. This is a key
// code-compression technique in WebAssembly. We run this pass (and
- // StoreResults above) very late, so that it sees as much code as possible,
- // including code emitted by PEI and expanded by late tail duplication.
+ // MemIntrinsicResults above) very late, so that it sees as much code as
+ // possible, including code emitted by PEI and expanded by late tail
+ // duplication.
addPass(createWebAssemblyRegStackify());
// Run the register coloring pass to reduce the total number of registers.
@@ -312,17 +327,9 @@ void WebAssemblyPassConfig::addPreEmitPass() {
addPass(createWebAssemblyRegColoring());
}
- // Eliminate multiple-entry loops. Do this before inserting explicit get_local
- // and set_local operators because we create a new variable that we want
- // converted into a local.
- addPass(createWebAssemblyFixIrreducibleControlFlow());
-
- // Insert explicit get_local and set_local operators.
+ // Insert explicit local.get and local.set operators.
addPass(createWebAssemblyExplicitLocals());
- // Do various transformations for exception handling
- addPass(createWebAssemblyLateEHPrepare());
-
// Sort the blocks of the CFG into topological order, a prerequisite for
// BLOCK and LOOP markers.
addPass(createWebAssemblyCFGSort());
diff --git a/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
index 5944cea5abd1..ada6fb9a96d7 100644
--- a/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -27,14 +27,26 @@ const char *const WebAssembly::PersonalityWrapperFn =
bool WebAssembly::isArgument(const MachineInstr &MI) {
switch (MI.getOpcode()) {
- case WebAssembly::ARGUMENT_I32:
- case WebAssembly::ARGUMENT_I64:
- case WebAssembly::ARGUMENT_F32:
- case WebAssembly::ARGUMENT_F64:
+ case WebAssembly::ARGUMENT_i32:
+ case WebAssembly::ARGUMENT_i32_S:
+ case WebAssembly::ARGUMENT_i64:
+ case WebAssembly::ARGUMENT_i64_S:
+ case WebAssembly::ARGUMENT_f32:
+ case WebAssembly::ARGUMENT_f32_S:
+ case WebAssembly::ARGUMENT_f64:
+ case WebAssembly::ARGUMENT_f64_S:
case WebAssembly::ARGUMENT_v16i8:
+ case WebAssembly::ARGUMENT_v16i8_S:
case WebAssembly::ARGUMENT_v8i16:
+ case WebAssembly::ARGUMENT_v8i16_S:
case WebAssembly::ARGUMENT_v4i32:
+ case WebAssembly::ARGUMENT_v4i32_S:
+ case WebAssembly::ARGUMENT_v2i64:
+ case WebAssembly::ARGUMENT_v2i64_S:
case WebAssembly::ARGUMENT_v4f32:
+ case WebAssembly::ARGUMENT_v4f32_S:
+ case WebAssembly::ARGUMENT_v2f64:
+ case WebAssembly::ARGUMENT_v2f64_S:
return true;
default:
return false;
@@ -44,9 +56,15 @@ bool WebAssembly::isArgument(const MachineInstr &MI) {
bool WebAssembly::isCopy(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::COPY_I32:
+ case WebAssembly::COPY_I32_S:
case WebAssembly::COPY_I64:
+ case WebAssembly::COPY_I64_S:
case WebAssembly::COPY_F32:
+ case WebAssembly::COPY_F32_S:
case WebAssembly::COPY_F64:
+ case WebAssembly::COPY_F64_S:
+ case WebAssembly::COPY_V128:
+ case WebAssembly::COPY_V128_S:
return true;
default:
return false;
@@ -56,9 +74,15 @@ bool WebAssembly::isCopy(const MachineInstr &MI) {
bool WebAssembly::isTee(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::TEE_I32:
+ case WebAssembly::TEE_I32_S:
case WebAssembly::TEE_I64:
+ case WebAssembly::TEE_I64_S:
case WebAssembly::TEE_F32:
+ case WebAssembly::TEE_F32_S:
case WebAssembly::TEE_F64:
+ case WebAssembly::TEE_F64_S:
+ case WebAssembly::TEE_V128:
+ case WebAssembly::TEE_V128_S:
return true;
default:
return false;
@@ -81,15 +105,29 @@ bool WebAssembly::isChild(const MachineInstr &MI,
bool WebAssembly::isCallDirect(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::CALL_VOID:
+ case WebAssembly::CALL_VOID_S:
case WebAssembly::CALL_I32:
+ case WebAssembly::CALL_I32_S:
case WebAssembly::CALL_I64:
+ case WebAssembly::CALL_I64_S:
case WebAssembly::CALL_F32:
+ case WebAssembly::CALL_F32_S:
case WebAssembly::CALL_F64:
+ case WebAssembly::CALL_F64_S:
case WebAssembly::CALL_v16i8:
+ case WebAssembly::CALL_v16i8_S:
case WebAssembly::CALL_v8i16:
+ case WebAssembly::CALL_v8i16_S:
case WebAssembly::CALL_v4i32:
+ case WebAssembly::CALL_v4i32_S:
+ case WebAssembly::CALL_v2i64:
+ case WebAssembly::CALL_v2i64_S:
case WebAssembly::CALL_v4f32:
+ case WebAssembly::CALL_v4f32_S:
+ case WebAssembly::CALL_v2f64:
+ case WebAssembly::CALL_v2f64_S:
case WebAssembly::CALL_EXCEPT_REF:
+ case WebAssembly::CALL_EXCEPT_REF_S:
return true;
default:
return false;
@@ -99,15 +137,29 @@ bool WebAssembly::isCallDirect(const MachineInstr &MI) {
bool WebAssembly::isCallIndirect(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::CALL_INDIRECT_VOID:
+ case WebAssembly::CALL_INDIRECT_VOID_S:
case WebAssembly::CALL_INDIRECT_I32:
+ case WebAssembly::CALL_INDIRECT_I32_S:
case WebAssembly::CALL_INDIRECT_I64:
+ case WebAssembly::CALL_INDIRECT_I64_S:
case WebAssembly::CALL_INDIRECT_F32:
+ case WebAssembly::CALL_INDIRECT_F32_S:
case WebAssembly::CALL_INDIRECT_F64:
+ case WebAssembly::CALL_INDIRECT_F64_S:
case WebAssembly::CALL_INDIRECT_v16i8:
+ case WebAssembly::CALL_INDIRECT_v16i8_S:
case WebAssembly::CALL_INDIRECT_v8i16:
+ case WebAssembly::CALL_INDIRECT_v8i16_S:
case WebAssembly::CALL_INDIRECT_v4i32:
+ case WebAssembly::CALL_INDIRECT_v4i32_S:
+ case WebAssembly::CALL_INDIRECT_v2i64:
+ case WebAssembly::CALL_INDIRECT_v2i64_S:
case WebAssembly::CALL_INDIRECT_v4f32:
+ case WebAssembly::CALL_INDIRECT_v4f32_S:
+ case WebAssembly::CALL_INDIRECT_v2f64:
+ case WebAssembly::CALL_INDIRECT_v2f64_S:
case WebAssembly::CALL_INDIRECT_EXCEPT_REF:
+ case WebAssembly::CALL_INDIRECT_EXCEPT_REF_S:
return true;
default:
return false;
@@ -117,18 +169,54 @@ bool WebAssembly::isCallIndirect(const MachineInstr &MI) {
unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::CALL_VOID:
+ case WebAssembly::CALL_VOID_S:
case WebAssembly::CALL_INDIRECT_VOID:
+ case WebAssembly::CALL_INDIRECT_VOID_S:
return 0;
case WebAssembly::CALL_I32:
+ case WebAssembly::CALL_I32_S:
case WebAssembly::CALL_I64:
+ case WebAssembly::CALL_I64_S:
case WebAssembly::CALL_F32:
+ case WebAssembly::CALL_F32_S:
case WebAssembly::CALL_F64:
+ case WebAssembly::CALL_F64_S:
+ case WebAssembly::CALL_v16i8:
+ case WebAssembly::CALL_v16i8_S:
+ case WebAssembly::CALL_v8i16:
+ case WebAssembly::CALL_v8i16_S:
+ case WebAssembly::CALL_v4i32:
+ case WebAssembly::CALL_v4i32_S:
+ case WebAssembly::CALL_v2i64:
+ case WebAssembly::CALL_v2i64_S:
+ case WebAssembly::CALL_v4f32:
+ case WebAssembly::CALL_v4f32_S:
+ case WebAssembly::CALL_v2f64:
+ case WebAssembly::CALL_v2f64_S:
case WebAssembly::CALL_EXCEPT_REF:
+ case WebAssembly::CALL_EXCEPT_REF_S:
case WebAssembly::CALL_INDIRECT_I32:
+ case WebAssembly::CALL_INDIRECT_I32_S:
case WebAssembly::CALL_INDIRECT_I64:
+ case WebAssembly::CALL_INDIRECT_I64_S:
case WebAssembly::CALL_INDIRECT_F32:
+ case WebAssembly::CALL_INDIRECT_F32_S:
case WebAssembly::CALL_INDIRECT_F64:
+ case WebAssembly::CALL_INDIRECT_F64_S:
+ case WebAssembly::CALL_INDIRECT_v16i8:
+ case WebAssembly::CALL_INDIRECT_v16i8_S:
+ case WebAssembly::CALL_INDIRECT_v8i16:
+ case WebAssembly::CALL_INDIRECT_v8i16_S:
+ case WebAssembly::CALL_INDIRECT_v4i32:
+ case WebAssembly::CALL_INDIRECT_v4i32_S:
+ case WebAssembly::CALL_INDIRECT_v2i64:
+ case WebAssembly::CALL_INDIRECT_v2i64_S:
+ case WebAssembly::CALL_INDIRECT_v4f32:
+ case WebAssembly::CALL_INDIRECT_v4f32_S:
+ case WebAssembly::CALL_INDIRECT_v2f64:
+ case WebAssembly::CALL_INDIRECT_v2f64_S:
case WebAssembly::CALL_INDIRECT_EXCEPT_REF:
+ case WebAssembly::CALL_INDIRECT_EXCEPT_REF_S:
return 1;
default:
llvm_unreachable("Not a call instruction");
@@ -138,11 +226,17 @@ unsigned WebAssembly::getCalleeOpNo(const MachineInstr &MI) {
bool WebAssembly::isMarker(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::BLOCK:
+ case WebAssembly::BLOCK_S:
case WebAssembly::END_BLOCK:
+ case WebAssembly::END_BLOCK_S:
case WebAssembly::LOOP:
+ case WebAssembly::LOOP_S:
case WebAssembly::END_LOOP:
+ case WebAssembly::END_LOOP_S:
case WebAssembly::TRY:
+ case WebAssembly::TRY_S:
case WebAssembly::END_TRY:
+ case WebAssembly::END_TRY_S:
return true;
default:
return false;
@@ -152,7 +246,9 @@ bool WebAssembly::isMarker(const MachineInstr &MI) {
bool WebAssembly::isThrow(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::THROW_I32:
+ case WebAssembly::THROW_I32_S:
case WebAssembly::THROW_I64:
+ case WebAssembly::THROW_I64_S:
return true;
default:
return false;
@@ -162,7 +258,9 @@ bool WebAssembly::isThrow(const MachineInstr &MI) {
bool WebAssembly::isRethrow(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::RETHROW:
+ case WebAssembly::RETHROW_S:
case WebAssembly::RETHROW_TO_CALLER:
+ case WebAssembly::RETHROW_TO_CALLER_S:
return true;
default:
return false;
@@ -172,8 +270,11 @@ bool WebAssembly::isRethrow(const MachineInstr &MI) {
bool WebAssembly::isCatch(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::CATCH_I32:
+ case WebAssembly::CATCH_I32_S:
case WebAssembly::CATCH_I64:
+ case WebAssembly::CATCH_I64_S:
case WebAssembly::CATCH_ALL:
+ case WebAssembly::CATCH_ALL_S:
return true;
default:
return false;
@@ -183,8 +284,11 @@ bool WebAssembly::isCatch(const MachineInstr &MI) {
bool WebAssembly::mayThrow(const MachineInstr &MI) {
switch (MI.getOpcode()) {
case WebAssembly::THROW_I32:
+ case WebAssembly::THROW_I32_S:
case WebAssembly::THROW_I64:
+ case WebAssembly::THROW_I64_S:
case WebAssembly::RETHROW:
+ case WebAssembly::RETHROW_S:
return true;
}
if (isCallIndirect(MI))
@@ -212,7 +316,9 @@ bool WebAssembly::isCatchTerminatePad(const MachineBasicBlock &MBB) {
bool SeenCatch = false;
for (auto &MI : MBB) {
if (MI.getOpcode() == WebAssembly::CATCH_I32 ||
- MI.getOpcode() == WebAssembly::CATCH_I64)
+ MI.getOpcode() == WebAssembly::CATCH_I64 ||
+ MI.getOpcode() == WebAssembly::CATCH_I32_S ||
+ MI.getOpcode() == WebAssembly::CATCH_I64_S)
SeenCatch = true;
if (SeenCatch && MI.isCall()) {
const MachineOperand &CalleeOp = MI.getOperand(getCalleeOpNo(MI));
@@ -229,7 +335,8 @@ bool WebAssembly::isCatchAllTerminatePad(const MachineBasicBlock &MBB) {
return false;
bool SeenCatchAll = false;
for (auto &MI : MBB) {
- if (MI.getOpcode() == WebAssembly::CATCH_ALL)
+ if (MI.getOpcode() == WebAssembly::CATCH_ALL ||
+ MI.getOpcode() == WebAssembly::CATCH_ALL_S)
SeenCatchAll = true;
if (SeenCatchAll && MI.isCall()) {
const MachineOperand &CalleeOp = MI.getOperand(getCalleeOpNo(MI));