diff options
Diffstat (limited to 'contrib/llvm/lib/Target/WebAssembly')
90 files changed, 0 insertions, 22326 deletions
diff --git a/contrib/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/contrib/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp deleted file mode 100644 index 09628e872dd5..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ /dev/null @@ -1,837 +0,0 @@ -//==- WebAssemblyAsmParser.cpp - Assembler for WebAssembly -*- C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file is part of the WebAssembly Assembler. -/// -/// It contains code to translate a parsed .s file into MCInsts. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "MCTargetDesc/WebAssemblyTargetStreamer.h" -#include "TargetInfo/WebAssemblyTargetInfo.h" -#include "WebAssembly.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCExpr.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/MCSectionWasm.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/MC/MCSymbolWasm.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/TargetRegistry.h" - -using namespace llvm; - -#define DEBUG_TYPE "wasm-asm-parser" - -namespace { - -/// WebAssemblyOperand - Instances of this class represent the operands in a -/// parsed WASM machine instruction. -struct WebAssemblyOperand : public MCParsedAsmOperand { - enum KindTy { Token, Integer, Float, Symbol, BrList } Kind; - - SMLoc StartLoc, EndLoc; - - struct TokOp { - StringRef Tok; - }; - - struct IntOp { - int64_t Val; - }; - - struct FltOp { - double Val; - }; - - struct SymOp { - const MCExpr *Exp; - }; - - struct BrLOp { - std::vector<unsigned> List; - }; - - union { - struct TokOp Tok; - 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, IntOp 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) {} - WebAssemblyOperand(KindTy K, SMLoc Start, SMLoc End, SymOp 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 == Symbol; } - bool isFPImm() const { return Kind == Float; } - bool isMem() const override { return false; } - bool isReg() const override { return false; } - bool isBrList() const { return Kind == BrList; } - - unsigned getReg() const override { - llvm_unreachable("Assembly inspects a register operand"); - return 0; - } - - StringRef getToken() const { - assert(isToken()); - return Tok.Tok; - } - - SMLoc getStartLoc() const override { return StartLoc; } - SMLoc getEndLoc() const override { return EndLoc; } - - void addRegOperands(MCInst &, unsigned) const { - // Required by the assembly matcher. - llvm_unreachable("Assembly matcher creates register operands"); - } - - void addImmOperands(MCInst &Inst, unsigned N) const { - assert(N == 1 && "Invalid number of operands!"); - if (Kind == Integer) - Inst.addOperand(MCOperand::createImm(Int.Val)); - else if (Kind == Symbol) - Inst.addOperand(MCOperand::createExpr(Sym.Exp)); - else - llvm_unreachable("Should be integer immediate or symbol!"); - } - - void addFPImmOperands(MCInst &Inst, unsigned N) const { - assert(N == 1 && "Invalid number of operands!"); - if (Kind == Float) - Inst.addOperand(MCOperand::createFPImm(Flt.Val)); - else - llvm_unreachable("Should be float immediate!"); - } - - 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 Integer: - OS << "Int:" << Int.Val; - break; - case Float: - OS << "Flt:" << Flt.Val; - break; - case Symbol: - OS << "Sym:" << Sym.Exp; - break; - case BrList: - OS << "BrList:" << BrL.List.size(); - break; - } - } -}; - -class WebAssemblyAsmParser final : public MCTargetAsmParser { - MCAsmParser &Parser; - MCAsmLexer &Lexer; - - // 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, - EndFunction, - DataSection, - } 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; - MCSymbol *LastFunctionLabel = nullptr; - -public: - 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 { - llvm_unreachable("ParseRegister is not implemented."); - } - - 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 expect(AsmToken::TokenKind Kind, const char *KindName) { - if (!isNext(Kind)) - return error(std::string("Expected ") + KindName + ", instead got: ", - Lexer.getTok()); - return false; - } - - StringRef expectIdent() { - if (!Lexer.is(AsmToken::Identifier)) { - error("Expected identifier, got: ", Lexer.getTok()); - return StringRef(); - } - auto Name = Lexer.getTok().getString(); - Parser.Lex(); - return Name; - } - - 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; - if (Type == "exnref") - return wasm::ValType::EXNREF; - return Optional<wasm::ValType>(); - } - - 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("exnref", WebAssembly::ExprType::Exnref) - .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 error("unknown type: ", Lexer.getTok()); - Types.push_back(Type.getValue()); - Parser.Lex(); - if (!isNext(AsmToken::Comma)) - break; - } - return false; - } - - void parseSingleInteger(bool IsNegative, OperandVector &Operands) { - auto &Int = Lexer.getTok(); - int64_t Val = Int.getIntVal(); - if (IsNegative) - Val = -Val; - Operands.push_back(make_unique<WebAssemblyOperand>( - WebAssemblyOperand::Integer, Int.getLoc(), Int.getEndLoc(), - WebAssemblyOperand::IntOp{Val})); - Parser.Lex(); - } - - bool parseSingleFloat(bool IsNegative, OperandVector &Operands) { - auto &Flt = Lexer.getTok(); - double Val; - if (Flt.getString().getAsDouble(Val, false)) - return error("Cannot parse real: ", Flt); - if (IsNegative) - Val = -Val; - Operands.push_back(make_unique<WebAssemblyOperand>( - WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(), - WebAssemblyOperand::FltOp{Val})); - Parser.Lex(); - return false; - } - - bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) { - if (Lexer.isNot(AsmToken::Identifier)) - return true; - auto &Flt = Lexer.getTok(); - auto S = Flt.getString(); - double Val; - if (S.compare_lower("infinity") == 0) { - Val = std::numeric_limits<double>::infinity(); - } else if (S.compare_lower("nan") == 0) { - Val = std::numeric_limits<double>::quiet_NaN(); - } else { - return true; - } - if (IsNegative) - Val = -Val; - Operands.push_back(make_unique<WebAssemblyOperand>( - WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(), - WebAssemblyOperand::FltOp{Val})); - Parser.Lex(); - return false; - } - - bool checkForP2AlignIfLoadStore(OperandVector &Operands, StringRef InstName) { - // FIXME: there is probably a cleaner way to do this. - auto IsLoadStore = InstName.find(".load") != StringRef::npos || - InstName.find(".store") != StringRef::npos; - auto IsAtomic = InstName.find("atomic.") != StringRef::npos; - if (IsLoadStore || IsAtomic) { - // Parse load/store operands of the form: offset:p2align=align - if (IsLoadStore && isNext(AsmToken::Colon)) { - auto Id = expectIdent(); - if (Id != "p2align") - return error("Expected p2align, instead got: " + Id); - if (expect(AsmToken::Equal, "=")) - return true; - if (!Lexer.is(AsmToken::Integer)) - return error("Expected integer constant"); - parseSingleInteger(false, Operands); - } else { - // Alignment not specified (or atomics, must use default alignment). - // We can't just call WebAssembly::GetDefaultP2Align since we don't have - // an opcode until after the assembly matcher, so set a default to fix - // up later. - auto Tok = Lexer.getTok(); - Operands.push_back(make_unique<WebAssemblyOperand>( - WebAssemblyOperand::Integer, Tok.getLoc(), Tok.getEndLoc(), - WebAssemblyOperand::IntOp{-1})); - } - } - return false; - } - - 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 { - // 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})); - - // If this instruction is part of a control flow structure, ensure - // proper nesting. - bool ExpectBlockType = false; - if (Name == "block") { - push(Block); - ExpectBlockType = true; - } else if (Name == "loop") { - push(Loop); - ExpectBlockType = true; - } else if (Name == "try") { - push(Try); - ExpectBlockType = true; - } else if (Name == "if") { - push(If); - ExpectBlockType = true; - } else if (Name == "else") { - if (pop(Name, If)) - return true; - push(Else); - } else if (Name == "catch") { - if (pop(Name, Try)) - return true; - push(Try); - } else if (Name == "end_if") { - if (pop(Name, If, Else)) - return true; - } else if (Name == "end_try") { - if (pop(Name, Try)) - return true; - } else if (Name == "end_loop") { - if (pop(Name, Loop)) - return true; - } else if (Name == "end_block") { - if (pop(Name, Block)) - return true; - } else if (Name == "end_function") { - CurrentState = EndFunction; - if (pop(Name, Function) || ensureEmptyNestingStack()) - return true; - } - - while (Lexer.isNot(AsmToken::EndOfStatement)) { - auto &Tok = Lexer.getTok(); - switch (Tok.getKind()) { - case AsmToken::Identifier: { - if (!parseSpecialFloatMaybe(false, Operands)) - break; - auto &Id = Lexer.getTok(); - 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.parseExpression(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 (checkForP2AlignIfLoadStore(Operands, Name)) - return true; - } - break; - } - case AsmToken::Minus: - Parser.Lex(); - if (Lexer.is(AsmToken::Integer)) { - parseSingleInteger(true, Operands); - if (checkForP2AlignIfLoadStore(Operands, Name)) - return true; - } else if(Lexer.is(AsmToken::Real)) { - if (parseSingleFloat(true, Operands)) - return true; - } else if (!parseSpecialFloatMaybe(true, Operands)) { - } else { - return error("Expected numeric constant instead got: ", - Lexer.getTok()); - } - break; - case AsmToken::Integer: - parseSingleInteger(false, Operands); - if (checkForP2AlignIfLoadStore(Operands, Name)) - return true; - break; - case AsmToken::Real: { - if (parseSingleFloat(false, Operands)) - return true; - 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); - } - if (Lexer.isNot(AsmToken::EndOfStatement)) { - if (expect(AsmToken::Comma, ",")) - return true; - } - } - 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; - } - - bool CheckDataSection() { - if (CurrentState != DataSection) { - auto WS = cast<MCSectionWasm>(getStreamer().getCurrentSection().first); - if (WS && WS->getKind().isText()) - return error("data directive must occur in a data segment: ", - Lexer.getTok()); - } - CurrentState = DataSection; - 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()); - auto &Ctx = Out.getContext(); - - // TODO: any time we return an error, at least one token must have been - // consumed, otherwise this will not signal an error to the caller. - if (DirectiveID.getString() == ".globaltype") { - auto SymName = expectIdent(); - if (SymName.empty()) - return true; - 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>(Ctx.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>(Ctx.getOrCreateSymbol(SymName)); - if (CurrentState == Label && WasmSym == LastLabel) { - // This .functype indicates a start of a function. - if (ensureEmptyNestingStack()) - return true; - CurrentState = FunctionStart; - LastFunctionLabel = LastLabel; - push(Function); - } - 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>(Ctx.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); - CurrentState = FunctionLocals; - return expect(AsmToken::EndOfStatement, "EOL"); - } - - if (DirectiveID.getString() == ".int8" || - DirectiveID.getString() == ".int16" || - DirectiveID.getString() == ".int32" || - DirectiveID.getString() == ".int64") { - if (CheckDataSection()) return true; - const MCExpr *Val; - SMLoc End; - if (Parser.parseExpression(Val, End)) - return error("Cannot parse .int expression: ", Lexer.getTok()); - size_t NumBits = 0; - DirectiveID.getString().drop_front(4).getAsInteger(10, NumBits); - Out.EmitValue(Val, NumBits / 8, End); - return expect(AsmToken::EndOfStatement, "EOL"); - } - - if (DirectiveID.getString() == ".asciz") { - if (CheckDataSection()) return true; - std::string S; - if (Parser.parseEscapedString(S)) - return error("Cannot parse string constant: ", Lexer.getTok()); - Out.EmitBytes(StringRef(S.c_str(), S.length() + 1)); - 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 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>()); - } - // Fix unknown p2align operands. - auto Align = WebAssembly::GetDefaultP2AlignAny(Inst.getOpcode()); - if (Align != -1U) { - auto &Op0 = Inst.getOperand(0); - if (Op0.getImm() == -1) - Op0.setImm(Align); - } - Out.EmitInstruction(Inst, getSTI()); - if (CurrentState == EndFunction) { - onEndOfFunction(); - } else { - CurrentState = Instructions; - } - return false; - } - case Match_MissingFeature: - return Parser.Error( - IDLoc, "instruction requires a WASM feature not currently enabled"); - case Match_MnemonicFail: - return Parser.Error(IDLoc, "invalid instruction"); - case Match_NearMisses: - return Parser.Error(IDLoc, "ambiguous instruction"); - case Match_InvalidTiedOperand: - case Match_InvalidOperand: { - SMLoc ErrorLoc = IDLoc; - if (ErrorInfo != ~0ULL) { - if (ErrorInfo >= Operands.size()) - return Parser.Error(IDLoc, "too few operands for instruction"); - ErrorLoc = Operands[ErrorInfo]->getStartLoc(); - if (ErrorLoc == SMLoc()) - ErrorLoc = IDLoc; - } - return Parser.Error(ErrorLoc, "invalid operand for instruction"); - } - } - llvm_unreachable("Implement any new match types added!"); - } - - void doBeforeLabelEmit(MCSymbol *Symbol) override { - // Start a new section for the next function automatically, since our - // object writer expects each function to have its own section. This way - // The user can't forget this "convention". - auto SymName = Symbol->getName(); - if (SymName.startswith(".L")) - return; // Local Symbol. - // Only create a new text section if we're already in one. - auto CWS = cast<MCSectionWasm>(getStreamer().getCurrentSection().first); - if (!CWS || !CWS->getKind().isText()) - return; - auto SecName = ".text." + SymName; - auto WS = getContext().getWasmSection(SecName, SectionKind::getText()); - getStreamer().SwitchSection(WS); - } - - void onEndOfFunction() { - // Automatically output a .size directive, so it becomes optional for the - // user. - if (!LastFunctionLabel) return; - auto TempSym = getContext().createLinkerPrivateTempSymbol(); - getStreamer().EmitLabel(TempSym); - auto Start = MCSymbolRefExpr::create(LastFunctionLabel, getContext()); - auto End = MCSymbolRefExpr::create(TempSym, getContext()); - auto Expr = - MCBinaryExpr::create(MCBinaryExpr::Sub, End, Start, getContext()); - getStreamer().emitELFSize(LastFunctionLabel, Expr); - } - - void onEndOfFile() override { ensureEmptyNestingStack(); } -}; -} // end anonymous namespace - -// Force static initialization. -extern "C" void LLVMInitializeWebAssemblyAsmParser() { - RegisterMCAsmParser<WebAssemblyAsmParser> X(getTheWebAssemblyTarget32()); - RegisterMCAsmParser<WebAssemblyAsmParser> Y(getTheWebAssemblyTarget64()); -} - -#define GET_REGISTER_MATCHER -#define GET_MATCHER_IMPLEMENTATION -#include "WebAssemblyGenAsmMatcher.inc" diff --git a/contrib/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/contrib/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp deleted file mode 100644 index f9bf3f85d30f..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ /dev/null @@ -1,278 +0,0 @@ -//==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file is part of the WebAssembly Disassembler. -/// -/// It contains code to translate the data produced by the decoder into -/// MCInsts. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyInstPrinter.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "TargetInfo/WebAssemblyTargetInfo.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCDisassembler/MCDisassembler.h" -#include "llvm/MC/MCFixedLenDisassembler.h" -#include "llvm/MC/MCInst.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/TargetRegistry.h" - -using namespace llvm; - -#define DEBUG_TYPE "wasm-disassembler" - -using DecodeStatus = MCDisassembler::DecodeStatus; - -#include "WebAssemblyGenDisassemblerTables.inc" - -namespace { -static constexpr int WebAssemblyInstructionTableSize = 256; - -class WebAssemblyDisassembler final : public MCDisassembler { - std::unique_ptr<const MCInstrInfo> MCII; - - DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, - ArrayRef<uint8_t> Bytes, uint64_t Address, - raw_ostream &VStream, - raw_ostream &CStream) const override; - DecodeStatus onSymbolStart(StringRef Name, uint64_t &Size, - ArrayRef<uint8_t> Bytes, uint64_t Address, - raw_ostream &VStream, - raw_ostream &CStream) const override; - -public: - WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, - std::unique_ptr<const MCInstrInfo> MCII) - : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {} -}; -} // end anonymous namespace - -static MCDisassembler *createWebAssemblyDisassembler(const Target &T, - const MCSubtargetInfo &STI, - MCContext &Ctx) { - std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo()); - return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII)); -} - -extern "C" void LLVMInitializeWebAssemblyDisassembler() { - // Register the disassembler for each target. - TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(), - createWebAssemblyDisassembler); - TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(), - createWebAssemblyDisassembler); -} - -static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) { - if (Size >= Bytes.size()) - return -1; - auto V = Bytes[Size]; - Size++; - return V; -} - -static bool nextLEB(int64_t &Val, ArrayRef<uint8_t> Bytes, uint64_t &Size, - bool Signed) { - unsigned N = 0; - const char *Error = nullptr; - 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 parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) { - if (Size + sizeof(T) > Bytes.size()) - return false; - T Val = support::endian::read<T, support::endianness::little, 1>( - Bytes.data() + Size); - Size += sizeof(T); - 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; -} - -MCDisassembler::DecodeStatus WebAssemblyDisassembler::onSymbolStart( - StringRef Name, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address, - raw_ostream &VStream, raw_ostream &CStream) const { - Size = 0; - if (Address == 0) { - // Start of a code section: we're parsing only the function count. - int64_t FunctionCount; - if (!nextLEB(FunctionCount, Bytes, Size, false)) - return MCDisassembler::Fail; - outs() << " # " << FunctionCount << " functions in section."; - } else { - // Parse the start of a single function. - int64_t BodySize, LocalEntryCount; - if (!nextLEB(BodySize, Bytes, Size, false) || - !nextLEB(LocalEntryCount, Bytes, Size, false)) - return MCDisassembler::Fail; - if (LocalEntryCount) { - outs() << " .local "; - for (int64_t I = 0; I < LocalEntryCount; I++) { - int64_t Count, Type; - if (!nextLEB(Count, Bytes, Size, false) || - !nextLEB(Type, Bytes, Size, false)) - return MCDisassembler::Fail; - for (int64_t J = 0; J < Count; J++) { - if (I || J) - outs() << ", "; - outs() << WebAssembly::anyTypeToString(Type); - } - } - } - } - outs() << "\n"; - return MCDisassembler::Success; -} - -MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( - MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/, - raw_ostream & /*OS*/, raw_ostream &CS) const { - CommentStream = &CS; - Size = 0; - int Opc = nextByte(Bytes, Size); - if (Opc < 0) - return MCDisassembler::Fail; - const auto *WasmInst = &InstructionTable0[Opc]; - // If this is a prefix byte, indirect to another table. - if (WasmInst->ET == ET_Prefix) { - WasmInst = nullptr; - // Linear search, so far only 2 entries. - for (auto PT = PrefixTable; PT->Table; PT++) { - if (PT->Prefix == Opc) { - WasmInst = PT->Table; - break; - } - } - if (!WasmInst) - return MCDisassembler::Fail; - int64_t PrefixedOpc; - if (!nextLEB(PrefixedOpc, Bytes, Size, false)) - return MCDisassembler::Fail; - if (PrefixedOpc < 0 || PrefixedOpc >= WebAssemblyInstructionTableSize) - return MCDisassembler::Fail; - WasmInst += PrefixedOpc; - } - if (WasmInst->ET == ET_Unused) - return MCDisassembler::Fail; - // At this point we must have a valid instruction to decode. - assert(WasmInst->ET == ET_Instruction); - MI.setOpcode(WasmInst->Opcode); - // Parse any operands. - for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) { - auto OT = OperandTable[WasmInst->OperandStart + OPI]; - switch (OT) { - // ULEB operands: - case WebAssembly::OPERAND_BASIC_BLOCK: - case WebAssembly::OPERAND_LOCAL: - case WebAssembly::OPERAND_GLOBAL: - case WebAssembly::OPERAND_FUNCTION32: - case WebAssembly::OPERAND_OFFSET32: - case WebAssembly::OPERAND_P2ALIGN: - case WebAssembly::OPERAND_TYPEINDEX: - case WebAssembly::OPERAND_EVENT: - case MCOI::OPERAND_IMMEDIATE: { - if (!parseLEBImmediate(MI, Size, Bytes, false)) - return MCDisassembler::Fail; - break; - } - // SLEB operands: - case WebAssembly::OPERAND_I32IMM: - 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 (!parseImmediate<float>(MI, Size, Bytes)) - return MCDisassembler::Fail; - break; - } - case WebAssembly::OPERAND_F64IMM: { - if (!parseImmediate<double>(MI, Size, Bytes)) - return MCDisassembler::Fail; - break; - } - // 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"); - } - } - return MCDisassembler::Success; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp deleted file mode 100644 index 70b409cf4a90..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp +++ /dev/null @@ -1,133 +0,0 @@ -//===-- WebAssemblyAsmBackend.cpp - WebAssembly Assembler Backend ---------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements the WebAssemblyAsmBackend class. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyFixupKinds.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAssembler.h" -#include "llvm/MC/MCDirectives.h" -#include "llvm/MC/MCExpr.h" -#include "llvm/MC/MCFixupKindInfo.h" -#include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/MC/MCWasmObjectWriter.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -namespace { - -class WebAssemblyAsmBackend final : public MCAsmBackend { - bool Is64Bit; - -public: - explicit WebAssemblyAsmBackend(bool Is64Bit) - : MCAsmBackend(support::little), Is64Bit(Is64Bit) {} - - unsigned getNumFixupKinds() const override { - return WebAssembly::NumTargetFixupKinds; - } - - const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; - - void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, - const MCValue &Target, MutableArrayRef<char> Data, - uint64_t Value, bool IsPCRel, - const MCSubtargetInfo *STI) const override; - - std::unique_ptr<MCObjectTargetWriter> - createObjectTargetWriter() const override; - - // No instruction requires relaxation - bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, - const MCRelaxableFragment *DF, - const MCAsmLayout &Layout) const override { - return false; - } - - bool mayNeedRelaxation(const MCInst &Inst, - const MCSubtargetInfo &STI) const override { - return false; - } - - void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, - MCInst &Res) const override {} - - bool writeNopData(raw_ostream &OS, uint64_t Count) const override; -}; - -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_sleb128_i32", 0, 5 * 8, 0}, - {"fixup_sleb128_i64", 0, 10 * 8, 0}, - {"fixup_uleb128_i32", 0, 5 * 8, 0}, - }; - - if (Kind < FirstTargetFixupKind) - return MCAsmBackend::getFixupKindInfo(Kind); - - assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && - "Invalid kind!"); - return Infos[Kind - FirstTargetFixupKind]; -} - -bool WebAssemblyAsmBackend::writeNopData(raw_ostream &OS, - uint64_t Count) const { - for (uint64_t I = 0; I < Count; ++I) - OS << char(WebAssembly::Nop); - - return true; -} - -void WebAssemblyAsmBackend::applyFixup(const MCAssembler &Asm, - const MCFixup &Fixup, - const MCValue &Target, - MutableArrayRef<char> Data, - uint64_t Value, bool IsPCRel, - const MCSubtargetInfo *STI) const { - const MCFixupKindInfo &Info = getFixupKindInfo(Fixup.getKind()); - assert(Info.Flags == 0 && "WebAssembly does not use MCFixupKindInfo flags"); - - unsigned NumBytes = alignTo(Info.TargetSize, 8) / 8; - if (Value == 0) - return; // Doesn't change encoding. - - // Shift the value into position. - Value <<= Info.TargetOffset; - - unsigned Offset = Fixup.getOffset(); - assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); - - // For each byte of the fragment that the fixup touches, mask in the - // bits from the fixup value. - for (unsigned I = 0; I != NumBytes; ++I) - Data[Offset + I] |= uint8_t((Value >> (I * 8)) & 0xff); -} - -std::unique_ptr<MCObjectTargetWriter> -WebAssemblyAsmBackend::createObjectTargetWriter() const { - return createWebAssemblyWasmObjectWriter(Is64Bit); -} - -} // end anonymous namespace - -MCAsmBackend *llvm::createWebAssemblyAsmBackend(const Triple &TT) { - return new WebAssemblyAsmBackend(TT.isArch64Bit()); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h deleted file mode 100644 index 33e8de282955..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h +++ /dev/null @@ -1,28 +0,0 @@ -//=- WebAssemblyFixupKinds.h - WebAssembly Specific Fixup Entries -*- C++ -*-=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYFIXUPKINDS_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYFIXUPKINDS_H - -#include "llvm/MC/MCFixup.h" - -namespace llvm { -namespace WebAssembly { -enum Fixups { - fixup_sleb128_i32 = FirstTargetFixupKind, // 32-bit signed - fixup_sleb128_i64, // 64-bit signed - fixup_uleb128_i32, // 32-bit unsigned - - // Marker - LastTargetFixupKind, - NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind -}; -} // end namespace WebAssembly -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp deleted file mode 100644 index b5d4d369b726..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ /dev/null @@ -1,296 +0,0 @@ -//=- WebAssemblyInstPrinter.cpp - WebAssembly assembly instruction printing -=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Print MCInst instructions to wasm format. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyInstPrinter.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/CodeGen/TargetRegisterInfo.h" -#include "llvm/MC/MCExpr.h" -#include "llvm/MC/MCInst.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FormattedStream.h" -using namespace llvm; - -#define DEBUG_TYPE "asm-printer" - -#include "WebAssemblyGenAsmWriter.inc" - -WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI, - const MCInstrInfo &MII, - const MCRegisterInfo &MRI) - : MCInstPrinter(MAI, MII, MRI) {} - -void WebAssemblyInstPrinter::printRegName(raw_ostream &OS, - unsigned RegNo) const { - assert(RegNo != WebAssemblyFunctionInfo::UnusedReg); - // Note that there's an implicit local.get/local.set here! - OS << "$" << RegNo; -} - -void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS, - StringRef Annot, - const MCSubtargetInfo &STI) { - // Print the instruction (this uses the AsmStrings from the .td files). - printInstruction(MI, OS); - - // Print any additional variadic operands. - const MCInstrDesc &Desc = MII.get(MI->getOpcode()); - if (Desc.isVariadic()) - for (auto I = Desc.getNumOperands(), E = MI->getNumOperands(); I < E; ++I) { - // 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 && - MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID_S) || - I != Desc.getNumOperands())) - OS << ", "; - printOperand(MI, I, OS); - } - - // Print any added annotation. - printAnnotation(OS, Annot); - - if (CommentStream) { - // Observe any effects on the control flow stack, for use in annotating - // control flow label references. - unsigned Opc = MI->getOpcode(); - switch (Opc) { - default: - break; - - 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: - case WebAssembly::END_LOOP_S: - if (ControlFlowStack.empty()) { - printAnnotation(OS, "End marker mismatch!"); - } else { - ControlFlowStack.pop_back(); - } - break; - - case WebAssembly::END_BLOCK: - 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: - case WebAssembly::CATCH_S: - if (EHPadStack.empty()) { - printAnnotation(OS, "try-catch mismatch!"); - } else { - printAnnotation(OS, "catch" + utostr(EHPadStack.pop_back_val()) + ':'); - } - break; - } - - // Annotate any control flow label references. - - // rethrow instruction does not take any depth argument and rethrows to the - // nearest enclosing catch scope, if any. If there's no enclosing catch - // scope, it throws up to the caller. - if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) { - if (EHPadStack.empty()) { - printAnnotation(OS, "to caller"); - } else { - printAnnotation(OS, "down to catch" + utostr(EHPadStack.back())); - } - - } else { - unsigned NumFixedOperands = Desc.NumOperands; - SmallSet<uint64_t, 8> Printed; - for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) { - // 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; - 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())) && - !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); - } - - // Use C99's hexadecimal floating-point representation. - static const size_t BufBytes = 128; - char Buf[BufBytes]; - auto Written = FP.convertToHexString( - Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven); - (void)Written; - assert(Written != 0); - assert(Written < BufBytes); - return Buf; -} - -void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, - raw_ostream &O) { - const MCOperand &Op = MI->getOperand(OpNo); - if (Op.isReg()) { - unsigned WAReg = Op.getReg(); - if (int(WAReg) >= 0) - printRegName(O, WAReg); - else if (OpNo >= MII.get(MI->getOpcode()).getNumDefs()) - O << "$pop" << WebAssemblyFunctionInfo::getWARegStackId(WAReg); - else if (WAReg != WebAssemblyFunctionInfo::UnusedReg) - O << "$push" << WebAssemblyFunctionInfo::getWARegStackId(WAReg); - else - O << "$drop"; - // Add a '=' suffix if this is a def. - if (OpNo < MII.get(MI->getOpcode()).getNumDefs()) - O << '='; - } else if (Op.isImm()) { - O << Op.getImm(); - } else if (Op.isFPImm()) { - const MCInstrDesc &Desc = MII.get(MI->getOpcode()); - const MCOperandInfo &Info = Desc.OpInfo[OpNo]; - if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { - // TODO: MC converts all floating point immediate operands to double. - // This is fine for numeric values, but may cause NaNs to change bits. - O << ::toString(APFloat(float(Op.getFPImm()))); - } else { - assert(Info.OperandType == WebAssembly::OPERAND_F64IMM); - O << ::toString(APFloat(Op.getFPImm())); - } - } else { - assert(Op.isExpr() && "unknown operand kind in printOperand"); - Op.getExpr()->print(O, &MAI); - } -} - -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) { - auto Imm = static_cast<unsigned>(MI->getOperand(OpNo).getImm()); - if (Imm != wasm::WASM_TYPE_NORESULT) - O << WebAssembly::anyTypeToString(Imm); -} - -// 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 wasm::WASM_TYPE_I64: - return "i64"; - case wasm::WASM_TYPE_F32: - return "f32"; - case wasm::WASM_TYPE_F64: - return "f64"; - case wasm::WASM_TYPE_V128: - return "v128"; - case wasm::WASM_TYPE_FUNCREF: - return "funcref"; - case wasm::WASM_TYPE_FUNC: - return "func"; - case wasm::WASM_TYPE_EXNREF: - return "exnref"; - case wasm::WASM_TYPE_NORESULT: - return "void"; - default: - return "invalid_type"; - } -} - -const char *llvm::WebAssembly::typeToString(wasm::ValType Ty) { - return anyTypeToString(static_cast<unsigned>(Ty)); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h deleted file mode 100644 index b979de5028bf..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.h +++ /dev/null @@ -1,65 +0,0 @@ -// WebAssemblyInstPrinter.h - Print wasm MCInst to assembly syntax -*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This class prints an WebAssembly MCInst to wasm file syntax. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_INSTPRINTER_WEBASSEMBLYINSTPRINTER_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_INSTPRINTER_WEBASSEMBLYINSTPRINTER_H - -#include "llvm/ADT/SmallVector.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/MC/MCInstPrinter.h" -#include "llvm/Support/MachineValueType.h" - -namespace llvm { - -class MCSubtargetInfo; - -class WebAssemblyInstPrinter final : public MCInstPrinter { - 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, - const MCRegisterInfo &MRI); - - void printRegName(raw_ostream &OS, unsigned RegNo) const override; - void printInst(const MCInst *MI, raw_ostream &OS, StringRef Annot, - const MCSubtargetInfo &STI) override; - - // 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, - raw_ostream &O); - - // Autogenerated by tblgen. - void printInstruction(const MCInst *MI, raw_ostream &O); - static const char *getRegisterName(unsigned RegNo); -}; - -namespace WebAssembly { - -const char *typeToString(wasm::ValType Ty); -const char *anyTypeToString(unsigned Ty); - -} // end namespace WebAssembly - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp deleted file mode 100644 index 8f6531563e1b..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp +++ /dev/null @@ -1,47 +0,0 @@ -//===-- WebAssemblyMCAsmInfo.cpp - WebAssembly asm properties -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the declarations of the WebAssemblyMCAsmInfo -/// properties. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyMCAsmInfo.h" -#include "llvm/ADT/Triple.h" - -using namespace llvm; - -#define DEBUG_TYPE "wasm-mc-asm-info" - -WebAssemblyMCAsmInfo::~WebAssemblyMCAsmInfo() = default; // anchor. - -WebAssemblyMCAsmInfo::WebAssemblyMCAsmInfo(const Triple &T) { - CodePointerSize = CalleeSaveStackSlotSize = T.isArch64Bit() ? 8 : 4; - - // TODO: What should MaxInstLength be? - - UseDataRegionDirectives = true; - - // Use .skip instead of .zero because .zero is confusing when used with two - // arguments (it doesn't actually zero things out). - ZeroDirective = "\t.skip\t"; - - Data8bitsDirective = "\t.int8\t"; - Data16bitsDirective = "\t.int16\t"; - Data32bitsDirective = "\t.int32\t"; - Data64bitsDirective = "\t.int64\t"; - - AlignmentIsInBytes = false; - COMMDirectiveAlignmentIsInBytes = false; - LCOMMDirectiveAlignmentType = LCOMM::Log2Alignment; - - SupportsDebugInformation = true; - - // TODO: UseIntegratedAssembler? -} diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h deleted file mode 100644 index 9efbbf881f59..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.h +++ /dev/null @@ -1,31 +0,0 @@ -//===-- WebAssemblyMCAsmInfo.h - WebAssembly asm properties -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the declaration of the WebAssemblyMCAsmInfo class. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCASMINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCASMINFO_H - -#include "llvm/MC/MCAsmInfoWasm.h" - -namespace llvm { - -class Triple; - -class WebAssemblyMCAsmInfo final : public MCAsmInfoWasm { -public: - explicit WebAssemblyMCAsmInfo(const Triple &T); - ~WebAssemblyMCAsmInfo() override; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp deleted file mode 100644 index 44b6d6a968a9..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//=- WebAssemblyMCCodeEmitter.cpp - Convert WebAssembly code to machine code -// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements the WebAssemblyMCCodeEmitter class. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyFixupKinds.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/MC/MCCodeEmitter.h" -#include "llvm/MC/MCFixup.h" -#include "llvm/MC/MCInst.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/EndianStream.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -#define DEBUG_TYPE "mccodeemitter" - -STATISTIC(MCNumEmitted, "Number of MC instructions emitted."); -STATISTIC(MCNumFixups, "Number of MC fixups created."); - -namespace { -class WebAssemblyMCCodeEmitter final : public MCCodeEmitter { - const MCInstrInfo &MCII; - - // Implementation generated by tablegen. - uint64_t getBinaryCodeForInstr(const MCInst &MI, - SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const; - - void encodeInstruction(const MCInst &MI, raw_ostream &OS, - SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const override; - -public: - WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) : MCII(MCII) {} -}; -} // end anonymous namespace - -MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) { - return new WebAssemblyMCCodeEmitter(MCII); -} - -void WebAssemblyMCCodeEmitter::encodeInstruction( - const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, - const MCSubtargetInfo &STI) const { - uint64_t Start = OS.tell(); - - uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI); - if (Binary <= UINT8_MAX) { - OS << uint8_t(Binary); - } else { - assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet"); - 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 (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); - - const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); - for (unsigned I = 0, E = MI.getNumOperands(); I < E; ++I) { - const MCOperand &MO = MI.getOperand(I); - if (MO.isReg()) { - /* nothing to encode */ - - } else if (MO.isImm()) { - if (I < Desc.getNumOperands()) { - const MCOperandInfo &Info = Desc.OpInfo[I]; - LLVM_DEBUG(dbgs() << "Encoding immediate: type=" - << int(Info.OperandType) << "\n"); - switch (Info.OperandType) { - case WebAssembly::OPERAND_I32IMM: - encodeSLEB128(int32_t(MO.getImm()), OS); - break; - case WebAssembly::OPERAND_OFFSET32: - encodeULEB128(uint32_t(MO.getImm()), OS); - break; - case WebAssembly::OPERAND_I64IMM: - encodeSLEB128(int64_t(MO.getImm()), OS); - break; - case WebAssembly::OPERAND_SIGNATURE: - OS << uint8_t(MO.getImm()); - 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 { - encodeULEB128(uint64_t(MO.getImm()), OS); - } - - } else if (MO.isFPImm()) { - const MCOperandInfo &Info = Desc.OpInfo[I]; - if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { - // TODO: MC converts all floating point immediate operands to double. - // This is fine for numeric values, but may cause NaNs to change bits. - auto F = float(MO.getFPImm()); - support::endian::write<float>(OS, F, support::little); - } else { - assert(Info.OperandType == WebAssembly::OPERAND_F64IMM); - 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; - switch (Info.OperandType) { - case WebAssembly::OPERAND_I32IMM: - FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i32); - break; - case WebAssembly::OPERAND_I64IMM: - FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i64); - PaddedSize = 10; - 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_uleb128_i32); - break; - default: - llvm_unreachable("unexpected symbolic operand kind"); - } - Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(), - FixupKind, MI.getLoc())); - ++MCNumFixups; - encodeULEB128(0, OS, PaddedSize); - } else { - llvm_unreachable("unexpected operand kind"); - } - } - - ++MCNumEmitted; // Keep track of the # of mi's emitted. -} - -#include "WebAssemblyGenMCCodeEmitter.inc" diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp deleted file mode 100644 index 9c8ca1f13b18..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ /dev/null @@ -1,154 +0,0 @@ -//===-- WebAssemblyMCTargetDesc.cpp - WebAssembly Target Descriptions -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file provides WebAssembly-specific target descriptions. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "MCTargetDesc/WebAssemblyInstPrinter.h" -#include "MCTargetDesc/WebAssemblyMCAsmInfo.h" -#include "MCTargetDesc/WebAssemblyTargetStreamer.h" -#include "TargetInfo/WebAssemblyTargetInfo.h" -#include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/TargetRegistry.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-mc-target-desc" - -#define GET_INSTRINFO_MC_DESC -#include "WebAssemblyGenInstrInfo.inc" - -#define GET_SUBTARGETINFO_MC_DESC -#include "WebAssemblyGenSubtargetInfo.inc" - -#define GET_REGINFO_MC_DESC -#include "WebAssemblyGenRegisterInfo.inc" - -static MCAsmInfo *createMCAsmInfo(const MCRegisterInfo & /*MRI*/, - const Triple &TT) { - return new WebAssemblyMCAsmInfo(TT); -} - -static MCInstrInfo *createMCInstrInfo() { - auto *X = new MCInstrInfo(); - InitWebAssemblyMCInstrInfo(X); - return X; -} - -static MCRegisterInfo *createMCRegisterInfo(const Triple & /*T*/) { - auto *X = new MCRegisterInfo(); - InitWebAssemblyMCRegisterInfo(X, 0); - return X; -} - -static MCInstPrinter *createMCInstPrinter(const Triple & /*T*/, - unsigned SyntaxVariant, - const MCAsmInfo &MAI, - const MCInstrInfo &MII, - const MCRegisterInfo &MRI) { - assert(SyntaxVariant == 0 && "WebAssembly only has one syntax variant"); - return new WebAssemblyInstPrinter(MAI, MII, MRI); -} - -static MCCodeEmitter *createCodeEmitter(const MCInstrInfo &MCII, - const MCRegisterInfo & /*MRI*/, - MCContext &Ctx) { - return createWebAssemblyMCCodeEmitter(MCII); -} - -static MCAsmBackend *createAsmBackend(const Target & /*T*/, - const MCSubtargetInfo &STI, - const MCRegisterInfo & /*MRI*/, - const MCTargetOptions & /*Options*/) { - return createWebAssemblyAsmBackend(STI.getTargetTriple()); -} - -static MCSubtargetInfo *createMCSubtargetInfo(const Triple &TT, StringRef CPU, - StringRef FS) { - return createWebAssemblyMCSubtargetInfoImpl(TT, CPU, FS); -} - -static MCTargetStreamer * -createObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { - return new WebAssemblyTargetWasmStreamer(S); -} - -static MCTargetStreamer *createAsmTargetStreamer(MCStreamer &S, - formatted_raw_ostream &OS, - MCInstPrinter * /*InstPrint*/, - bool /*isVerboseAsm*/) { - return new WebAssemblyTargetAsmStreamer(S, OS); -} - -static MCTargetStreamer *createNullTargetStreamer(MCStreamer &S) { - return new WebAssemblyTargetNullStreamer(S); -} - -// Force static initialization. -extern "C" void LLVMInitializeWebAssemblyTargetMC() { - for (Target *T : - {&getTheWebAssemblyTarget32(), &getTheWebAssemblyTarget64()}) { - // Register the MC asm info. - RegisterMCAsmInfoFn X(*T, createMCAsmInfo); - - // Register the MC instruction info. - TargetRegistry::RegisterMCInstrInfo(*T, createMCInstrInfo); - - // Register the MC register info. - TargetRegistry::RegisterMCRegInfo(*T, createMCRegisterInfo); - - // Register the MCInstPrinter. - TargetRegistry::RegisterMCInstPrinter(*T, createMCInstPrinter); - - // Register the MC code emitter. - TargetRegistry::RegisterMCCodeEmitter(*T, createCodeEmitter); - - // Register the ASM Backend. - TargetRegistry::RegisterMCAsmBackend(*T, createAsmBackend); - - // Register the MC subtarget info. - TargetRegistry::RegisterMCSubtargetInfo(*T, createMCSubtargetInfo); - - // Register the object target streamer. - TargetRegistry::RegisterObjectTargetStreamer(*T, - 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::v16i8: - case MVT::v8i16: - case MVT::v4i32: - case MVT::v2i64: - case MVT::v4f32: - case MVT::v2f64: - return wasm::ValType::V128; - case MVT::exnref: - return wasm::ValType::EXNREF; - default: - llvm_unreachable("unexpected type"); - } -} diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h deleted file mode 100644 index 7a9f59b1a4f2..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ /dev/null @@ -1,590 +0,0 @@ -//==- WebAssemblyMCTargetDesc.h - WebAssembly Target Descriptions -*- C++ -*-=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file provides WebAssembly-specific target descriptions. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTARGETDESC_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTARGETDESC_H - -#include "../WebAssemblySubtarget.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/MC/MCInstrDesc.h" -#include "llvm/Support/DataTypes.h" -#include <memory> - -namespace llvm { - -class MCAsmBackend; -class MCCodeEmitter; -class MCContext; -class MCInstrInfo; -class MCObjectTargetWriter; -class MCSubtargetInfo; -class MVT; -class Target; -class Triple; -class raw_pwrite_stream; - -MCCodeEmitter *createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII); - -MCAsmBackend *createWebAssemblyAsmBackend(const Triple &TT); - -std::unique_ptr<MCObjectTargetWriter> -createWebAssemblyWasmObjectWriter(bool Is64Bit); - -namespace WebAssembly { -enum OperandType { - /// Basic block label in a branch construct. - OPERAND_BASIC_BLOCK = MCOI::OPERAND_FIRST_TARGET, - /// Local index. - OPERAND_LOCAL, - /// Global index. - OPERAND_GLOBAL, - /// 32-bit integer immediates. - OPERAND_I32IMM, - /// 64-bit integer immediates. - OPERAND_I64IMM, - /// 32-bit floating-point immediates. - 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. - OPERAND_OFFSET32, - /// p2align immediate for load and store address alignment. - OPERAND_P2ALIGN, - /// signature immediate for block/loop. - 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 { - -/// Target Operand Flag enum. -enum TOF { - MO_NO_FLAG = 0, - - // On a symbol operand this indicates that the immediate is a wasm global - // index. The value of the wasm global will be set to the symbol address at - // runtime. This adds a level of indirection similar to the GOT on native - // platforms. - MO_GOT, - - // On a symbol operand this indicates that the immediate is the symbol - // address relative the __memory_base wasm global. - // Only applicable to data symbols. - MO_MEMORY_BASE_REL, - - // On a symbol operand this indicates that the immediate is the symbol - // address relative the __table_base wasm global. - // Only applicable to function symbols. - MO_TABLE_BASE_REL, -}; - -} // end namespace WebAssemblyII - -} // end namespace llvm - -// Defines symbolic names for WebAssembly registers. This defines a mapping from -// register name to register number. -// -#define GET_REGINFO_ENUM -#include "WebAssemblyGenRegisterInfo.inc" - -// Defines symbolic names for the WebAssembly instructions. -// -#define GET_INSTRINFO_ENUM -#include "WebAssemblyGenInstrInfo.inc" - -namespace llvm { -namespace WebAssembly { - -/// This is used to indicate block signatures. -enum class ExprType : unsigned { - Void = 0x40, - I32 = 0x7F, - I64 = 0x7E, - F32 = 0x7D, - F64 = 0x7C, - V128 = 0x7B, - Exnref = 0x68, - Invalid = 0x00 -}; - -/// Instruction opcodes emitted via means other than CodeGen. -static const unsigned Nop = 0x01; -static const unsigned End = 0x0b; - -wasm::ValType toValType(const MVT &Ty); - -/// Return the default p2align value for a load or store with the given opcode. -inline unsigned GetDefaultP2AlignAny(unsigned Opc) { - switch (Opc) { - case WebAssembly::LOAD8_S_I32: - case WebAssembly::LOAD8_S_I32_S: - case WebAssembly::LOAD8_U_I32: - case WebAssembly::LOAD8_U_I32_S: - case WebAssembly::LOAD8_S_I64: - case WebAssembly::LOAD8_S_I64_S: - case WebAssembly::LOAD8_U_I64: - case WebAssembly::LOAD8_U_I64_S: - case WebAssembly::ATOMIC_LOAD8_U_I32: - case WebAssembly::ATOMIC_LOAD8_U_I32_S: - case WebAssembly::ATOMIC_LOAD8_U_I64: - case WebAssembly::ATOMIC_LOAD8_U_I64_S: - case WebAssembly::STORE8_I32: - case WebAssembly::STORE8_I32_S: - case WebAssembly::STORE8_I64: - case WebAssembly::STORE8_I64_S: - case WebAssembly::ATOMIC_STORE8_I32: - case WebAssembly::ATOMIC_STORE8_I32_S: - case WebAssembly::ATOMIC_STORE8_I64: - case WebAssembly::ATOMIC_STORE8_I64_S: - case WebAssembly::ATOMIC_RMW8_U_ADD_I32: - case WebAssembly::ATOMIC_RMW8_U_ADD_I32_S: - case WebAssembly::ATOMIC_RMW8_U_ADD_I64: - case WebAssembly::ATOMIC_RMW8_U_ADD_I64_S: - case WebAssembly::ATOMIC_RMW8_U_SUB_I32: - case WebAssembly::ATOMIC_RMW8_U_SUB_I32_S: - case WebAssembly::ATOMIC_RMW8_U_SUB_I64: - case WebAssembly::ATOMIC_RMW8_U_SUB_I64_S: - case WebAssembly::ATOMIC_RMW8_U_AND_I32: - case WebAssembly::ATOMIC_RMW8_U_AND_I32_S: - case WebAssembly::ATOMIC_RMW8_U_AND_I64: - case WebAssembly::ATOMIC_RMW8_U_AND_I64_S: - case WebAssembly::ATOMIC_RMW8_U_OR_I32: - case WebAssembly::ATOMIC_RMW8_U_OR_I32_S: - case WebAssembly::ATOMIC_RMW8_U_OR_I64: - case WebAssembly::ATOMIC_RMW8_U_OR_I64_S: - case WebAssembly::ATOMIC_RMW8_U_XOR_I32: - case WebAssembly::ATOMIC_RMW8_U_XOR_I32_S: - case WebAssembly::ATOMIC_RMW8_U_XOR_I64: - case WebAssembly::ATOMIC_RMW8_U_XOR_I64_S: - case WebAssembly::ATOMIC_RMW8_U_XCHG_I32: - 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: - case WebAssembly::LOAD16_U_I32: - case WebAssembly::LOAD16_U_I32_S: - case WebAssembly::LOAD16_S_I64: - case WebAssembly::LOAD16_S_I64_S: - case WebAssembly::LOAD16_U_I64: - case WebAssembly::LOAD16_U_I64_S: - case WebAssembly::ATOMIC_LOAD16_U_I32: - case WebAssembly::ATOMIC_LOAD16_U_I32_S: - case WebAssembly::ATOMIC_LOAD16_U_I64: - case WebAssembly::ATOMIC_LOAD16_U_I64_S: - case WebAssembly::STORE16_I32: - case WebAssembly::STORE16_I32_S: - case WebAssembly::STORE16_I64: - case WebAssembly::STORE16_I64_S: - case WebAssembly::ATOMIC_STORE16_I32: - case WebAssembly::ATOMIC_STORE16_I32_S: - case WebAssembly::ATOMIC_STORE16_I64: - case WebAssembly::ATOMIC_STORE16_I64_S: - case WebAssembly::ATOMIC_RMW16_U_ADD_I32: - case WebAssembly::ATOMIC_RMW16_U_ADD_I32_S: - case WebAssembly::ATOMIC_RMW16_U_ADD_I64: - case WebAssembly::ATOMIC_RMW16_U_ADD_I64_S: - case WebAssembly::ATOMIC_RMW16_U_SUB_I32: - case WebAssembly::ATOMIC_RMW16_U_SUB_I32_S: - case WebAssembly::ATOMIC_RMW16_U_SUB_I64: - case WebAssembly::ATOMIC_RMW16_U_SUB_I64_S: - case WebAssembly::ATOMIC_RMW16_U_AND_I32: - case WebAssembly::ATOMIC_RMW16_U_AND_I32_S: - case WebAssembly::ATOMIC_RMW16_U_AND_I64: - case WebAssembly::ATOMIC_RMW16_U_AND_I64_S: - case WebAssembly::ATOMIC_RMW16_U_OR_I32: - case WebAssembly::ATOMIC_RMW16_U_OR_I32_S: - case WebAssembly::ATOMIC_RMW16_U_OR_I64: - case WebAssembly::ATOMIC_RMW16_U_OR_I64_S: - case WebAssembly::ATOMIC_RMW16_U_XOR_I32: - case WebAssembly::ATOMIC_RMW16_U_XOR_I32_S: - case WebAssembly::ATOMIC_RMW16_U_XOR_I64: - case WebAssembly::ATOMIC_RMW16_U_XOR_I64_S: - case WebAssembly::ATOMIC_RMW16_U_XCHG_I32: - 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: - case WebAssembly::LOAD_F32: - case WebAssembly::LOAD_F32_S: - case WebAssembly::STORE_I32: - case WebAssembly::STORE_I32_S: - case WebAssembly::STORE_F32: - case WebAssembly::STORE_F32_S: - case WebAssembly::LOAD32_S_I64: - case WebAssembly::LOAD32_S_I64_S: - case WebAssembly::LOAD32_U_I64: - case WebAssembly::LOAD32_U_I64_S: - case WebAssembly::STORE32_I64: - case WebAssembly::STORE32_I64_S: - case WebAssembly::ATOMIC_LOAD_I32: - case WebAssembly::ATOMIC_LOAD_I32_S: - case WebAssembly::ATOMIC_LOAD32_U_I64: - case WebAssembly::ATOMIC_LOAD32_U_I64_S: - case WebAssembly::ATOMIC_STORE_I32: - case WebAssembly::ATOMIC_STORE_I32_S: - case WebAssembly::ATOMIC_STORE32_I64: - case WebAssembly::ATOMIC_STORE32_I64_S: - case WebAssembly::ATOMIC_RMW_ADD_I32: - case WebAssembly::ATOMIC_RMW_ADD_I32_S: - case WebAssembly::ATOMIC_RMW32_U_ADD_I64: - case WebAssembly::ATOMIC_RMW32_U_ADD_I64_S: - case WebAssembly::ATOMIC_RMW_SUB_I32: - case WebAssembly::ATOMIC_RMW_SUB_I32_S: - case WebAssembly::ATOMIC_RMW32_U_SUB_I64: - case WebAssembly::ATOMIC_RMW32_U_SUB_I64_S: - case WebAssembly::ATOMIC_RMW_AND_I32: - case WebAssembly::ATOMIC_RMW_AND_I32_S: - case WebAssembly::ATOMIC_RMW32_U_AND_I64: - case WebAssembly::ATOMIC_RMW32_U_AND_I64_S: - case WebAssembly::ATOMIC_RMW_OR_I32: - case WebAssembly::ATOMIC_RMW_OR_I32_S: - case WebAssembly::ATOMIC_RMW32_U_OR_I64: - case WebAssembly::ATOMIC_RMW32_U_OR_I64_S: - case WebAssembly::ATOMIC_RMW_XOR_I32: - case WebAssembly::ATOMIC_RMW_XOR_I32_S: - case WebAssembly::ATOMIC_RMW32_U_XOR_I64: - case WebAssembly::ATOMIC_RMW32_U_XOR_I64_S: - case WebAssembly::ATOMIC_RMW_XCHG_I32: - 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: - case WebAssembly::LOAD_F64: - case WebAssembly::LOAD_F64_S: - case WebAssembly::STORE_I64: - case WebAssembly::STORE_I64_S: - case WebAssembly::STORE_F64: - case WebAssembly::STORE_F64_S: - case WebAssembly::ATOMIC_LOAD_I64: - case WebAssembly::ATOMIC_LOAD_I64_S: - case WebAssembly::ATOMIC_STORE_I64: - case WebAssembly::ATOMIC_STORE_I64_S: - case WebAssembly::ATOMIC_RMW_ADD_I64: - case WebAssembly::ATOMIC_RMW_ADD_I64_S: - case WebAssembly::ATOMIC_RMW_SUB_I64: - case WebAssembly::ATOMIC_RMW_SUB_I64_S: - case WebAssembly::ATOMIC_RMW_AND_I64: - case WebAssembly::ATOMIC_RMW_AND_I64_S: - case WebAssembly::ATOMIC_RMW_OR_I64: - case WebAssembly::ATOMIC_RMW_OR_I64_S: - case WebAssembly::ATOMIC_RMW_XOR_I64: - 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: - return -1; - } -} - -inline unsigned GetDefaultP2Align(unsigned Opc) { - auto Align = GetDefaultP2AlignAny(Opc); - if (Align == -1U) { - llvm_unreachable("Only loads and stores have p2align values"); - } - return Align; -} - -inline bool isArgument(unsigned Opc) { - switch (Opc) { - 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: - case WebAssembly::ARGUMENT_exnref: - case WebAssembly::ARGUMENT_exnref_S: - return true; - default: - return false; - } -} - -inline bool isCopy(unsigned Opc) { - switch (Opc) { - 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: - case WebAssembly::COPY_EXNREF: - case WebAssembly::COPY_EXNREF_S: - return true; - default: - return false; - } -} - -inline bool isTee(unsigned Opc) { - switch (Opc) { - 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: - case WebAssembly::TEE_EXNREF: - case WebAssembly::TEE_EXNREF_S: - return true; - default: - return false; - } -} - -inline bool isCallDirect(unsigned Opc) { - switch (Opc) { - 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_exnref: - case WebAssembly::CALL_exnref_S: - case WebAssembly::RET_CALL: - case WebAssembly::RET_CALL_S: - return true; - default: - return false; - } -} - -inline bool isCallIndirect(unsigned Opc) { - switch (Opc) { - 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_exnref: - case WebAssembly::CALL_INDIRECT_exnref_S: - case WebAssembly::RET_CALL_INDIRECT: - case WebAssembly::RET_CALL_INDIRECT_S: - return true; - default: - return false; - } -} - -/// Returns the operand number of a callee, assuming the argument is a call -/// instruction. -inline unsigned getCalleeOpNo(unsigned Opc) { - switch (Opc) { - case WebAssembly::CALL_VOID: - case WebAssembly::CALL_VOID_S: - case WebAssembly::CALL_INDIRECT_VOID: - case WebAssembly::CALL_INDIRECT_VOID_S: - case WebAssembly::RET_CALL: - case WebAssembly::RET_CALL_S: - case WebAssembly::RET_CALL_INDIRECT: - case WebAssembly::RET_CALL_INDIRECT_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_exnref: - case WebAssembly::CALL_exnref_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_exnref: - case WebAssembly::CALL_INDIRECT_exnref_S: - return 1; - default: - llvm_unreachable("Not a call instruction"); - } -} - -inline bool isMarker(unsigned Opc) { - switch (Opc) { - 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; - } -} - -} // end namespace WebAssembly -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp deleted file mode 100644 index e05efef7201b..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ /dev/null @@ -1,152 +0,0 @@ -//==-- WebAssemblyTargetStreamer.cpp - WebAssembly Target Streamer Methods --=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines WebAssembly-specific target streamer classes. -/// These are for implementing support for target-specific assembly directives. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyTargetStreamer.h" -#include "MCTargetDesc/WebAssemblyInstPrinter.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCSectionWasm.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbolWasm.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/FormattedStream.h" -using namespace llvm; - -WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S) - : MCTargetStreamer(S) {} - -void WebAssemblyTargetStreamer::emitValueType(wasm::ValType Type) { - Streamer.EmitIntValue(uint8_t(Type), 1); -} - -WebAssemblyTargetAsmStreamer::WebAssemblyTargetAsmStreamer( - MCStreamer &S, formatted_raw_ostream &OS) - : WebAssemblyTargetStreamer(S), OS(OS) {} - -WebAssemblyTargetWasmStreamer::WebAssemblyTargetWasmStreamer(MCStreamer &S) - : WebAssemblyTargetStreamer(S) {} - -static void printTypes(formatted_raw_ostream &OS, - ArrayRef<wasm::ValType> Types) { - bool First = true; - for (auto Type : Types) { - if (First) - First = false; - else - OS << ", "; - OS << WebAssembly::typeToString(Type); - } - OS << '\n'; -} - -void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<wasm::ValType> Types) { - if (!Types.empty()) { - OS << "\t.local \t"; - printTypes(OS, Types); - } -} - -void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; } - -void WebAssemblyTargetAsmStreamer::emitSignature( - const wasm::WasmSignature *Sig) { - OS << "("; - emitParamList(Sig); - OS << ") -> ("; - emitReturnList(Sig); - OS << ")"; -} - -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::emitReturnList( - const wasm::WasmSignature *Sig) { - auto &Returns = Sig->Returns; - for (auto &Ty : Returns) { - if (&Ty != &Returns[0]) - OS << ", "; - OS << WebAssembly::typeToString(Ty); - } -} - -void WebAssemblyTargetAsmStreamer::emitFunctionType(const MCSymbolWasm *Sym) { - assert(Sym->isFunction()); - OS << "\t.functype\t" << Sym->getName() << " "; - emitSignature(Sym->getSignature()); - 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::emitEventType(const MCSymbolWasm *Sym) { - assert(Sym->isEvent()); - OS << "\t.eventtype\t" << Sym->getName() << " "; - emitParamList(Sym->getSignature()); - OS << "\n"; -} - -void WebAssemblyTargetAsmStreamer::emitImportModule(const MCSymbolWasm *Sym, - StringRef ImportModule) { - OS << "\t.import_module\t" << Sym->getName() << ", " - << ImportModule << '\n'; -} - -void WebAssemblyTargetAsmStreamer::emitImportName(const MCSymbolWasm *Sym, - StringRef ImportName) { - OS << "\t.import_name\t" << Sym->getName() << ", " - << ImportName << '\n'; -} - -void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) { - OS << "\t.indidx \t" << *Value << '\n'; -} - -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 - ++Grouped.back().second; - } - - Streamer.EmitULEB128IntValue(Grouped.size()); - for (auto Pair : Grouped) { - Streamer.EmitULEB128IntValue(Pair.second); - emitValueType(Pair.first); - } -} - -void WebAssemblyTargetWasmStreamer::emitEndFunc() { - llvm_unreachable(".end_func is not needed for direct wasm output"); -} - -void WebAssemblyTargetWasmStreamer::emitIndIdx(const MCExpr *Value) { - llvm_unreachable(".indidx encoding not yet implemented"); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h deleted file mode 100644 index 5ea62b179d22..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ /dev/null @@ -1,111 +0,0 @@ -//==-- WebAssemblyTargetStreamer.h - WebAssembly Target Streamer -*- C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file declares WebAssembly-specific target streamer classes. -/// These are for implementing support for target-specific assembly directives. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYTARGETSTREAMER_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYTARGETSTREAMER_H - -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/Support/MachineValueType.h" - -namespace llvm { - -class MCWasmStreamer; -class MCSymbolWasm; - -/// WebAssembly-specific streamer interface, to implement support -/// WebAssembly-specific assembly directives. -class WebAssemblyTargetStreamer : public MCTargetStreamer { -public: - explicit WebAssemblyTargetStreamer(MCStreamer &S); - - /// .local - virtual void emitLocal(ArrayRef<wasm::ValType> Types) = 0; - /// .endfunc - virtual void emitEndFunc() = 0; - /// .functype - virtual void emitFunctionType(const MCSymbolWasm *Sym) = 0; - /// .indidx - virtual void emitIndIdx(const MCExpr *Value) = 0; - /// .globaltype - virtual void emitGlobalType(const MCSymbolWasm *Sym) = 0; - /// .eventtype - virtual void emitEventType(const MCSymbolWasm *Sym) = 0; - /// .import_module - virtual void emitImportModule(const MCSymbolWasm *Sym, - StringRef ImportModule) = 0; - /// .import_name - virtual void emitImportName(const MCSymbolWasm *Sym, - StringRef ImportName) = 0; - -protected: - void emitValueType(wasm::ValType Type); -}; - -/// 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 emitLocal(ArrayRef<wasm::ValType> Types) override; - void emitEndFunc() override; - void emitFunctionType(const MCSymbolWasm *Sym) override; - void emitIndIdx(const MCExpr *Value) override; - void emitGlobalType(const MCSymbolWasm *Sym) override; - void emitEventType(const MCSymbolWasm *Sym) override; - void emitImportModule(const MCSymbolWasm *Sym, StringRef ImportModule) override; - void emitImportName(const MCSymbolWasm *Sym, StringRef ImportName) override; -}; - -/// This part is for Wasm object output -class WebAssemblyTargetWasmStreamer final : public WebAssemblyTargetStreamer { -public: - explicit WebAssemblyTargetWasmStreamer(MCStreamer &S); - - void emitLocal(ArrayRef<wasm::ValType> Types) override; - void emitEndFunc() override; - void emitFunctionType(const MCSymbolWasm *Sym) override {} - void emitIndIdx(const MCExpr *Value) override; - void emitGlobalType(const MCSymbolWasm *Sym) override {} - void emitEventType(const MCSymbolWasm *Sym) override {} - void emitImportModule(const MCSymbolWasm *Sym, - StringRef ImportModule) override {} - void emitImportName(const MCSymbolWasm *Sym, - StringRef ImportName) 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 {} - void emitImportName(const MCSymbolWasm *, StringRef) override {} -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp deleted file mode 100644 index a1cc3e268e8f..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ /dev/null @@ -1,121 +0,0 @@ -//===-- WebAssemblyWasmObjectWriter.cpp - WebAssembly Wasm Writer ---------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file handles Wasm-specific object emission, converting LLVM's -/// internal fixups into the appropriate relocations. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyFixupKinds.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCFixup.h" -#include "llvm/MC/MCFixupKindInfo.h" -#include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCSectionWasm.h" -#include "llvm/MC/MCSymbolWasm.h" -#include "llvm/MC/MCValue.h" -#include "llvm/MC/MCWasmObjectWriter.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/ErrorHandling.h" - -using namespace llvm; - -namespace { -class WebAssemblyWasmObjectWriter final : public MCWasmObjectTargetWriter { -public: - explicit WebAssemblyWasmObjectWriter(bool Is64Bit); - -private: - unsigned getRelocType(const MCValue &Target, - const MCFixup &Fixup) const override; -}; -} // end anonymous namespace - -WebAssemblyWasmObjectWriter::WebAssemblyWasmObjectWriter(bool Is64Bit) - : MCWasmObjectTargetWriter(Is64Bit) {} - -static const MCSection *getFixupSection(const MCExpr *Expr) { - if (auto SyExp = dyn_cast<MCSymbolRefExpr>(Expr)) { - if (SyExp->getSymbol().isInSection()) - return &SyExp->getSymbol().getSection(); - return nullptr; - } - - if (auto BinOp = dyn_cast<MCBinaryExpr>(Expr)) { - auto SectionLHS = getFixupSection(BinOp->getLHS()); - auto SectionRHS = getFixupSection(BinOp->getRHS()); - return SectionLHS == SectionRHS ? nullptr : SectionLHS; - } - - if (auto UnOp = dyn_cast<MCUnaryExpr>(Expr)) - return getFixupSection(UnOp->getSubExpr()); - - return nullptr; -} - -unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target, - const MCFixup &Fixup) const { - const MCSymbolRefExpr *RefA = Target.getSymA(); - assert(RefA); - auto& SymA = cast<MCSymbolWasm>(RefA->getSymbol()); - - MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); - - switch (Modifier) { - case MCSymbolRefExpr::VK_GOT: - return wasm::R_WASM_GLOBAL_INDEX_LEB; - case MCSymbolRefExpr::VK_WASM_TBREL: - assert(SymA.isFunction()); - return wasm::R_WASM_TABLE_INDEX_REL_SLEB; - case MCSymbolRefExpr::VK_WASM_MBREL: - assert(SymA.isData()); - return wasm::R_WASM_MEMORY_ADDR_REL_SLEB; - case MCSymbolRefExpr::VK_WASM_TYPEINDEX: - return wasm::R_WASM_TYPE_INDEX_LEB; - default: - break; - } - - switch (unsigned(Fixup.getKind())) { - case WebAssembly::fixup_sleb128_i32: - if (SymA.isFunction()) - return wasm::R_WASM_TABLE_INDEX_SLEB; - return wasm::R_WASM_MEMORY_ADDR_SLEB; - case WebAssembly::fixup_sleb128_i64: - llvm_unreachable("fixup_sleb128_i64 not implemented yet"); - case WebAssembly::fixup_uleb128_i32: - if (SymA.isGlobal()) - return wasm::R_WASM_GLOBAL_INDEX_LEB; - if (SymA.isFunction()) - return wasm::R_WASM_FUNCTION_INDEX_LEB; - if (SymA.isEvent()) - return wasm::R_WASM_EVENT_INDEX_LEB; - return wasm::R_WASM_MEMORY_ADDR_LEB; - case FK_Data_4: - if (SymA.isFunction()) - return wasm::R_WASM_TABLE_INDEX_I32; - if (auto Section = static_cast<const MCSectionWasm *>( - getFixupSection(Fixup.getValue()))) { - if (Section->getKind().isText()) - return wasm::R_WASM_FUNCTION_OFFSET_I32; - else if (!Section->isWasmData()) - return wasm::R_WASM_SECTION_OFFSET_I32; - } - return wasm::R_WASM_MEMORY_ADDR_I32; - default: - llvm_unreachable("unimplemented fixup kind"); - } -} - -std::unique_ptr<MCObjectTargetWriter> -llvm::createWebAssemblyWasmObjectWriter(bool Is64Bit) { - return llvm::make_unique<WebAssemblyWasmObjectWriter>(Is64Bit); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/README.txt b/contrib/llvm/lib/Target/WebAssembly/README.txt deleted file mode 100644 index ef3f5aaf7d33..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/README.txt +++ /dev/null @@ -1,196 +0,0 @@ -//===-- README.txt - Notes for WebAssembly code gen -----------------------===// - -This WebAssembly backend is presently under development. - -The most notable feature which is not yet stable is the ".o" file format. -".o" file support is needed for many common ways of using LLVM, such as -using it through "clang -c", so this backend is not yet considered widely -usable. However, this backend is usable within some language toolchain -packages: - -Emscripten provides a C/C++ compilation environment that includes standard -libraries, tools, and packaging for producing WebAssembly applications that -can run in browsers and other environments. For more information, see the -Emscripten documentation in general, and this page in particular: - - * https://github.com/kripken/emscripten/wiki/New-WebAssembly-Backend - -Rust provides WebAssembly support integrated into Cargo. There are two -main options: - - wasm32-unknown-unknown, which provides a relatively minimal environment - that has an emphasis on being "native" - - wasm32-unknown-emscripten, which uses Emscripten internally and - provides standard C/C++ libraries, filesystem emulation, GL and SDL - bindings -For more information, see: - * https://www.hellorust.com/ - - -This backend does not yet support debug info. Full DWARF support needs a -design for how DWARF should be represented in WebAssembly. Sourcemap support -has an existing design and some corresponding browser implementations, so it -just needs implementing in LLVM. - -Work-in-progress documentation for the ".o" file format is here: - - * https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md - -A corresponding linker implementation is also under development: - - * https://lld.llvm.org/WebAssembly.html - -For more information on WebAssembly itself, see the home page: - * https://webassembly.github.io/ - -The following documents contain some information on the semantics and binary -encoding of WebAssembly itself: - * https://github.com/WebAssembly/design/blob/master/Semantics.md - * https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md - -The backend is built, tested and archived on the following waterfall: - https://wasm-stat.us - -The backend's bringup is done in part by using the GCC torture test suite, since -it doesn't require C library support. Current known failures are in -known_gcc_test_failures.txt, all other tests should pass. The waterfall will -turn red if not. Once most of these pass, further testing will use LLVM's own -test suite. The tests can be run locally using: - https://github.com/WebAssembly/waterfall/blob/master/src/compile_torture_tests.py - -Some notes on ways that the generated code could be improved follow: - -//===---------------------------------------------------------------------===// - -Br, br_if, and br_table instructions can support having a value on the value -stack across the jump (sometimes). We should (a) model this, and (b) extend -the stackifier to utilize it. - -//===---------------------------------------------------------------------===// - -The min/max instructions aren't exactly a<b?a:b because of NaN and negative zero -behavior. The ARM target has the same kind of min/max instructions and has -implemented optimizations for them; we should do similar optimizations for -WebAssembly. - -//===---------------------------------------------------------------------===// - -AArch64 runs SeparateConstOffsetFromGEPPass, followed by EarlyCSE and LICM. -Would these be useful to run for WebAssembly too? Also, it has an option to -run SimplifyCFG after running the AtomicExpand pass. Would this be useful for -us too? - -//===---------------------------------------------------------------------===// - -Register stackification uses the VALUE_STACK physical register to impose -ordering dependencies on instructions with stack operands. This is pessimistic; -we should consider alternate ways to model stack dependencies. - -//===---------------------------------------------------------------------===// - -Lots of things could be done in WebAssemblyTargetTransformInfo.cpp. Similarly, -there are numerous optimization-related hooks that can be overridden in -WebAssemblyTargetLowering. - -//===---------------------------------------------------------------------===// - -Instead of the OptimizeReturned pass, which should consider preserving the -"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. - -//===---------------------------------------------------------------------===// - -Consider implementing optimizeSelect, optimizeCompareInstr, optimizeCondBranch, -optimizeLoadInstr, and/or getMachineCombinerPatterns. - -//===---------------------------------------------------------------------===// - -Find a clean way to fix the problem which leads to the Shrink Wrapping pass -being run after the WebAssembly PEI pass. - -//===---------------------------------------------------------------------===// - -When setting multiple local variables to the same constant, we currently get -code like this: - - i32.const $4=, 0 - i32.const $3=, 0 - -It could be done with a smaller encoding like this: - - i32.const $push5=, 0 - local.tee $push6=, $4=, $pop5 - local.copy $3=, $pop6 - -//===---------------------------------------------------------------------===// - -WebAssembly registers are implicitly initialized to zero. Explicit zeroing is -therefore often redundant and could be optimized away. - -//===---------------------------------------------------------------------===// - -Small indices may use smaller encodings than large indices. -WebAssemblyRegColoring and/or WebAssemblyRegRenumbering should sort registers -according to their usage frequency to maximize the usage of smaller encodings. - -//===---------------------------------------------------------------------===// - -Many cases of irreducible control flow could be transformed more optimally -than via the transform in WebAssemblyFixIrreducibleControlFlow.cpp. - -It may also be worthwhile to do transforms before register coloring, -particularly when duplicating code, to allow register coloring to be aware of -the duplication. - -//===---------------------------------------------------------------------===// - -WebAssemblyRegStackify could use AliasAnalysis to reorder loads and stores more -aggressively. - -//===---------------------------------------------------------------------===// - -WebAssemblyRegStackify is currently a greedy algorithm. This means that, for -example, a binary operator will stackify with its user before its operands. -However, if moving the binary operator to its user moves it to a place where -its operands can't be moved to, it would be better to leave it in place, or -perhaps move it up, so that it can stackify its operands. A binary operator -has two operands and one result, so in such cases there could be a net win by -preferring the operands. - -//===---------------------------------------------------------------------===// - -Instruction ordering has a significant influence on register stackification and -coloring. Consider experimenting with the MachineScheduler (enable via -enableMachineScheduler) and determine if it can be configured to schedule -instructions advantageously for this purpose. - -//===---------------------------------------------------------------------===// - -WebAssemblyRegStackify currently assumes that the stack must be empty after -an instruction with no return values, however wasm doesn't actually require -this. WebAssemblyRegStackify could be extended, or possibly rewritten, to take -full advantage of what WebAssembly permits. - -//===---------------------------------------------------------------------===// - -Add support for mergeable sections in the Wasm writer, such as for strings and -floating-point constants. - -//===---------------------------------------------------------------------===// - -The function @dynamic_alloca_redzone in test/CodeGen/WebAssembly/userstack.ll -ends up with a local.tee in its prolog which has an unused result, requiring -an extra drop: - - global.get $push8=, 0 - local.tee $push9=, 1, $pop8 - drop $pop9 - [...] - -The prologue code initially thinks it needs an FP register, but later it -turns out to be unneeded, so one could either approach this by being more -clever about not inserting code for an FP in the first place, or optimizing -away the copy later. - -//===---------------------------------------------------------------------===// diff --git a/contrib/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp deleted file mode 100644 index e4afe2bb2830..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//===-- WebAssemblyTargetInfo.cpp - WebAssembly Target Implementation -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file registers the WebAssembly target. -/// -//===----------------------------------------------------------------------===// - -#include "TargetInfo/WebAssemblyTargetInfo.h" -#include "llvm/Support/TargetRegistry.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-target-info" - -Target &llvm::getTheWebAssemblyTarget32() { - static Target TheWebAssemblyTarget32; - return TheWebAssemblyTarget32; -} -Target &llvm::getTheWebAssemblyTarget64() { - static Target TheWebAssemblyTarget64; - return TheWebAssemblyTarget64; -} - -extern "C" void LLVMInitializeWebAssemblyTargetInfo() { - RegisterTarget<Triple::wasm32> X(getTheWebAssemblyTarget32(), "wasm32", - "WebAssembly 32-bit", "WebAssembly"); - RegisterTarget<Triple::wasm64> Y(getTheWebAssemblyTarget64(), "wasm64", - "WebAssembly 64-bit", "WebAssembly"); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h b/contrib/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h deleted file mode 100644 index a7427f78c72c..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h +++ /dev/null @@ -1,26 +0,0 @@ -//===-- WebAssemblyTargetInfo.h - WebAssembly Target Impl -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file registers the WebAssembly target. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_TARGETINFO_WEBASSEMBLYTARGETINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_TARGETINFO_WEBASSEMBLYTARGETINFO_H - -namespace llvm { - -class Target; - -Target &getTheWebAssemblyTarget32(); -Target &getTheWebAssemblyTarget64(); - -} // namespace llvm - -#endif // LLVM_LIB_TARGET_WEBASSEMBLY_TARGETINFO_WEBASSEMBLYTARGETINFO_H diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssembly.h b/contrib/llvm/lib/Target/WebAssembly/WebAssembly.h deleted file mode 100644 index fcbd0a5082ff..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssembly.h +++ /dev/null @@ -1,84 +0,0 @@ -//===-- WebAssembly.h - Top-level interface for WebAssembly ----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the entry points for global functions defined in -/// the LLVM WebAssembly back-end. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLY_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLY_H - -#include "llvm/PassRegistry.h" -#include "llvm/Support/CodeGen.h" - -namespace llvm { - -class WebAssemblyTargetMachine; -class ModulePass; -class FunctionPass; - -// LLVM IR passes. -ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj); -ModulePass *createWebAssemblyLowerGlobalDtors(); -ModulePass *createWebAssemblyAddMissingPrototypes(); -ModulePass *createWebAssemblyFixFunctionBitcasts(); -FunctionPass *createWebAssemblyOptimizeReturned(); - -// ISel and immediate followup passes. -FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, - CodeGenOpt::Level OptLevel); -FunctionPass *createWebAssemblyArgumentMove(); -FunctionPass *createWebAssemblySetP2AlignOperands(); - -// Late passes. -FunctionPass *createWebAssemblyReplacePhysRegs(); -FunctionPass *createWebAssemblyPrepareForLiveIntervals(); -FunctionPass *createWebAssemblyOptimizeLiveIntervals(); -FunctionPass *createWebAssemblyMemIntrinsicResults(); -FunctionPass *createWebAssemblyRegStackify(); -FunctionPass *createWebAssemblyRegColoring(); -FunctionPass *createWebAssemblyFixIrreducibleControlFlow(); -FunctionPass *createWebAssemblyLateEHPrepare(); -FunctionPass *createWebAssemblyCFGSort(); -FunctionPass *createWebAssemblyCFGStackify(); -FunctionPass *createWebAssemblyExplicitLocals(); -FunctionPass *createWebAssemblyLowerBrUnless(); -FunctionPass *createWebAssemblyRegNumbering(); -FunctionPass *createWebAssemblyPeephole(); -FunctionPass *createWebAssemblyCallIndirectFixup(); - -// PassRegistry initialization declarations. -void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &); -void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &); -void initializeLowerGlobalDtorsPass(PassRegistry &); -void initializeFixFunctionBitcastsPass(PassRegistry &); -void initializeOptimizeReturnedPass(PassRegistry &); -void initializeWebAssemblyArgumentMovePass(PassRegistry &); -void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &); -void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &); -void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &); -void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &); -void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &); -void initializeWebAssemblyRegStackifyPass(PassRegistry &); -void initializeWebAssemblyRegColoringPass(PassRegistry &); -void initializeWebAssemblyFixIrreducibleControlFlowPass(PassRegistry &); -void initializeWebAssemblyLateEHPreparePass(PassRegistry &); -void initializeWebAssemblyExceptionInfoPass(PassRegistry &); -void initializeWebAssemblyCFGSortPass(PassRegistry &); -void initializeWebAssemblyCFGStackifyPass(PassRegistry &); -void initializeWebAssemblyExplicitLocalsPass(PassRegistry &); -void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &); -void initializeWebAssemblyRegNumberingPass(PassRegistry &); -void initializeWebAssemblyPeepholePass(PassRegistry &); -void initializeWebAssemblyCallIndirectFixupPass(PassRegistry &); - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssembly.td b/contrib/llvm/lib/Target/WebAssembly/WebAssembly.td deleted file mode 100644 index b0b8a9b996a3..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssembly.td +++ /dev/null @@ -1,123 +0,0 @@ -//- WebAssembly.td - Describe the WebAssembly Target Machine --*- tablegen -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This is a target description file for the WebAssembly architecture, -/// which is also known as "wasm". -/// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// Target-independent interfaces which we are implementing -//===----------------------------------------------------------------------===// - -include "llvm/Target/Target.td" - -//===----------------------------------------------------------------------===// -// WebAssembly Subtarget features. -//===----------------------------------------------------------------------===// - -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 : - SubtargetFeature<"nontrapping-fptoint", - "HasNontrappingFPToInt", "true", - "Enable non-trapping float-to-int conversion operators">; - -def FeatureSignExt : - SubtargetFeature<"sign-ext", - "HasSignExt", "true", - "Enable sign extension operators">; - -def FeatureTailCall : - SubtargetFeature<"tail-call", - "HasTailCall", "true", - "Enable tail call instructions">; - -def FeatureExceptionHandling : - SubtargetFeature<"exception-handling", "HasExceptionHandling", "true", - "Enable Wasm exception handling">; - -def FeatureBulkMemory : - SubtargetFeature<"bulk-memory", "HasBulkMemory", "true", - "Enable bulk memory operations">; - -def FeatureMultivalue : - SubtargetFeature<"multivalue", - "HasMultivalue", "true", - "Enable multivalue blocks, instructions, and functions">; - -def FeatureMutableGlobals : - SubtargetFeature<"mutable-globals", "HasMutableGlobals", "true", - "Enable mutable globals">; - -//===----------------------------------------------------------------------===// -// Architectures. -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// Register File Description -//===----------------------------------------------------------------------===// - -include "WebAssemblyRegisterInfo.td" - -//===----------------------------------------------------------------------===// -// Instruction Descriptions -//===----------------------------------------------------------------------===// - -include "WebAssemblyInstrInfo.td" - -def WebAssemblyInstrInfo : InstrInfo; - -//===----------------------------------------------------------------------===// -// WebAssembly Processors supported. -//===----------------------------------------------------------------------===// - -// Minimal Viable Product. -def : ProcessorModel<"mvp", NoSchedModel, []>; - -// Generic processor: latest stable version. -def : ProcessorModel<"generic", NoSchedModel, []>; - -// Latest and greatest experimental version of WebAssembly. Bugs included! -def : ProcessorModel<"bleeding-edge", NoSchedModel, - [FeatureSIMD128, FeatureAtomics, - FeatureNontrappingFPToInt, FeatureSignExt, - FeatureMutableGlobals]>; - -//===----------------------------------------------------------------------===// -// Target Declaration -//===----------------------------------------------------------------------===// - -def WebAssemblyAsmParser : AsmParser { - // The physical register names are not in the binary format or asm text - let ShouldEmitMatchRegisterName = 0; -} - -def WebAssemblyAsmWriter : AsmWriter { - string AsmWriterClassName = "InstPrinter"; - int PassSubtarget = 0; - int Variant = 0; - bit isMCAsmWriter = 1; -} - -def WebAssembly : Target { - let InstructionSet = WebAssemblyInstrInfo; - let AssemblyParsers = [WebAssemblyAsmParser]; - let AssemblyWriters = [WebAssemblyAsmWriter]; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp deleted file mode 100644 index b7a701f15782..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAddMissingPrototypes.cpp +++ /dev/null @@ -1,144 +0,0 @@ -//===-- WebAssemblyAddMissingPrototypes.cpp - Fix prototypeless functions -===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Add prototypes to prototypes-less functions. -/// -/// WebAssembly has strict function prototype checking so we need functions -/// declarations to match the call sites. Clang treats prototype-less functions -/// as varargs (foo(...)) which happens to work on existing platforms but -/// doesn't under WebAssembly. This pass will find all the call sites of each -/// prototype-less function, ensure they agree, and then set the signature -/// on the function declaration accordingly. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Operator.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" - -namespace { -class WebAssemblyAddMissingPrototypes final : public ModulePass { - StringRef getPassName() const override { - return "Add prototypes to prototypes-less functions"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - ModulePass::getAnalysisUsage(AU); - } - - bool runOnModule(Module &M) override; - -public: - static char ID; - WebAssemblyAddMissingPrototypes() : ModulePass(ID) {} -}; -} // End anonymous namespace - -char WebAssemblyAddMissingPrototypes::ID = 0; -INITIALIZE_PASS(WebAssemblyAddMissingPrototypes, DEBUG_TYPE, - "Add prototypes to prototypes-less functions", false, false) - -ModulePass *llvm::createWebAssemblyAddMissingPrototypes() { - return new WebAssemblyAddMissingPrototypes(); -} - -bool WebAssemblyAddMissingPrototypes::runOnModule(Module &M) { - LLVM_DEBUG(dbgs() << "********** Add Missing Prototypes **********\n"); - - 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"); - - // When clang emits prototype-less C functions it uses (...), i.e. varargs - // function that take no arguments (have no sentinel). When we see a - // no-prototype attribute we expect the function have these properties. - if (!F.isVarArg()) - report_fatal_error( - "Functions with 'no-prototype' attribute must take varargs: " + - F.getName()); - unsigned NumParams = F.getFunctionType()->getNumParams(); - if (NumParams != 0) { - if (!(NumParams == 1 && F.arg_begin()->hasStructRetAttr())) - report_fatal_error("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; - for (Use &U : F.uses()) { - LLVM_DEBUG(dbgs() << "prototype-less use: " << F.getName() << "\n"); - LLVM_DEBUG(dbgs() << *U.getUser() << "\n"); - 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; - LLVM_DEBUG(dbgs() << "found function type: " << *NewType << "\n"); - } else if (NewType != DestType) { - errs() << "warning: prototype-less function used with " - "conflicting signatures: " - << F.getName() << "\n"; - LLVM_DEBUG(dbgs() << " " << *DestType << "\n"); - LLVM_DEBUG(dbgs() << " "<< *NewType << "\n"); - } - } - } - } - - if (!NewType) { - LLVM_DEBUG( - dbgs() << "could not derive a function prototype from usage: " + - F.getName() + "\n"); - // We could not derive a type for this function. In this case strip - // the isVarArg and make it a simple zero-arg function. This has more - // chance of being correct. The current signature of (...) is illegal in - // C since it doesn't have any arguments before the "...", we this at - // least makes it possible for this symbol to be resolved by the linker. - NewType = FunctionType::get(F.getFunctionType()->getReturnType(), false); - } - - Function *NewF = - Function::Create(NewType, F.getLinkage(), F.getName() + ".fixed_sig"); - NewF->setAttributes(F.getAttributes()); - NewF->removeFnAttr("no-prototype"); - Replacements.emplace_back(&F, NewF); - } - - for (auto &Pair : Replacements) { - Function *OldF = Pair.first; - Function *NewF = Pair.second; - std::string Name = OldF->getName(); - M.getFunctionList().push_back(NewF); - OldF->replaceAllUsesWith( - ConstantExpr::getPointerBitCastOrAddrSpaceCast(NewF, OldF->getType())); - OldF->eraseFromParent(); - NewF->setName(Name); - } - - return !Replacements.empty(); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp deleted file mode 100644 index 02f5cc6da77c..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//===-- WebAssemblyArgumentMove.cpp - Argument instruction moving ---------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file moves ARGUMENT instructions after ScheduleDAG scheduling. -/// -/// Arguments are really live-in registers, however, since we use virtual -/// registers and LLVM doesn't support live-in virtual registers, we're -/// currently making do with ARGUMENT instructions which are placed at the top -/// of the entry block. The trick is to get them to *stay* at the top of the -/// entry block. -/// -/// The ARGUMENTS physical register keeps these instructions pinned in place -/// during liveness-aware CodeGen passes, however one thing which does not -/// respect this is the ScheduleDAG scheduler. This pass is therefore run -/// immediately after that. -/// -/// This is all hopefully a temporary solution until we find a better solution -/// for describing the live-in nature of arguments. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyUtilities.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-argument-move" - -namespace { -class WebAssemblyArgumentMove final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyArgumentMove() : MachineFunctionPass(ID) {} - - StringRef getPassName() const override { return "WebAssembly Argument Move"; } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - AU.addPreservedID(MachineDominatorsID); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; -}; -} // end anonymous namespace - -char WebAssemblyArgumentMove::ID = 0; -INITIALIZE_PASS(WebAssemblyArgumentMove, DEBUG_TYPE, - "Move ARGUMENT instructions for WebAssembly", false, false) - -FunctionPass *llvm::createWebAssemblyArgumentMove() { - return new WebAssemblyArgumentMove(); -} - -bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Argument Move **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - bool Changed = false; - MachineBasicBlock &EntryMBB = MF.front(); - MachineBasicBlock::iterator InsertPt = EntryMBB.end(); - - // Look for the first NonArg instruction. - for (MachineInstr &MI : EntryMBB) { - if (!WebAssembly::isArgument(MI.getOpcode())) { - InsertPt = MI; - break; - } - } - - // Now move any argument instructions later in the block - // to before our first NonArg instruction. - for (MachineInstr &MI : llvm::make_range(InsertPt, EntryMBB.end())) { - if (WebAssembly::isArgument(MI.getOpcode())) { - EntryMBB.insert(InsertPt, MI.removeFromParent()); - Changed = true; - } - } - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp deleted file mode 100644 index 7f9d41da3978..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ /dev/null @@ -1,449 +0,0 @@ -//===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains a printer that converts from our internal -/// representation of machine-dependent LLVM code to the WebAssembly assembly -/// language. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyAsmPrinter.h" -#include "MCTargetDesc/WebAssemblyInstPrinter.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "MCTargetDesc/WebAssemblyTargetStreamer.h" -#include "TargetInfo/WebAssemblyTargetInfo.h" -#include "WebAssembly.h" -#include "WebAssemblyMCInstLower.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblyRegisterInfo.h" -#include "WebAssemblyTargetMachine.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/CodeGen/Analysis.h" -#include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/CodeGen/MachineConstantPool.h" -#include "llvm/CodeGen/MachineInstr.h" -#include "llvm/CodeGen/MachineModuleInfoImpls.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/DebugInfoMetadata.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Metadata.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCSectionWasm.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/MC/MCSymbolWasm.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -#define DEBUG_TYPE "asm-printer" - -extern cl::opt<bool> WasmKeepRegisters; - -//===----------------------------------------------------------------------===// -// Helpers. -//===----------------------------------------------------------------------===// - -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::v2i64, MVT::v4f32, MVT::v2f64}) - if (TRI->isTypeLegalForClass(*TRC, T)) - return T; - LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo); - llvm_unreachable("Unknown register type"); - return MVT::Other; -} - -std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { - unsigned RegNo = MO.getReg(); - assert(TargetRegisterInfo::isVirtualRegister(RegNo) && - "Unlowered physical register encountered during assembly printing"); - assert(!MFI->isVRegStackified(RegNo)); - unsigned WAReg = MFI->getWAReg(RegNo); - assert(WAReg != WebAssemblyFunctionInfo::UnusedReg); - return '$' + utostr(WAReg); -} - -WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() { - MCTargetStreamer *TS = OutStreamer->getTargetStreamer(); - return static_cast<WebAssemblyTargetStreamer *>(TS); -} - -//===----------------------------------------------------------------------===// -// WebAssemblyAsmPrinter Implementation. -//===----------------------------------------------------------------------===// - -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.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")) { - StringRef Name = - F.getFnAttribute("wasm-import-module").getValueAsString(); - Sym->setImportModule(Name); - getTargetStreamer()->emitImportModule(Sym, Name); - } - if (TM.getTargetTriple().isOSBinFormatWasm() && - F.hasFnAttribute("wasm-import-name")) { - StringRef Name = - F.getFnAttribute("wasm-import-name").getValueAsString(); - Sym->setImportName(Name); - getTargetStreamer()->emitImportName(Sym, Name); - } - } - } - - for (const auto &G : M.globals()) { - if (!G.hasInitializer() && G.hasExternalLinkage()) { - if (G.getValueType()->isSized()) { - uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); - OutStreamer->emitELFSize(getSymbol(&G), - MCConstantExpr::create(Size, OutContext)); - } - } - } - - if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) { - for (const Metadata *MD : Named->operands()) { - const auto *Tuple = dyn_cast<MDTuple>(MD); - if (!Tuple || Tuple->getNumOperands() != 2) - continue; - const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0)); - const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1)); - if (!Name || !Contents) - continue; - - OutStreamer->PushSection(); - std::string SectionName = (".custom_section." + Name->getString()).str(); - MCSectionWasm *MySection = - OutContext.getWasmSection(SectionName, SectionKind::getMetadata()); - OutStreamer->SwitchSection(MySection); - OutStreamer->EmitBytes(Contents->getString()); - OutStreamer->PopSection(); - } - } - - EmitProducerInfo(M); - EmitTargetFeatures(M); -} - -void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { - llvm::SmallVector<std::pair<std::string, std::string>, 4> Languages; - if (const NamedMDNode *Debug = M.getNamedMetadata("llvm.dbg.cu")) { - llvm::SmallSet<StringRef, 4> SeenLanguages; - for (size_t I = 0, E = Debug->getNumOperands(); I < E; ++I) { - const auto *CU = cast<DICompileUnit>(Debug->getOperand(I)); - StringRef Language = dwarf::LanguageString(CU->getSourceLanguage()); - Language.consume_front("DW_LANG_"); - if (SeenLanguages.insert(Language).second) - Languages.emplace_back(Language.str(), ""); - } - } - - llvm::SmallVector<std::pair<std::string, std::string>, 4> Tools; - if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) { - llvm::SmallSet<StringRef, 4> SeenTools; - for (size_t I = 0, E = Ident->getNumOperands(); I < E; ++I) { - const auto *S = cast<MDString>(Ident->getOperand(I)->getOperand(0)); - std::pair<StringRef, StringRef> Field = S->getString().split("version"); - StringRef Name = Field.first.trim(); - StringRef Version = Field.second.trim(); - if (SeenTools.insert(Name).second) - Tools.emplace_back(Name.str(), Version.str()); - } - } - - int FieldCount = int(!Languages.empty()) + int(!Tools.empty()); - if (FieldCount != 0) { - MCSectionWasm *Producers = OutContext.getWasmSection( - ".custom_section.producers", SectionKind::getMetadata()); - OutStreamer->PushSection(); - OutStreamer->SwitchSection(Producers); - OutStreamer->EmitULEB128IntValue(FieldCount); - for (auto &Producers : {std::make_pair("language", &Languages), - std::make_pair("processed-by", &Tools)}) { - if (Producers.second->empty()) - continue; - OutStreamer->EmitULEB128IntValue(strlen(Producers.first)); - OutStreamer->EmitBytes(Producers.first); - OutStreamer->EmitULEB128IntValue(Producers.second->size()); - for (auto &Producer : *Producers.second) { - OutStreamer->EmitULEB128IntValue(Producer.first.size()); - OutStreamer->EmitBytes(Producer.first); - OutStreamer->EmitULEB128IntValue(Producer.second.size()); - OutStreamer->EmitBytes(Producer.second); - } - } - OutStreamer->PopSection(); - } -} - -void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) { - struct FeatureEntry { - uint8_t Prefix; - StringRef Name; - }; - - // Read target features and linkage policies from module metadata - SmallVector<FeatureEntry, 4> EmittedFeatures; - for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { - std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str(); - Metadata *Policy = M.getModuleFlag(MDKey); - if (Policy == nullptr) - continue; - - FeatureEntry Entry; - Entry.Prefix = 0; - Entry.Name = KV.Key; - - if (auto *MD = cast<ConstantAsMetadata>(Policy)) - if (auto *I = cast<ConstantInt>(MD->getValue())) - Entry.Prefix = I->getZExtValue(); - - // Silently ignore invalid metadata - if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED && - Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED && - Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED) - continue; - - EmittedFeatures.push_back(Entry); - } - - if (EmittedFeatures.size() == 0) - return; - - // Emit features and linkage policies into the "target_features" section - MCSectionWasm *FeaturesSection = OutContext.getWasmSection( - ".custom_section.target_features", SectionKind::getMetadata()); - OutStreamer->PushSection(); - OutStreamer->SwitchSection(FeaturesSection); - - OutStreamer->EmitULEB128IntValue(EmittedFeatures.size()); - for (auto &F : EmittedFeatures) { - OutStreamer->EmitIntValue(F.Prefix, 1); - OutStreamer->EmitULEB128IntValue(F.Name.size()); - OutStreamer->EmitBytes(F.Name); - } - - OutStreamer->PopSection(); -} - -void WebAssemblyAsmPrinter::EmitConstantPool() { - assert(MF->getConstantPool()->getConstants().empty() && - "WebAssembly disables constant pools"); -} - -void WebAssemblyAsmPrinter::EmitJumpTableInfo() { - // Nothing to do; jump tables are incorporated into the instruction stream. -} - -void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { - 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")) { - assert(Idx->getNumOperands() == 1); - - getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( - cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue())); - } - - SmallVector<wasm::ValType, 16> Locals; - valTypesFromMVTs(MFI->getLocals(), Locals); - getTargetStreamer()->emitLocal(Locals); - - AsmPrinter::EmitFunctionBodyStart(); -} - -void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { - LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); - - switch (MI->getOpcode()) { - 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_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. Always pops one value off the stack. - if (isVerbose()) { - 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-void"); - OutStreamer->AddBlankLine(); - } - break; - case WebAssembly::COMPILER_FENCE: - // This is a compiler barrier that prevents instruction reordering during - // backend compilation, and should not be emitted. - break; - case WebAssembly::EXTRACT_EXCEPTION_I32: - case WebAssembly::EXTRACT_EXCEPTION_I32_S: - // These are pseudo instructions that simulates popping values from stack. - // We print these only when we have -wasm-keep-registers on for assembly - // readability. - if (!WasmKeepRegisters) - break; - LLVM_FALLTHROUGH; - default: { - WebAssemblyMCInstLower MCInstLowering(OutContext, *this); - MCInst TmpInst; - MCInstLowering.lower(MI, TmpInst); - EmitToStreamer(*OutStreamer, TmpInst); - break; - } - } -} - -bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, - unsigned OpNo, - const char *ExtraCode, - raw_ostream &OS) { - // First try the generic code, which knows about modifiers like 'c' and 'n'. - if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) - return false; - - if (!ExtraCode) { - const MachineOperand &MO = MI->getOperand(OpNo); - switch (MO.getType()) { - case MachineOperand::MO_Immediate: - 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: - PrintSymbolOperand(MO, OS); - return false; - case MachineOperand::MO_ExternalSymbol: - GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI); - printOffset(MO.getOffset(), OS); - return false; - case MachineOperand::MO_MachineBasicBlock: - MO.getMBB()->getSymbol()->print(OS, MAI); - return false; - default: - break; - } - } - - return true; -} - -bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, - unsigned OpNo, - const char *ExtraCode, - raw_ostream &OS) { - // The current approach to inline asm is that "r" constraints are expressed - // as local indices, rather than values on the operand stack. This simplifies - // using "r" as it eliminates the need to push and pop the values in a - // particular order, however it also makes it impossible to have an "m" - // constraint. So we don't support it. - - return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); -} - -// Force static initialization. -extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { - RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32()); - RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64()); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h deleted file mode 100644 index 4e55c81dec38..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ /dev/null @@ -1,79 +0,0 @@ -// WebAssemblyAsmPrinter.h - WebAssembly implementation of AsmPrinter-*- C++ -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYASMPRINTER_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYASMPRINTER_H - -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/Target/TargetMachine.h" - -namespace llvm { -class MCSymbol; -class WebAssemblyTargetStreamer; -class WebAssemblyMCInstLower; - -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) {} - - 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. - //===------------------------------------------------------------------===// - - bool runOnMachineFunction(MachineFunction &MF) override { - Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); - MRI = &MF.getRegInfo(); - MFI = MF.getInfo<WebAssemblyFunctionInfo>(); - return AsmPrinter::runOnMachineFunction(MF); - } - - //===------------------------------------------------------------------===// - // AsmPrinter Implementation. - //===------------------------------------------------------------------===// - - void EmitEndOfAsmFile(Module &M) override; - void EmitProducerInfo(Module &M); - void EmitTargetFeatures(Module &M); - void EmitJumpTableInfo() override; - void EmitConstantPool() override; - void EmitFunctionBodyStart() override; - void EmitInstruction(const MachineInstr *MI) override; - bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, - const char *ExtraCode, raw_ostream &OS) override; - bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, - const char *ExtraCode, raw_ostream &OS) override; - - MVT getRegType(unsigned RegNo) const; - std::string regToString(const MachineOperand &MO); - WebAssemblyTargetStreamer *getTargetStreamer(); -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp deleted file mode 100644 index 4c5d0192fc28..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCFGSort.cpp +++ /dev/null @@ -1,420 +0,0 @@ -//===-- WebAssemblyCFGSort.cpp - CFG Sorting ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements 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 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" -#include "llvm/ADT/SetVector.h" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/CodeGen/MachineLoopInfo.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-cfg-sort" - -// Option to disable EH pad first sorting. Only for testing unwind destination -// mismatches in CFGStackify. -static cl::opt<bool> WasmDisableEHPadSort( - "wasm-disable-ehpad-sort", cl::ReallyHidden, - cl::desc( - "WebAssembly: Disable EH pad-first sort order. Testing purpose only."), - cl::init(false)); - -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"; } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<MachineDominatorTree>(); - AU.addPreserved<MachineDominatorTree>(); - AU.addRequired<MachineLoopInfo>(); - AU.addPreserved<MachineLoopInfo>(); - AU.addRequired<WebAssemblyExceptionInfo>(); - AU.addPreserved<WebAssemblyExceptionInfo>(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyCFGSort() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyCFGSort::ID = 0; -INITIALIZE_PASS(WebAssemblyCFGSort, DEBUG_TYPE, - "Reorders blocks in topological order", false, false) - -FunctionPass *llvm::createWebAssemblyCFGSort() { - return new WebAssemblyCFGSort(); -} - -static void maybeUpdateTerminator(MachineBasicBlock *MBB) { -#ifndef NDEBUG - bool AnyBarrier = false; -#endif - bool AllAnalyzable = true; - for (const MachineInstr &Term : MBB->terminators()) { -#ifndef NDEBUG - AnyBarrier |= Term.isBarrier(); -#endif - AllAnalyzable &= Term.isBranch() && !Term.isIndirectBranch(); - } - assert((AnyBarrier || AllAnalyzable) && - "AnalyzeBranch needs to analyze any block with a fallthrough"); - if (AllAnalyzable) - MBB->updateTerminator(); -} - -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 (!WasmDisableEHPadSort) { - if (A->isEHPad() && !B->isEHPad()) - return false; - if (!A->isEHPad() && B->isEHPad()) - return true; - } - - return A->getNumber() > B->getNumber(); - } -}; -/// Sort blocks by their number in the opposite order.. -struct CompareBlockNumbersBackwards { - bool operator()(const MachineBasicBlock *A, - const MachineBasicBlock *B) const { - if (!WasmDisableEHPadSort) { - if (A->isEHPad() && !B->isEHPad()) - return false; - if (!A->isEHPad() && B->isEHPad()) - return true; - } - - return A->getNumber() < B->getNumber(); - } -}; -/// 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 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 class Region *R) - : TheRegion(R), NumBlocksLeft(R->getNumBlocks()) {} -}; -} // end anonymous namespace - -/// 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. - MF.RenumberBlocks(); - SmallVector<unsigned, 16> NumPredsLeft(MF.getNumBlockIDs(), 0); - for (MachineBasicBlock &MBB : MF) { - unsigned N = MBB.pred_size(); - if (MachineLoop *L = MLI.getLoopFor(&MBB)) - if (L->getHeader() == &MBB) - for (const MachineBasicBlock *Pred : MBB.predecessors()) - if (L->contains(Pred)) - --N; - NumPredsLeft[MBB.getNumber()] = N; - } - - // Topological sort the CFG, with additional constraints: - // - 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. EH blocks are picked first - // from both queues. - PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>, - CompareBlockNumbers> - Preferred; - PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>, - CompareBlockNumbersBackwards> - Ready; - - RegionInfo RI(MLI, WEI); - SmallVector<Entry, 4> Entries; - for (MachineBasicBlock *MBB = &MF.front();;) { - const Region *R = RI.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 (!Entries.empty() && Entries.back().NumBlocksLeft == 0) - Entries.pop_back(); - } - // The main topological sort logic. - for (MachineBasicBlock *Succ : MBB->successors()) { - // Ignore backedges. - if (MachineLoop *SuccL = MLI.getLoopFor(Succ)) - if (SuccL->getHeader() == Succ && SuccL->contains(MBB)) - continue; - // Decrement the predecessor count. If it's now zero, it's ready. - if (--NumPredsLeft[Succ->getNumber()] == 0) - Preferred.push(Succ); - } - // Determine the block to follow MBB. First try to find a preferred block, - // to preserve the original block order when possible. - MachineBasicBlock *Next = nullptr; - while (!Preferred.empty()) { - Next = Preferred.top(); - Preferred.pop(); - // 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() && - (!R || !R->contains(Next) || - R->getHeader()->getNumber() < Next->getNumber())) { - Ready.push(Next); - Next = nullptr; - continue; - } - break; - } - // If we didn't find a suitable block in the Preferred list, check the - // general Ready list. - if (!Next) { - // If there are no more blocks to process, we're done. - if (Ready.empty()) { - maybeUpdateTerminator(MBB); - break; - } - for (;;) { - Next = Ready.top(); - Ready.pop(); - // 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; - } - } - // Move the next block into place and iterate. - Next->moveAfter(MBB); - maybeUpdateTerminator(MBB); - MBB = Next; - } - assert(Entries.empty() && "Active sort region list not finished"); - MF.RenumberBlocks(); - -#ifndef NDEBUG - 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 - // executes once. - OnStack.insert(nullptr); - - for (auto &MBB : MF) { - assert(MBB.getNumber() >= 0 && "Renumbered blocks should be non-negative."); - const Region *Region = RI.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."); - - } 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(RI.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 region header"); - assert(OnStack.empty() && - "Control flow stack pushes and pops should be balanced."); -#endif -} - -bool WebAssemblyCFGSort::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** CFG Sorting **********\n" - "********** Function: " - << 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 sort regions. - sortBlocks(MF, MLI, WEI, MDT); - - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp deleted file mode 100644 index e6bfc5226e2e..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ /dev/null @@ -1,1378 +0,0 @@ -//===-- WebAssemblyCFGStackify.cpp - CFG Stackification -------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements a CFG stacking pass. -/// -/// 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 "WebAssembly.h" -#include "WebAssemblyExceptionInfo.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyUtilities.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/MC/MCAsmInfo.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-cfg-stackify" - -STATISTIC(NumUnwindMismatches, "Number of EH pad unwind mismatches found"); - -namespace { -class WebAssemblyCFGStackify final : public MachineFunctionPass { - StringRef getPassName() const override { return "WebAssembly CFG Stackify"; } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.addRequired<MachineDominatorTree>(); - AU.addRequired<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; - - // Placing markers. - void placeMarkers(MachineFunction &MF); - void placeBlockMarker(MachineBasicBlock &MBB); - void placeLoopMarker(MachineBasicBlock &MBB); - void placeTryMarker(MachineBasicBlock &MBB); - void removeUnnecessaryInstrs(MachineFunction &MF); - bool fixUnwindMismatches(MachineFunction &MF); - 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; - - // There can be an appendix block at the end of each function, shared for: - // - creating a correct signature for fallthrough returns - // - target for rethrows that need to unwind to the caller, but are trapped - // inside another try/catch - MachineBasicBlock *AppendixBB = nullptr; - MachineBasicBlock *getAppendixBlock(MachineFunction &MF) { - if (!AppendixBB) { - AppendixBB = MF.CreateMachineBasicBlock(); - // Give it a fake predecessor so that AsmPrinter prints its label. - AppendixBB->addSuccessor(AppendixBB); - MF.push_back(AppendixBB); - } - return AppendixBB; - } - - // Helper functions to register / unregister scope information created by - // marker instructions. - void registerScope(MachineInstr *Begin, MachineInstr *End); - void registerTryScope(MachineInstr *Begin, MachineInstr *End, - MachineBasicBlock *EHPad); - void unregisterScope(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/LOOP/TRY markers for WebAssembly scopes", false, - false) - -FunctionPass *llvm::createWebAssemblyCFGStackify() { - return new WebAssemblyCFGStackify(); -} - -/// Test whether Pred has any terminators explicitly branching to MBB, as -/// opposed to falling through. Note that it's possible (eg. in unoptimized -/// code) for a branch instruction to both branch to a block and fallthrough -/// to it, so we check the actual branch operands to see if there are any -/// explicit mentions. -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; - 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; -} - -void WebAssemblyCFGStackify::unregisterScope(MachineInstr *Begin) { - assert(BeginToEnd.count(Begin)); - MachineInstr *End = BeginToEnd[Begin]; - assert(EndToBegin.count(End)); - BeginToEnd.erase(Begin); - EndToBegin.erase(End); - MachineBasicBlock *EHPad = TryToEHPad.lookup(Begin); - if (EHPad) { - assert(EHPadToTry.count(EHPad)); - TryToEHPad.erase(Begin); - EHPadToTry.erase(EHPad); - } -} - -/// Insert a BLOCK marker for branches to MBB (if needed). -// TODO Consider a more generalized way of handling block (and also loop and -// try) signatures when we implement the multi-value proposal later. -void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) { - assert(!MBB.isEHPad()); - 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; - bool IsBrOnExn = false; - MachineInstr *BrOnExn = nullptr; - int MBBNumber = MBB.getNumber(); - for (MachineBasicBlock *Pred : MBB.predecessors()) { - if (Pred->getNumber() < MBBNumber) { - Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred; - if (explicitlyBranchesTo(Pred, &MBB)) { - IsBranchedTo = true; - if (Pred->getFirstTerminator()->getOpcode() == WebAssembly::BR_ON_EXN) { - IsBrOnExn = true; - assert(!BrOnExn && "There should be only one br_on_exn per block"); - BrOnExn = &*Pred->getFirstTerminator(); - } - } - } - } - if (!Header) - return; - if (!IsBranchedTo) - return; - - assert(&MBB != &MF.front() && "Header blocks shouldn't have predecessors"); - MachineBasicBlock *LayoutPred = MBB.getPrevNode(); - - // 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(ScopeTop->getIterator()); - } else { - // We found a scope level at an appropriate depth. - Header = ScopeTop; - break; - } - } - } - - // Decide where in Header to put the BLOCK. - - // 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, it should be after the BLOCK, because the loop is - // nested in this BLOCK. Otherwise it should be before the BLOCK. - if (MI.getOpcode() == WebAssembly::LOOP) { - auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode(); - if (MBB.getNumber() > LoopBottom->getNumber()) - AfterSet.insert(&MI); -#ifndef NDEBUG - else - BeforeSet.insert(&MI); -#endif - } - - // All previously inserted BLOCK/TRY markers should be after the BLOCK - // because they are all nested blocks. - if (MI.getOpcode() == WebAssembly::BLOCK || - MI.getOpcode() == WebAssembly::TRY) - 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. - - // 'br_on_exn' extracts exnref object and pushes variable number of values - // depending on its tag. For C++ exception, its a single i32 value, and the - // generated code will be in the form of: - // block i32 - // br_on_exn 0, $__cpp_exception - // rethrow - // end_block - WebAssembly::ExprType ReturnType = WebAssembly::ExprType::Void; - if (IsBrOnExn) { - const char *TagName = BrOnExn->getOperand(1).getSymbolName(); - if (std::strcmp(TagName, "__cpp_exception") != 0) - llvm_unreachable("Only C++ exception is supported"); - ReturnType = WebAssembly::ExprType::I32; - } - - auto InsertPos = getLatestInsertPos(Header, BeforeSet, AfterSet); - MachineInstr *Begin = - BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos), - TII.get(WebAssembly::BLOCK)) - .addImm(int64_t(ReturnType)); - - // 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 = getEarliestInsertPos(&MBB, BeforeSet, AfterSet); - MachineInstr *End = BuildMI(MBB, InsertPos, MBB.findPrevDebugLoc(InsertPos), - TII.get(WebAssembly::END_BLOCK)); - registerScope(Begin, End); - - // Track the farthest-spanning scope that ends at this point. - int Number = MBB.getNumber(); - if (!ScopeTops[Number] || - ScopeTops[Number]->getNumber() > Header->getNumber()) - ScopeTops[Number] = Header; -} - -/// Insert a LOOP marker for a loop starting at MBB (if it's a loop header). -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; - - // The operand of a LOOP is the first block after the loop. If the loop is the - // bottom of the function, insert a dummy block at the end. - MachineBasicBlock *Bottom = WebAssembly::getBottom(Loop); - auto Iter = std::next(Bottom->getIterator()); - if (Iter == MF.end()) { - getAppendixBlock(MF); - Iter = std::next(Bottom->getIterator()); - } - MachineBasicBlock *AfterLoop = &*Iter; - - // 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)); - - // 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_empty() - ? DebugLoc() - : (*AfterLoop->pred_rbegin())->findBranchDebugLoc(); - MachineInstr *End = - BuildMI(*AfterLoop, InsertPos, EndDL, TII.get(WebAssembly::END_LOOP)); - registerScope(Begin, End); - - assert((!ScopeTops[AfterLoop->getNumber()] || - ScopeTops[AfterLoop->getNumber()]->getNumber() < MBB.getNumber()) && - "With block sorting the outermost loop for a block should be first."); - if (!ScopeTops[AfterLoop->getNumber()]) - ScopeTops[AfterLoop->getNumber()] = &MBB; -} - -void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) { - assert(MBB.isEHPad()); - 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(Bottom->getIterator()); - if (Iter == MF.end()) { - getAppendixBlock(MF); - Iter = std::next(Bottom->getIterator()); - } - MachineBasicBlock *Cont = &*Iter; - - assert(Cont != &MF.front()); - MachineBasicBlock *LayoutPred = Cont->getPrevNode(); - - // 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(ScopeTop->getIterator()); - } 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 TRY. - SmallPtrSet<const MachineInstr *, 4> BeforeSet; - // Instructions that should go after the TRY. - 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, it 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) { - auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode(); - if (MBB.getNumber() > LoopBottom->getNumber()) - AfterSet.insert(&MI); -#ifndef NDEBUG - else - BeforeSet.insert(&MI); -#endif - } - - // All previously inserted BLOCK/TRY markers should be after the TRY because - // they are all nested trys. - if (MI.getOpcode() == WebAssembly::BLOCK || - MI.getOpcode() == WebAssembly::TRY) - AfterSet.insert(&MI); - -#ifndef NDEBUG - // All END_(BLOCK/LOOP/TRY) markers should be before the TRY. - 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 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() || - TermPos->getOpcode() != WebAssembly::RETHROW) { - for (const auto &MI : reverse(*Header)) { - if (MI.isCall()) { - AfterSet.insert(&MI); - // Possibly throwing calls are usually wrapped by EH_LABEL - // instructions. We don't want to split them and the call. - if (MI.getIterator() != Header->begin() && - std::prev(MI.getIterator())->isEHLabel()) - AfterSet.insert(&*std::prev(MI.getIterator())); - 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 : *Cont) { -#ifndef NDEBUG - // END_TRY should precede existing LOOP and BLOCK markers. - if (MI.getOpcode() == WebAssembly::LOOP || - MI.getOpcode() == WebAssembly::BLOCK) - 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) { - // For a LOOP to be after TRY, LOOP's BB should be after TRY's BB; if they - // are in the same BB, LOOP is always before TRY. - if (EndToBegin[&MI]->getParent()->getNumber() > Header->getNumber()) - BeforeSet.insert(&MI); -#ifndef NDEBUG - else - AfterSet.insert(&MI); -#endif - } - - // It is not possible for an END_BLOCK to be already in this block. - } - - // Mark the end of the TRY. - InsertPos = getEarliestInsertPos(Cont, BeforeSet, AfterSet); - MachineInstr *End = - BuildMI(*Cont, InsertPos, Bottom->findBranchDebugLoc(), - TII.get(WebAssembly::END_TRY)); - registerTryScope(Begin, End, &MBB); - - // Track the farthest-spanning scope that ends at this point. We create two - // mappings: (BB with 'end_try' -> BB with 'try') and (BB with 'catch' -> BB - // with 'try'). We need to create 'catch' -> 'try' mapping here too because - // markers should not span across 'catch'. For example, this should not - // happen: - // - // try - // block --| (X) - // catch | - // end_block --| - // end_try - for (int Number : {Cont->getNumber(), MBB.getNumber()}) { - if (!ScopeTops[Number] || - ScopeTops[Number]->getNumber() > Header->getNumber()) - ScopeTops[Number] = Header; - } -} - -void WebAssemblyCFGStackify::removeUnnecessaryInstrs(MachineFunction &MF) { - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - - // When there is an unconditional branch right before a catch instruction and - // it branches to the end of end_try marker, we don't need the branch, because - // it there is no exception, the control flow transfers to that point anyway. - // bb0: - // try - // ... - // br bb2 <- Not necessary - // bb1: - // catch - // ... - // bb2: - // end - for (auto &MBB : MF) { - if (!MBB.isEHPad()) - continue; - - MachineBasicBlock *TBB = nullptr, *FBB = nullptr; - SmallVector<MachineOperand, 4> Cond; - MachineBasicBlock *EHPadLayoutPred = MBB.getPrevNode(); - MachineBasicBlock *Cont = BeginToEnd[EHPadToTry[&MBB]]->getParent(); - bool Analyzable = !TII.analyzeBranch(*EHPadLayoutPred, TBB, FBB, Cond); - if (Analyzable && ((Cond.empty() && TBB && TBB == Cont) || - (!Cond.empty() && FBB && FBB == Cont))) - TII.removeBranch(*EHPadLayoutPred); - } - - // When there are block / end_block markers that overlap with try / end_try - // markers, and the block and try markers' return types are the same, the - // block /end_block markers are not necessary, because try / end_try markers - // also can serve as boundaries for branches. - // block <- Not necessary - // try - // ... - // catch - // ... - // end - // end <- Not necessary - SmallVector<MachineInstr *, 32> ToDelete; - for (auto &MBB : MF) { - for (auto &MI : MBB) { - if (MI.getOpcode() != WebAssembly::TRY) - continue; - - MachineInstr *Try = &MI, *EndTry = BeginToEnd[Try]; - MachineBasicBlock *TryBB = Try->getParent(); - MachineBasicBlock *Cont = EndTry->getParent(); - int64_t RetType = Try->getOperand(0).getImm(); - for (auto B = Try->getIterator(), E = std::next(EndTry->getIterator()); - B != TryBB->begin() && E != Cont->end() && - std::prev(B)->getOpcode() == WebAssembly::BLOCK && - E->getOpcode() == WebAssembly::END_BLOCK && - std::prev(B)->getOperand(0).getImm() == RetType; - --B, ++E) { - ToDelete.push_back(&*std::prev(B)); - ToDelete.push_back(&*E); - } - } - } - for (auto *MI : ToDelete) { - if (MI->getOpcode() == WebAssembly::BLOCK) - unregisterScope(MI); - MI->eraseFromParent(); - } -} - -bool WebAssemblyCFGStackify::fixUnwindMismatches(MachineFunction &MF) { - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - MachineRegisterInfo &MRI = MF.getRegInfo(); - - // Linearizing the control flow by placing TRY / END_TRY markers can create - // mismatches in unwind destinations. There are two kinds of mismatches we - // try to solve here. - - // 1. When an instruction may throw, but the EH pad it will unwind to can be - // different from the original CFG. - // - // Example: we have the following CFG: - // bb0: - // call @foo (if it throws, unwind to bb2) - // bb1: - // call @bar (if it throws, unwind to bb3) - // bb2 (ehpad): - // catch - // ... - // bb3 (ehpad) - // catch - // handler body - // - // And the CFG is sorted in this order. Then after placing TRY markers, it - // will look like: (BB markers are omitted) - // try $label1 - // try - // call @foo - // call @bar (if it throws, unwind to bb3) - // catch <- ehpad (bb2) - // ... - // end_try - // catch <- ehpad (bb3) - // handler body - // end_try - // - // Now if bar() throws, it is going to end up ip in bb2, not bb3, where it - // is supposed to end up. We solve this problem by - // a. Split the target unwind EH pad (here bb3) so that the handler body is - // right after 'end_try', which means we extract the handler body out of - // the catch block. We do this because this handler body should be - // somewhere branch-eable from the inner scope. - // b. Wrap the call that has an incorrect unwind destination ('call @bar' - // here) with a nested try/catch/end_try scope, and within the new catch - // block, branches to the handler body. - // c. Place a branch after the newly inserted nested end_try so it can bypass - // the handler body, which is now outside of a catch block. - // - // The result will like as follows. (new: a) means this instruction is newly - // created in the process of doing 'a' above. - // - // block $label0 (new: placeBlockMarker) - // try $label1 - // try - // call @foo - // try (new: b) - // call @bar - // catch (new: b) - // local.set n / drop (new: b) - // br $label1 (new: b) - // end_try (new: b) - // catch <- ehpad (bb2) - // end_try - // br $label0 (new: c) - // catch <- ehpad (bb3) - // end_try (hoisted: a) - // handler body - // end_block (new: placeBlockMarker) - // - // Note that the new wrapping block/end_block will be generated later in - // placeBlockMarker. - // - // TODO Currently local.set and local.gets are generated to move exnref value - // created by catches. That's because we don't support yielding values from a - // block in LLVM machine IR yet, even though it is supported by wasm. Delete - // unnecessary local.get/local.sets once yielding values from a block is - // supported. The full EH spec requires multi-value support to do this, but - // for C++ we don't yet need it because we only throw a single i32. - // - // --- - // 2. The same as 1, but in this case an instruction unwinds to a caller - // function and not another EH pad. - // - // Example: we have the following CFG: - // bb0: - // call @foo (if it throws, unwind to bb2) - // bb1: - // call @bar (if it throws, unwind to caller) - // bb2 (ehpad): - // catch - // ... - // - // And the CFG is sorted in this order. Then after placing TRY markers, it - // will look like: - // try - // call @foo - // call @bar (if it throws, unwind to caller) - // catch <- ehpad (bb2) - // ... - // end_try - // - // Now if bar() throws, it is going to end up ip in bb2, when it is supposed - // throw up to the caller. - // We solve this problem by - // a. Create a new 'appendix' BB at the end of the function and put a single - // 'rethrow' instruction (+ local.get) in there. - // b. Wrap the call that has an incorrect unwind destination ('call @bar' - // here) with a nested try/catch/end_try scope, and within the new catch - // block, branches to the new appendix block. - // - // block $label0 (new: placeBlockMarker) - // try - // call @foo - // try (new: b) - // call @bar - // catch (new: b) - // local.set n (new: b) - // br $label0 (new: b) - // end_try (new: b) - // catch <- ehpad (bb2) - // ... - // end_try - // ... - // end_block (new: placeBlockMarker) - // local.get n (new: a) <- appendix block - // rethrow (new: a) - // - // In case there are multiple calls in a BB that may throw to the caller, they - // can be wrapped together in one nested try scope. (In 1, this couldn't - // happen, because may-throwing instruction there had an unwind destination, - // i.e., it was an invoke before, and there could be only one invoke within a - // BB.) - - SmallVector<const MachineBasicBlock *, 8> EHPadStack; - // Range of intructions to be wrapped in a new nested try/catch - using TryRange = std::pair<MachineInstr *, MachineInstr *>; - // In original CFG, <unwind destionation BB, a vector of try ranges> - DenseMap<MachineBasicBlock *, SmallVector<TryRange, 4>> UnwindDestToTryRanges; - // In new CFG, <destination to branch to, a vector of try ranges> - DenseMap<MachineBasicBlock *, SmallVector<TryRange, 4>> BrDestToTryRanges; - // In new CFG, <destination to branch to, register containing exnref> - DenseMap<MachineBasicBlock *, unsigned> BrDestToExnReg; - - // Gather possibly throwing calls (i.e., previously invokes) whose current - // unwind destination is not the same as the original CFG. - for (auto &MBB : reverse(MF)) { - bool SeenThrowableInstInBB = false; - for (auto &MI : reverse(MBB)) { - if (MI.getOpcode() == WebAssembly::TRY) - EHPadStack.pop_back(); - else if (MI.getOpcode() == WebAssembly::CATCH) - EHPadStack.push_back(MI.getParent()); - - // In this loop we only gather calls that have an EH pad to unwind. So - // there will be at most 1 such call (= invoke) in a BB, so after we've - // seen one, we can skip the rest of BB. Also if MBB has no EH pad - // successor or MI does not throw, this is not an invoke. - if (SeenThrowableInstInBB || !MBB.hasEHPadSuccessor() || - !WebAssembly::mayThrow(MI)) - continue; - SeenThrowableInstInBB = true; - - // If the EH pad on the stack top is where this instruction should unwind - // next, we're good. - MachineBasicBlock *UnwindDest = nullptr; - for (auto *Succ : MBB.successors()) { - if (Succ->isEHPad()) { - UnwindDest = Succ; - break; - } - } - if (EHPadStack.back() == UnwindDest) - continue; - - // If not, record the range. - UnwindDestToTryRanges[UnwindDest].push_back(TryRange(&MI, &MI)); - } - } - - assert(EHPadStack.empty()); - - // Gather possibly throwing calls that are supposed to unwind up to the caller - // if they throw, but currently unwind to an incorrect destination. Unlike the - // loop above, there can be multiple calls within a BB that unwind to the - // caller, which we should group together in a range. - bool NeedAppendixBlock = false; - for (auto &MBB : reverse(MF)) { - MachineInstr *RangeBegin = nullptr, *RangeEnd = nullptr; // inclusive - for (auto &MI : reverse(MBB)) { - if (MI.getOpcode() == WebAssembly::TRY) - EHPadStack.pop_back(); - else if (MI.getOpcode() == WebAssembly::CATCH) - EHPadStack.push_back(MI.getParent()); - - // If MBB has an EH pad successor, this inst does not unwind to caller. - if (MBB.hasEHPadSuccessor()) - continue; - - // We wrap up the current range when we see a marker even if we haven't - // finished a BB. - if (RangeEnd && WebAssembly::isMarker(MI.getOpcode())) { - NeedAppendixBlock = true; - // Record the range. nullptr here means the unwind destination is the - // caller. - UnwindDestToTryRanges[nullptr].push_back( - TryRange(RangeBegin, RangeEnd)); - RangeBegin = RangeEnd = nullptr; // Reset range pointers - } - - // If EHPadStack is empty, that means it is correctly unwind to caller if - // it throws, so we're good. If MI does not throw, we're good too. - if (EHPadStack.empty() || !WebAssembly::mayThrow(MI)) - continue; - - // We found an instruction that unwinds to the caller but currently has an - // incorrect unwind destination. Create a new range or increment the - // currently existing range. - if (!RangeEnd) - RangeBegin = RangeEnd = &MI; - else - RangeBegin = &MI; - } - - if (RangeEnd) { - NeedAppendixBlock = true; - // Record the range. nullptr here means the unwind destination is the - // caller. - UnwindDestToTryRanges[nullptr].push_back(TryRange(RangeBegin, RangeEnd)); - RangeBegin = RangeEnd = nullptr; // Reset range pointers - } - } - - assert(EHPadStack.empty()); - // We don't have any unwind destination mismatches to resolve. - if (UnwindDestToTryRanges.empty()) - return false; - - // If we found instructions that should unwind to the caller but currently - // have incorrect unwind destination, we create an appendix block at the end - // of the function with a local.get and a rethrow instruction. - if (NeedAppendixBlock) { - auto *AppendixBB = getAppendixBlock(MF); - unsigned ExnReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass); - BuildMI(AppendixBB, DebugLoc(), TII.get(WebAssembly::RETHROW)) - .addReg(ExnReg); - // These instruction ranges should branch to this appendix BB. - for (auto Range : UnwindDestToTryRanges[nullptr]) - BrDestToTryRanges[AppendixBB].push_back(Range); - BrDestToExnReg[AppendixBB] = ExnReg; - } - - // We loop through unwind destination EH pads that are targeted from some - // inner scopes. Because these EH pads are destination of more than one scope - // now, we split them so that the handler body is after 'end_try'. - // - Before - // ehpad: - // catch - // local.set n / drop - // handler body - // ... - // cont: - // end_try - // - // - After - // ehpad: - // catch - // local.set n / drop - // brdest: (new) - // end_try (hoisted from 'cont' BB) - // handler body (taken from 'ehpad') - // ... - // cont: - for (auto &P : UnwindDestToTryRanges) { - NumUnwindMismatches++; - - // This means the destination is the appendix BB, which was separately - // handled above. - if (!P.first) - continue; - - MachineBasicBlock *EHPad = P.first; - - // Find 'catch' and 'local.set' or 'drop' instruction that follows the - // 'catch'. If -wasm-disable-explicit-locals is not set, 'catch' should be - // always followed by either 'local.set' or a 'drop', because 'br_on_exn' is - // generated after 'catch' in LateEHPrepare and we don't support blocks - // taking values yet. - MachineInstr *Catch = nullptr; - unsigned ExnReg = 0; - for (auto &MI : *EHPad) { - switch (MI.getOpcode()) { - case WebAssembly::CATCH: - Catch = &MI; - ExnReg = Catch->getOperand(0).getReg(); - break; - } - } - assert(Catch && "EH pad does not have a catch"); - assert(ExnReg != 0 && "Invalid register"); - - auto SplitPos = std::next(Catch->getIterator()); - - // Create a new BB that's gonna be the destination for branches from the - // inner mismatched scope. - MachineInstr *BeginTry = EHPadToTry[EHPad]; - MachineInstr *EndTry = BeginToEnd[BeginTry]; - MachineBasicBlock *Cont = EndTry->getParent(); - auto *BrDest = MF.CreateMachineBasicBlock(); - MF.insert(std::next(EHPad->getIterator()), BrDest); - // Hoist up the existing 'end_try'. - BrDest->insert(BrDest->end(), EndTry->removeFromParent()); - // Take out the handler body from EH pad to the new branch destination BB. - BrDest->splice(BrDest->end(), EHPad, SplitPos, EHPad->end()); - // Fix predecessor-successor relationship. - BrDest->transferSuccessors(EHPad); - EHPad->addSuccessor(BrDest); - - // All try ranges that were supposed to unwind to this EH pad now have to - // branch to this new branch dest BB. - for (auto Range : UnwindDestToTryRanges[EHPad]) - BrDestToTryRanges[BrDest].push_back(Range); - BrDestToExnReg[BrDest] = ExnReg; - - // In case we fall through to the continuation BB after the catch block, we - // now have to add a branch to it. - // - Before - // try - // ... - // (falls through to 'cont') - // catch - // handler body - // end - // <-- cont - // - // - After - // try - // ... - // br %cont (new) - // catch - // end - // handler body - // <-- cont - MachineBasicBlock *EHPadLayoutPred = &*std::prev(EHPad->getIterator()); - MachineBasicBlock *TBB = nullptr, *FBB = nullptr; - SmallVector<MachineOperand, 4> Cond; - bool Analyzable = !TII.analyzeBranch(*EHPadLayoutPred, TBB, FBB, Cond); - if (Analyzable && !TBB && !FBB) { - DebugLoc DL = EHPadLayoutPred->empty() - ? DebugLoc() - : EHPadLayoutPred->rbegin()->getDebugLoc(); - BuildMI(EHPadLayoutPred, DL, TII.get(WebAssembly::BR)).addMBB(Cont); - } - } - - // For possibly throwing calls whose unwind destinations are currently - // incorrect because of CFG linearization, we wrap them with a nested - // try/catch/end_try, and within the new catch block, we branch to the correct - // handler. - // - Before - // mbb: - // call @foo <- Unwind destination mismatch! - // ehpad: - // ... - // - // - After - // mbb: - // try (new) - // call @foo - // nested-ehpad: (new) - // catch (new) - // local.set n / drop (new) - // br %brdest (new) - // nested-end: (new) - // end_try (new) - // ehpad: - // ... - for (auto &P : BrDestToTryRanges) { - MachineBasicBlock *BrDest = P.first; - auto &TryRanges = P.second; - unsigned ExnReg = BrDestToExnReg[BrDest]; - - for (auto Range : TryRanges) { - MachineInstr *RangeBegin = nullptr, *RangeEnd = nullptr; - std::tie(RangeBegin, RangeEnd) = Range; - auto *MBB = RangeBegin->getParent(); - - // Include possible EH_LABELs in the range - if (RangeBegin->getIterator() != MBB->begin() && - std::prev(RangeBegin->getIterator())->isEHLabel()) - RangeBegin = &*std::prev(RangeBegin->getIterator()); - if (std::next(RangeEnd->getIterator()) != MBB->end() && - std::next(RangeEnd->getIterator())->isEHLabel()) - RangeEnd = &*std::next(RangeEnd->getIterator()); - - MachineBasicBlock *EHPad = nullptr; - for (auto *Succ : MBB->successors()) { - if (Succ->isEHPad()) { - EHPad = Succ; - break; - } - } - - // Create the nested try instruction. - MachineInstr *NestedTry = - BuildMI(*MBB, *RangeBegin, RangeBegin->getDebugLoc(), - TII.get(WebAssembly::TRY)) - .addImm(int64_t(WebAssembly::ExprType::Void)); - - // Create the nested EH pad and fill instructions in. - MachineBasicBlock *NestedEHPad = MF.CreateMachineBasicBlock(); - MF.insert(std::next(MBB->getIterator()), NestedEHPad); - NestedEHPad->setIsEHPad(); - NestedEHPad->setIsEHScopeEntry(); - BuildMI(NestedEHPad, RangeEnd->getDebugLoc(), TII.get(WebAssembly::CATCH), - ExnReg); - BuildMI(NestedEHPad, RangeEnd->getDebugLoc(), TII.get(WebAssembly::BR)) - .addMBB(BrDest); - - // Create the nested continuation BB and end_try instruction. - MachineBasicBlock *NestedCont = MF.CreateMachineBasicBlock(); - MF.insert(std::next(NestedEHPad->getIterator()), NestedCont); - MachineInstr *NestedEndTry = - BuildMI(*NestedCont, NestedCont->begin(), RangeEnd->getDebugLoc(), - TII.get(WebAssembly::END_TRY)); - // In case MBB has more instructions after the try range, move them to the - // new nested continuation BB. - NestedCont->splice(NestedCont->end(), MBB, - std::next(RangeEnd->getIterator()), MBB->end()); - registerTryScope(NestedTry, NestedEndTry, NestedEHPad); - - // Fix predecessor-successor relationship. - NestedCont->transferSuccessors(MBB); - if (EHPad) - NestedCont->removeSuccessor(EHPad); - MBB->addSuccessor(NestedEHPad); - MBB->addSuccessor(NestedCont); - NestedEHPad->addSuccessor(BrDest); - } - } - - // Renumber BBs and recalculate ScopeTop info because new BBs might have been - // created and inserted above. - MF.RenumberBlocks(); - ScopeTops.clear(); - ScopeTops.resize(MF.getNumBlockIDs()); - for (auto &MBB : reverse(MF)) { - for (auto &MI : reverse(MBB)) { - if (ScopeTops[MBB.getNumber()]) - break; - switch (MI.getOpcode()) { - case WebAssembly::END_BLOCK: - case WebAssembly::END_LOOP: - case WebAssembly::END_TRY: - ScopeTops[MBB.getNumber()] = EndToBegin[&MI]->getParent(); - break; - case WebAssembly::CATCH: - ScopeTops[MBB.getNumber()] = EHPadToTry[&MBB]->getParent(); - break; - } - } - } - - // Recompute the dominator tree. - getAnalysis<MachineDominatorTree>().runOnMachineFunction(MF); - - // Place block markers for newly added branches. - SmallVector <MachineBasicBlock *, 8> BrDests; - for (auto &P : BrDestToTryRanges) - BrDests.push_back(P.first); - llvm::sort(BrDests, - [&](const MachineBasicBlock *A, const MachineBasicBlock *B) { - auto ANum = A->getNumber(); - auto BNum = B->getNumber(); - return ANum < BNum; - }); - for (auto *Dest : BrDests) - placeBlockMarker(*Dest); - - return true; -} - -static unsigned -getDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack, - const MachineBasicBlock *MBB) { - unsigned Depth = 0; - for (auto X : reverse(Stack)) { - if (X == MBB) - break; - ++Depth; - } - assert(Depth < Stack.size() && "Branch destination should be in scope"); - return Depth; -} - -/// In normal assembly languages, when the end of a function is unreachable, -/// because the function ends in an infinite loop or a noreturn call or similar, -/// it isn't necessary to worry about the function return type at the end of -/// the function, because it's never reached. However, in WebAssembly, blocks -/// 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. -void WebAssemblyCFGStackify::fixEndsAtEndOfFunction(MachineFunction &MF) { - const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - assert(MFI.getResults().size() <= 1); - - if (MFI.getResults().empty()) - return; - - 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: - case MVT::v8i16: - case MVT::v4i32: - case MVT::v2i64: - case MVT::v4f32: - case MVT::v2f64: - RetType = WebAssembly::ExprType::V128; - break; - case MVT::exnref: - RetType = WebAssembly::ExprType::Exnref; - break; - default: - llvm_unreachable("unexpected return type"); - } - - for (MachineBasicBlock &MBB : reverse(MF)) { - for (MachineInstr &MI : reverse(MBB)) { - if (MI.isPosition() || MI.isDebugInstr()) - continue; - if (MI.getOpcode() == WebAssembly::END_BLOCK) { - EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType)); - continue; - } - if (MI.getOpcode() == WebAssembly::END_LOOP) { - EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType)); - continue; - } - // Something other than an `end`. We're done. - return; - } - } -} - -// WebAssembly functions end with an end instruction, as if the function body -// were a block. -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/TRY/BLOCK markers at appropriate places. -void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) { - // 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); - - const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo(); - for (auto &MBB : MF) { - if (MBB.isEHPad()) { - // Place the TRY for MBB if MBB is the EH pad of an exception. - if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm && - MF.getFunction().hasPersonalityFn()) - placeTryMarker(MBB); - } else { - // Place the BLOCK for MBB if MBB is branched to from above. - placeBlockMarker(MBB); - } - } - // Fix mismatches in unwind destinations induced by linearizing the code. - fixUnwindMismatches(MF); -} - -void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) { - // Now rewrite references to basic blocks to be depth immediates. - SmallVector<const MachineBasicBlock *, 8> Stack; - for (auto &MBB : reverse(MF)) { - for (auto I = MBB.rbegin(), E = MBB.rend(); I != E; ++I) { - MachineInstr &MI = *I; - switch (MI.getOpcode()) { - case WebAssembly::BLOCK: - case WebAssembly::TRY: - assert(ScopeTops[Stack.back()->getNumber()]->getNumber() <= - MBB.getNumber() && - "Block/try marker should be balanced"); - Stack.pop_back(); - 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(EndToBegin[&MI]->getParent()); - break; - - default: - if (MI.isTerminator()) { - // Rewrite MBB operands to be depth immediates. - SmallVector<MachineOperand, 4> Ops(MI.operands()); - while (MI.getNumOperands() > 0) - MI.RemoveOperand(MI.getNumOperands() - 1); - for (auto MO : Ops) { - if (MO.isMBB()) - MO = MachineOperand::CreateImm(getDepth(Stack, MO.getMBB())); - MI.addOperand(MF, MO); - } - } - break; - } - } - } - assert(Stack.empty() && "Control flow should be balanced"); -} - -void WebAssemblyCFGStackify::releaseMemory() { - ScopeTops.clear(); - BeginToEnd.clear(); - EndToBegin.clear(); - TryToEHPad.clear(); - EHPadToTry.clear(); - AppendixBB = nullptr; -} - -bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** CFG Stackifying **********\n" - "********** Function: " - << MF.getName() << '\n'); - const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo(); - - releaseMemory(); - - // Liveness is not tracked for VALUE_STACK physreg. - MF.getRegInfo().invalidateLiveness(); - - // Place the BLOCK/LOOP/TRY markers to indicate the beginnings of scopes. - placeMarkers(MF); - - // Remove unnecessary instructions possibly introduced by try/end_trys. - if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm && - MF.getFunction().hasPersonalityFn()) - removeUnnecessaryInstrs(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); - - MF.getInfo<WebAssemblyFunctionInfo>()->setCFGStackified(); - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp deleted file mode 100644 index 2537e6042b1e..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp +++ /dev/null @@ -1,150 +0,0 @@ -//===-- WebAssemblyCallIndirectFixup.cpp - Fix call_indirects -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file converts pseudo call_indirect instructions into real -/// call_indirects. -/// -/// The order of arguments for a call_indirect is the arguments to the function -/// call, followed by the function pointer. There's no natural way to express -/// a machineinstr with varargs followed by one more arg, so we express it as -/// the function pointer followed by varargs, then rewrite it here. -/// -/// We need to rewrite the order of the arguments on the machineinstrs -/// themselves so that register stackification knows the order they'll be -/// executed in. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_* -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/CodeGen/LiveIntervals.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-call-indirect-fixup" - -namespace { -class WebAssemblyCallIndirectFixup final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly CallIndirect Fixup"; - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyCallIndirectFixup() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyCallIndirectFixup::ID = 0; -INITIALIZE_PASS(WebAssemblyCallIndirectFixup, DEBUG_TYPE, - "Rewrite call_indirect argument orderings", false, false) - -FunctionPass *llvm::createWebAssemblyCallIndirectFixup() { - return new WebAssemblyCallIndirectFixup(); -} - -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_v2i64: - return CALL_INDIRECT_v2i64; - case PCALL_INDIRECT_v4f32: - return CALL_INDIRECT_v4f32; - case PCALL_INDIRECT_v2f64: - return CALL_INDIRECT_v2f64; - case PCALL_INDIRECT_exnref: - return CALL_INDIRECT_exnref; - case PRET_CALL_INDIRECT: - return RET_CALL_INDIRECT; - default: - return INSTRUCTION_LIST_END; - } -} - -static bool isPseudoCallIndirect(const MachineInstr &MI) { - return getNonPseudoCallIndirectOpcode(MI) != - WebAssembly::INSTRUCTION_LIST_END; -} - -bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** Fixing up CALL_INDIRECTs **********\n" - << "********** Function: " << MF.getName() << '\n'); - - bool Changed = false; - const WebAssemblyInstrInfo *TII = - MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - - for (MachineBasicBlock &MBB : MF) { - for (MachineInstr &MI : MBB) { - if (isPseudoCallIndirect(MI)) { - LLVM_DEBUG(dbgs() << "Found call_indirect: " << MI << '\n'); - - // Rewrite pseudo to non-pseudo - const MCInstrDesc &Desc = TII->get(getNonPseudoCallIndirectOpcode(MI)); - MI.setDesc(Desc); - - // Rewrite argument order - SmallVector<MachineOperand, 8> Ops; - - // Set up a placeholder for the type signature immediate. - Ops.push_back(MachineOperand::CreateImm(0)); - - // Set up the flags immediate, which currently has no defined flags - // so it's always zero. - Ops.push_back(MachineOperand::CreateImm(0)); - - for (const MachineOperand &MO : - 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())); - - // Replace the instructions operands. - while (MI.getNumOperands() > MI.getDesc().getNumDefs()) - MI.RemoveOperand(MI.getNumOperands() - 1); - for (const MachineOperand &MO : Ops) - MI.addOperand(MO); - - LLVM_DEBUG(dbgs() << " After transform: " << MI); - Changed = true; - } - } - } - - LLVM_DEBUG(dbgs() << "\nDone fixing up CALL_INDIRECTs\n\n"); - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp deleted file mode 100644 index 579377c9a5d7..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//===-- WebAssemblyDebugValueManager.cpp - WebAssembly DebugValue Manager -===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements 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/contrib/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h deleted file mode 100644 index 06e8805b5ad0..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h +++ /dev/null @@ -1,37 +0,0 @@ -// WebAssemblyDebugValueManager.h - WebAssembly DebugValue Manager -*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the declaration of the WebAssembly-specific -/// 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/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp deleted file mode 100644 index 0387957b14c2..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp +++ /dev/null @@ -1,185 +0,0 @@ -//===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief This file implements WebAssemblyException information analysis. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyExceptionInfo.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssemblyUtilities.h" -#include "llvm/ADT/PostOrderIterator.h" -#include "llvm/CodeGen/MachineDominanceFrontier.h" -#include "llvm/CodeGen/MachineDominators.h" - -using namespace llvm; - -#define DEBUG_TYPE "wasm-exception-info" - -char WebAssemblyExceptionInfo::ID = 0; - -INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE, - "WebAssembly Exception Information", true, true) -INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) -INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier) -INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE, - "WebAssembly Exception Information", true, true) - -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>(); - recalculate(MDT, MDF); - return false; -} - -void WebAssemblyExceptionInfo::recalculate( - MachineDominatorTree &MDT, const MachineDominanceFrontier &MDF) { - // Postorder traversal of the dominator tree. - SmallVector<WebAssemblyException *, 8> Exceptions; - for (auto DomNode : post_order(&MDT)) { - MachineBasicBlock *EHPad = DomNode->getBlock(); - if (!EHPad->isEHPad()) - continue; - auto *WE = new WebAssemblyException(EHPad); - discoverAndMapException(WE, MDT, MDF); - Exceptions.push_back(WE); - } - - // Add BBs to exceptions - for (auto DomNode : post_order(&MDT)) { - MachineBasicBlock *MBB = DomNode->getBlock(); - WebAssemblyException *WE = getExceptionFor(MBB); - for (; WE; WE = WE->getParentException()) - WE->addBlock(MBB); - } - - // Add subexceptions to exceptions - for (auto *WE : Exceptions) { - if (WE->getParentException()) - WE->getParentException()->getSubExceptions().push_back(WE); - else - addTopLevelException(WE); - } - - // For convenience, Blocks and SubExceptions are inserted in postorder. - // Reverse the lists. - for (auto *WE : Exceptions) { - WE->reverseBlock(); - std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end()); - } -} - -void WebAssemblyExceptionInfo::releaseMemory() { - BBMap.clear(); - DeleteContainerPointers(TopLevelExceptions); - TopLevelExceptions.clear(); -} - -void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired<MachineDominatorTree>(); - AU.addRequired<MachineDominanceFrontier>(); - MachineFunctionPass::getAnalysisUsage(AU); -} - -void WebAssemblyExceptionInfo::discoverAndMapException( - WebAssemblyException *WE, const MachineDominatorTree &MDT, - const MachineDominanceFrontier &MDF) { - unsigned NumBlocks = 0; - unsigned NumSubExceptions = 0; - - // Map blocks that belong to a catchpad / cleanuppad - MachineBasicBlock *EHPad = WE->getEHPad(); - SmallVector<MachineBasicBlock *, 8> WL; - WL.push_back(EHPad); - while (!WL.empty()) { - MachineBasicBlock *MBB = WL.pop_back_val(); - - // Find its outermost discovered exception. If this is a discovered block, - // check if it is already discovered to be a subexception of this exception. - WebAssemblyException *SubE = getOutermostException(MBB); - if (SubE) { - if (SubE != WE) { - // Discover a subexception of this exception. - SubE->setParentException(WE); - ++NumSubExceptions; - NumBlocks += SubE->getBlocksVector().capacity(); - // All blocks that belong to this subexception have been already - // discovered. Skip all of them. Add the subexception's landing pad's - // dominance frontier to the worklist. - for (auto &Frontier : MDF.find(SubE->getEHPad())->second) - if (MDT.dominates(EHPad, Frontier)) - WL.push_back(Frontier); - } - continue; - } - - // This is an undiscovered block. Map it to the current exception. - changeExceptionFor(MBB, WE); - ++NumBlocks; - - // Add successors dominated by the current BB to the worklist. - for (auto *Succ : MBB->successors()) - if (MDT.dominates(EHPad, Succ)) - WL.push_back(Succ); - } - - WE->getSubExceptions().reserve(NumSubExceptions); - WE->reserveBlocks(NumBlocks); -} - -WebAssemblyException * -WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const { - WebAssemblyException *WE = getExceptionFor(MBB); - if (WE) { - while (WebAssemblyException *Parent = WE->getParentException()) - WE = Parent; - } - return WE; -} - -void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const { - OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth() - << " containing: "; - - for (unsigned I = 0; I < getBlocks().size(); ++I) { - MachineBasicBlock *MBB = getBlocks()[I]; - if (I) - OS << ", "; - OS << "%bb." << MBB->getNumber(); - if (const auto *BB = MBB->getBasicBlock()) - if (BB->hasName()) - OS << "." << BB->getName(); - - if (getEHPad() == MBB) - OS << " (landing-pad)"; - } - OS << "\n"; - - for (auto &SubE : SubExceptions) - SubE->print(OS, Depth + 2); -} - -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) -LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); } -#endif - -raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) { - WE.print(OS); - return OS; -} - -void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const { - for (auto *WE : TopLevelExceptions) - WE->print(OS); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h deleted file mode 100644 index 9a90d7df7d47..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.h +++ /dev/null @@ -1,169 +0,0 @@ -//===-- WebAssemblyExceptionInfo.h - WebAssembly Exception Info -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief This file implements WebAssemblyException information analysis. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYEXCEPTIONINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYEXCEPTIONINFO_H - -#include "WebAssembly.h" -#include "llvm/ADT/SetVector.h" -#include "llvm/CodeGen/MachineFunctionPass.h" - -namespace llvm { - -class MachineDominatorTree; -class MachineDominanceFrontier; - -// WebAssembly instructions for exception handling are structured as follows: -// try -// instructions* -// catch ----| -// instructions* | -> A WebAssemblyException consists of this region -// end ----| -// -// A WebAssemblyException object contains BBs that belong to a 'catch' part of -// the try-catch-end structure to be created later. 'try' and 'end' markers -// are not present at this stage and will be generated in CFGStackify pass. -// Because CFGSort requires all the BBs within a catch part to be sorted -// together as it does for loops, this pass calculates the nesting structure of -// catch part of exceptions in a function. -// -// An exception catch part is defined as a BB with catch instruction and all -// other BBs dominated by this BB. -class WebAssemblyException { - MachineBasicBlock *EHPad = nullptr; - - WebAssemblyException *ParentException = nullptr; - std::vector<WebAssemblyException *> SubExceptions; - std::vector<MachineBasicBlock *> Blocks; - SmallPtrSet<const MachineBasicBlock *, 8> BlockSet; - -public: - WebAssemblyException(MachineBasicBlock *EHPad) : EHPad(EHPad) {} - ~WebAssemblyException() { DeleteContainerPointers(SubExceptions); } - WebAssemblyException(const WebAssemblyException &) = delete; - const WebAssemblyException &operator=(const WebAssemblyException &) = delete; - - MachineBasicBlock *getEHPad() const { return EHPad; } - MachineBasicBlock *getHeader() const { return EHPad; } - WebAssemblyException *getParentException() const { return ParentException; } - void setParentException(WebAssemblyException *WE) { ParentException = WE; } - - bool contains(const WebAssemblyException *WE) const { - if (WE == this) - return true; - if (!WE) - return false; - return contains(WE->getParentException()); - } - bool contains(const MachineBasicBlock *MBB) const { - return BlockSet.count(MBB); - } - - void addBlock(MachineBasicBlock *MBB) { - Blocks.push_back(MBB); - BlockSet.insert(MBB); - } - ArrayRef<MachineBasicBlock *> getBlocks() const { return Blocks; } - using block_iterator = typename ArrayRef<MachineBasicBlock *>::const_iterator; - block_iterator block_begin() const { return getBlocks().begin(); } - block_iterator block_end() const { return getBlocks().end(); } - inline iterator_range<block_iterator> blocks() const { - return make_range(block_begin(), block_end()); - } - unsigned getNumBlocks() const { return Blocks.size(); } - std::vector<MachineBasicBlock *> &getBlocksVector() { return Blocks; } - - const std::vector<WebAssemblyException *> &getSubExceptions() const { - return SubExceptions; - } - std::vector<WebAssemblyException *> &getSubExceptions() { - return SubExceptions; - } - void addSubException(WebAssemblyException *E) { SubExceptions.push_back(E); } - using iterator = typename std::vector<WebAssemblyException *>::const_iterator; - iterator begin() const { return SubExceptions.begin(); } - iterator end() const { return SubExceptions.end(); } - - void reserveBlocks(unsigned Size) { Blocks.reserve(Size); } - void reverseBlock(unsigned From = 0) { - std::reverse(Blocks.begin() + From, Blocks.end()); - } - - // Return the nesting level. An outermost one has depth 1. - unsigned getExceptionDepth() const { - unsigned D = 1; - for (const WebAssemblyException *CurException = ParentException; - CurException; CurException = CurException->ParentException) - ++D; - return D; - } - - void print(raw_ostream &OS, unsigned Depth = 0) const; - void dump() const; -}; - -raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE); - -class WebAssemblyExceptionInfo final : public MachineFunctionPass { - // Mapping of basic blocks to the innermost exception they occur in - DenseMap<const MachineBasicBlock *, WebAssemblyException *> BBMap; - std::vector<WebAssemblyException *> TopLevelExceptions; - - void discoverAndMapException(WebAssemblyException *WE, - const MachineDominatorTree &MDT, - const MachineDominanceFrontier &MDF); - WebAssemblyException *getOutermostException(MachineBasicBlock *MBB) const; - -public: - static char ID; - WebAssemblyExceptionInfo() : MachineFunctionPass(ID) { - initializeWebAssemblyExceptionInfoPass(*PassRegistry::getPassRegistry()); - } - ~WebAssemblyExceptionInfo() override { releaseMemory(); } - WebAssemblyExceptionInfo(const WebAssemblyExceptionInfo &) = delete; - WebAssemblyExceptionInfo & - operator=(const WebAssemblyExceptionInfo &) = delete; - - bool runOnMachineFunction(MachineFunction &) override; - void releaseMemory() override; - void recalculate(MachineDominatorTree &MDT, - const MachineDominanceFrontier &MDF); - void getAnalysisUsage(AnalysisUsage &AU) const override; - - bool empty() const { return TopLevelExceptions.empty(); } - - // Return the innermost exception that MBB lives in. If the block is not in an - // exception, null is returned. - WebAssemblyException *getExceptionFor(const MachineBasicBlock *MBB) const { - return BBMap.lookup(MBB); - } - - void changeExceptionFor(MachineBasicBlock *MBB, WebAssemblyException *WE) { - if (!WE) { - BBMap.erase(MBB); - return; - } - BBMap[MBB] = WE; - } - - void addTopLevelException(WebAssemblyException *WE) { - assert(!WE->getParentException() && "Not a top level exception!"); - TopLevelExceptions.push_back(WE); - } - - void print(raw_ostream &OS, const Module *M = nullptr) const override; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp deleted file mode 100644 index dbd62179f055..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ /dev/null @@ -1,399 +0,0 @@ -//===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file converts any remaining registers into WebAssembly locals. -/// -/// After register stackification and register coloring, convert non-stackified -/// registers into locals, inserting explicit local.get and local.set -/// instructions. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyUtilities.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-explicit-locals" - -// 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 { -class WebAssemblyExplicitLocals final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Explicit Locals"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyExplicitLocals::ID = 0; -INITIALIZE_PASS(WebAssemblyExplicitLocals, DEBUG_TYPE, - "Convert registers to WebAssembly locals", false, false) - -FunctionPass *llvm::createWebAssemblyExplicitLocals() { - return new WebAssemblyExplicitLocals(); -} - -/// Return a local id number for the given register, assigning it a new one -/// if it doesn't yet have one. -static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local, - unsigned &CurLocal, unsigned Reg) { - auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal)); - if (P.second) - ++CurLocal; - return P.first->second; -} - -/// Get the appropriate drop opcode for the given register class. -static unsigned getDropOpcode(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return WebAssembly::DROP_I32; - if (RC == &WebAssembly::I64RegClass) - return WebAssembly::DROP_I64; - if (RC == &WebAssembly::F32RegClass) - return WebAssembly::DROP_F32; - if (RC == &WebAssembly::F64RegClass) - return WebAssembly::DROP_F64; - if (RC == &WebAssembly::V128RegClass) - return WebAssembly::DROP_V128; - if (RC == &WebAssembly::EXNREFRegClass) - return WebAssembly::DROP_EXNREF; - llvm_unreachable("Unexpected register class"); -} - -/// Get the appropriate local.get opcode for the given register class. -static unsigned getLocalGetOpcode(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return WebAssembly::LOCAL_GET_I32; - if (RC == &WebAssembly::I64RegClass) - return WebAssembly::LOCAL_GET_I64; - if (RC == &WebAssembly::F32RegClass) - return WebAssembly::LOCAL_GET_F32; - if (RC == &WebAssembly::F64RegClass) - return WebAssembly::LOCAL_GET_F64; - if (RC == &WebAssembly::V128RegClass) - return WebAssembly::LOCAL_GET_V128; - if (RC == &WebAssembly::EXNREFRegClass) - return WebAssembly::LOCAL_GET_EXNREF; - llvm_unreachable("Unexpected register class"); -} - -/// Get the appropriate local.set opcode for the given register class. -static unsigned getLocalSetOpcode(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return WebAssembly::LOCAL_SET_I32; - if (RC == &WebAssembly::I64RegClass) - return WebAssembly::LOCAL_SET_I64; - if (RC == &WebAssembly::F32RegClass) - return WebAssembly::LOCAL_SET_F32; - if (RC == &WebAssembly::F64RegClass) - return WebAssembly::LOCAL_SET_F64; - if (RC == &WebAssembly::V128RegClass) - return WebAssembly::LOCAL_SET_V128; - if (RC == &WebAssembly::EXNREFRegClass) - return WebAssembly::LOCAL_SET_EXNREF; - llvm_unreachable("Unexpected register class"); -} - -/// Get the appropriate local.tee opcode for the given register class. -static unsigned getLocalTeeOpcode(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return WebAssembly::LOCAL_TEE_I32; - if (RC == &WebAssembly::I64RegClass) - return WebAssembly::LOCAL_TEE_I64; - if (RC == &WebAssembly::F32RegClass) - return WebAssembly::LOCAL_TEE_F32; - if (RC == &WebAssembly::F64RegClass) - return WebAssembly::LOCAL_TEE_F64; - if (RC == &WebAssembly::V128RegClass) - return WebAssembly::LOCAL_TEE_V128; - if (RC == &WebAssembly::EXNREFRegClass) - return WebAssembly::LOCAL_TEE_EXNREF; - llvm_unreachable("Unexpected register class"); -} - -/// Get the type associated with the given register class. -static MVT typeForRegClass(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return MVT::i32; - if (RC == &WebAssembly::I64RegClass) - return MVT::i64; - if (RC == &WebAssembly::F32RegClass) - return MVT::f32; - if (RC == &WebAssembly::F64RegClass) - return MVT::f64; - if (RC == &WebAssembly::V128RegClass) - return MVT::v16i8; - if (RC == &WebAssembly::EXNREFRegClass) - return MVT::exnref; - llvm_unreachable("unrecognized register class"); -} - -/// Given a MachineOperand of a stackified vreg, return the instruction at the -/// start of the expression tree. -static MachineInstr *findStartOfTree(MachineOperand &MO, - MachineRegisterInfo &MRI, - WebAssemblyFunctionInfo &MFI) { - unsigned Reg = MO.getReg(); - assert(MFI.isVRegStackified(Reg)); - MachineInstr *Def = MRI.getVRegDef(Reg); - - // Find the first stackified use and proceed from there. - for (MachineOperand &DefMO : Def->explicit_uses()) { - if (!DefMO.isReg()) - continue; - return findStartOfTree(DefMO, MRI, MFI); - } - - // If there were no stackified uses, we've reached the start. - return Def; -} - -bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** Make Locals Explicit **********\n" - "********** Function: " - << MF.getName() << '\n'); - - // Disable this pass if directed to do so. - if (WasmDisableExplicitLocals) - return false; - - bool Changed = false; - MachineRegisterInfo &MRI = MF.getRegInfo(); - WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - - // Map non-stackified virtual registers to their local ids. - DenseMap<unsigned, unsigned> Reg2Local; - - // Handle ARGUMENTS first to ensure that they get the designated numbers. - for (MachineBasicBlock::iterator I = MF.begin()->begin(), - E = MF.begin()->end(); - I != E;) { - MachineInstr &MI = *I++; - if (!WebAssembly::isArgument(MI.getOpcode())) - break; - unsigned Reg = MI.getOperand(0).getReg(); - assert(!MFI.isVRegStackified(Reg)); - Reg2Local[Reg] = static_cast<unsigned>(MI.getOperand(1).getImm()); - MI.eraseFromParent(); - Changed = true; - } - - // Start assigning local numbers after the last parameter. - 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)); - - // Visit each instruction in the function. - for (MachineBasicBlock &MBB : MF) { - for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) { - MachineInstr &MI = *I++; - assert(!WebAssembly::isArgument(MI.getOpcode())); - - if (MI.isDebugInstr() || MI.isLabel()) - continue; - - // 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.getOpcode())) { - assert(MFI.isVRegStackified(MI.getOperand(0).getReg())); - assert(!MFI.isVRegStackified(MI.getOperand(1).getReg())); - unsigned OldReg = MI.getOperand(2).getReg(); - const TargetRegisterClass *RC = MRI.getRegClass(OldReg); - - // Stackify the input if it isn't stackified yet. - if (!MFI.isVRegStackified(OldReg)) { - unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); - unsigned NewReg = MRI.createVirtualRegister(RC); - unsigned Opc = getLocalGetOpcode(RC); - BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg) - .addImm(LocalId); - MI.getOperand(2).setReg(NewReg); - MFI.stackifyVReg(NewReg); - } - - // Replace the TEE with a LOCAL_TEE. - unsigned LocalId = - getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg()); - unsigned Opc = getLocalTeeOpcode(RC); - BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), - MI.getOperand(0).getReg()) - .addImm(LocalId) - .addReg(MI.getOperand(2).getReg()); - - MI.eraseFromParent(); - Changed = true; - continue; - } - - // 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) { - unsigned OldReg = MI.getOperand(0).getReg(); - if (!MFI.isVRegStackified(OldReg)) { - const TargetRegisterClass *RC = MRI.getRegClass(OldReg); - unsigned NewReg = MRI.createVirtualRegister(RC); - auto InsertPt = std::next(MI.getIterator()); - if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) { - MI.eraseFromParent(); - Changed = true; - continue; - } - if (UseEmpty[TargetRegisterInfo::virtReg2Index(OldReg)]) { - unsigned Opc = getDropOpcode(RC); - MachineInstr *Drop = - BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) - .addReg(NewReg); - // After the drop instruction, this reg operand will not be used - Drop->getOperand(0).setIsKill(); - } else { - unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); - unsigned Opc = getLocalSetOpcode(RC); - BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) - .addImm(LocalId) - .addReg(NewReg); - } - MI.getOperand(0).setReg(NewReg); - // 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 local.gets for any uses that aren't stackified yet. - MachineInstr *InsertPt = &MI; - for (MachineOperand &MO : reverse(MI.explicit_uses())) { - if (!MO.isReg()) - continue; - - unsigned OldReg = MO.getReg(); - - // Inline asm may have a def in the middle of the operands. Our contract - // with inline asm register operands is to provide local indices as - // immediates. - if (MO.isDef()) { - assert(MI.isInlineAsm()); - unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); - // If this register operand is tied to another operand, we can't - // change it to an immediate. Untie it first. - MI.untieRegOperand(MI.getOperandNo(&MO)); - MO.ChangeToImmediate(LocalId); - continue; - } - - // If we see a stackified register, prepare to insert subsequent - // local.gets before the start of its tree. - if (MFI.isVRegStackified(OldReg)) { - InsertPt = findStartOfTree(MO, MRI, MFI); - continue; - } - - // Our contract with inline asm register operands is to provide local - // indices as immediates. - if (MI.isInlineAsm()) { - unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); - // Untie it first if this reg operand is tied to another operand. - MI.untieRegOperand(MI.getOperandNo(&MO)); - MO.ChangeToImmediate(LocalId); - continue; - } - - // Insert a local.get. - unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); - const TargetRegisterClass *RC = MRI.getRegClass(OldReg); - unsigned NewReg = MRI.createVirtualRegister(RC); - unsigned Opc = getLocalGetOpcode(RC); - InsertPt = - BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg) - .addImm(LocalId); - MO.setReg(NewReg); - MFI.stackifyVReg(NewReg); - Changed = true; - } - - // Coalesce and eliminate COPY instructions. - if (WebAssembly::isCopy(MI.getOpcode())) { - MRI.replaceRegWith(MI.getOperand(1).getReg(), - MI.getOperand(0).getReg()); - MI.eraseFromParent(); - Changed = true; - } - } - } - - // Define the locals. - // TODO: Sort the locals for better compression. - MFI.setNumLocals(CurLocal - 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(RL->second - MFI.getParams().size(), - typeForRegClass(MRI.getRegClass(Reg))); - Changed = true; - } - -#ifndef NDEBUG - // Assert that all registers have been stackified at this point. - for (const MachineBasicBlock &MBB : MF) { - for (const MachineInstr &MI : MBB) { - if (MI.isDebugInstr() || MI.isLabel()) - continue; - for (const MachineOperand &MO : MI.explicit_operands()) { - assert( - (!MO.isReg() || MRI.use_empty(MO.getReg()) || - MFI.isVRegStackified(MO.getReg())) && - "WebAssemblyExplicitLocals failed to stackify a register operand"); - } - } - } -#endif - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp deleted file mode 100644 index 2552e9150833..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ /dev/null @@ -1,1417 +0,0 @@ -//===-- WebAssemblyFastISel.cpp - WebAssembly FastISel implementation -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines the WebAssembly-specific support for the FastISel -/// class. Some of the target-specific code is generated by tablegen in the file -/// WebAssemblyGenFastISel.inc, which is #included here. -/// -/// TODO: kill flags -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyTargetMachine.h" -#include "llvm/Analysis/BranchProbabilityInfo.h" -#include "llvm/CodeGen/FastISel.h" -#include "llvm/CodeGen/FunctionLoweringInfo.h" -#include "llvm/CodeGen/MachineConstantPool.h" -#include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" -#include "llvm/IR/GlobalAlias.h" -#include "llvm/IR/GlobalVariable.h" -#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" - -namespace { - -class WebAssemblyFastISel final : public FastISel { - // All possible address modes. - class Address { - public: - using BaseKind = enum { RegBase, FrameIndexBase }; - - private: - BaseKind Kind = RegBase; - union { - unsigned Reg; - int FI; - } Base; - - int64_t Offset = 0; - - const GlobalValue *GV = nullptr; - - public: - // Innocuous defaults for our address. - Address() { Base.Reg = 0; } - void setKind(BaseKind K) { - assert(!isSet() && "Can't change kind with non-zero base"); - Kind = K; - } - BaseKind getKind() const { return Kind; } - bool isRegBase() const { return Kind == RegBase; } - bool isFIBase() const { return Kind == FrameIndexBase; } - void setReg(unsigned Reg) { - assert(isRegBase() && "Invalid base register access!"); - assert(Base.Reg == 0 && "Overwriting non-zero register"); - Base.Reg = Reg; - } - unsigned getReg() const { - assert(isRegBase() && "Invalid base register access!"); - return Base.Reg; - } - void setFI(unsigned FI) { - assert(isFIBase() && "Invalid base frame index access!"); - assert(Base.FI == 0 && "Overwriting non-zero frame index"); - Base.FI = FI; - } - unsigned getFI() const { - assert(isFIBase() && "Invalid base frame index access!"); - return Base.FI; - } - - void setOffset(int64_t NewOffset) { - assert(NewOffset >= 0 && "Offsets must be non-negative"); - Offset = NewOffset; - } - int64_t getOffset() const { return Offset; } - void setGlobalValue(const GlobalValue *G) { GV = G; } - const GlobalValue *getGlobalValue() const { return GV; } - bool isSet() const { - if (isRegBase()) { - return Base.Reg != 0; - } else { - return Base.FI != 0; - } - } - }; - - /// Keep a pointer to the WebAssemblySubtarget around so that we can make the - /// right decision when generating code for different targets. - const WebAssemblySubtarget *Subtarget; - LLVMContext *Context; - -private: - // Utility helper routines - MVT::SimpleValueType getSimpleType(Type *Ty) { - EVT VT = TLI.getValueType(DL, Ty, /*AllowUnknown=*/true); - return VT.isSimple() ? VT.getSimpleVT().SimpleTy - : MVT::INVALID_SIMPLE_VALUE_TYPE; - } - MVT::SimpleValueType getLegalType(MVT::SimpleValueType VT) { - switch (VT) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - return MVT::i32; - case MVT::i32: - case MVT::i64: - case MVT::f32: - case MVT::f64: - case MVT::exnref: - return VT; - case MVT::f16: - return MVT::f32; - case MVT::v16i8: - case MVT::v8i16: - case MVT::v4i32: - case MVT::v4f32: - if (Subtarget->hasSIMD128()) - return VT; - break; - case MVT::v2i64: - case MVT::v2f64: - if (Subtarget->hasUnimplementedSIMD128()) - return VT; - break; - default: - break; - } - return MVT::INVALID_SIMPLE_VALUE_TYPE; - } - bool computeAddress(const Value *Obj, Address &Addr); - void materializeLoadStoreOperands(Address &Addr); - void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB, - MachineMemOperand *MMO); - unsigned maskI1Value(unsigned Reg, const Value *V); - unsigned getRegForI1Value(const Value *V, bool &Not); - unsigned zeroExtendToI32(unsigned Reg, const Value *V, - MVT::SimpleValueType From); - unsigned signExtendToI32(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, - MVT::SimpleValueType To); - unsigned getRegForUnsignedValue(const Value *V); - unsigned getRegForSignedValue(const Value *V); - unsigned getRegForPromotedValue(const Value *V, bool IsSigned); - unsigned notValue(unsigned Reg); - unsigned copyValue(unsigned Reg); - - // Backend specific FastISel code. - unsigned fastMaterializeAlloca(const AllocaInst *AI) override; - unsigned fastMaterializeConstant(const Constant *C) override; - bool fastLowerArguments() override; - - // Selection routines. - bool selectCall(const Instruction *I); - bool selectSelect(const Instruction *I); - bool selectTrunc(const Instruction *I); - bool selectZExt(const Instruction *I); - bool selectSExt(const Instruction *I); - bool selectICmp(const Instruction *I); - bool selectFCmp(const Instruction *I); - bool selectBitCast(const Instruction *I); - bool selectLoad(const Instruction *I); - bool selectStore(const Instruction *I); - bool selectBr(const Instruction *I); - bool selectRet(const Instruction *I); - bool selectUnreachable(const Instruction *I); - -public: - // Backend specific FastISel code. - WebAssemblyFastISel(FunctionLoweringInfo &FuncInfo, - const TargetLibraryInfo *LibInfo) - : FastISel(FuncInfo, LibInfo, /*SkipTargetIndependentISel=*/true) { - Subtarget = &FuncInfo.MF->getSubtarget<WebAssemblySubtarget>(); - Context = &FuncInfo.Fn->getContext(); - } - - bool fastSelectInstruction(const Instruction *I) override; - -#include "WebAssemblyGenFastISel.inc" -}; - -} // end anonymous namespace - -bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { - const User *U = nullptr; - unsigned Opcode = Instruction::UserOp1; - if (const auto *I = dyn_cast<Instruction>(Obj)) { - // Don't walk into other basic blocks unless the object is an alloca from - // another block, otherwise it may not have a virtual register assigned. - if (FuncInfo.StaticAllocaMap.count(static_cast<const AllocaInst *>(Obj)) || - FuncInfo.MBBMap[I->getParent()] == FuncInfo.MBB) { - Opcode = I->getOpcode(); - U = I; - } - } else if (const auto *C = dyn_cast<ConstantExpr>(Obj)) { - Opcode = C->getOpcode(); - U = C; - } - - if (auto *Ty = dyn_cast<PointerType>(Obj->getType())) - if (Ty->getAddressSpace() > 255) - // Fast instruction selection doesn't support the special - // address spaces. - return false; - - if (const auto *GV = dyn_cast<GlobalValue>(Obj)) { - if (TLI.isPositionIndependent()) - return false; - if (Addr.getGlobalValue()) - return false; - if (GV->isThreadLocal()) - return false; - Addr.setGlobalValue(GV); - return true; - } - - switch (Opcode) { - default: - break; - case Instruction::BitCast: { - // Look through bitcasts. - return computeAddress(U->getOperand(0), Addr); - } - case Instruction::IntToPtr: { - // Look past no-op inttoptrs. - if (TLI.getValueType(DL, U->getOperand(0)->getType()) == - TLI.getPointerTy(DL)) - return computeAddress(U->getOperand(0), Addr); - break; - } - case Instruction::PtrToInt: { - // Look past no-op ptrtoints. - if (TLI.getValueType(DL, U->getType()) == TLI.getPointerTy(DL)) - return computeAddress(U->getOperand(0), Addr); - break; - } - case Instruction::GetElementPtr: { - Address SavedAddr = Addr; - uint64_t TmpOffset = Addr.getOffset(); - // Non-inbounds geps can wrap; wasm's offsets can't. - if (!cast<GEPOperator>(U)->isInBounds()) - goto unsupported_gep; - // Iterate through the GEP folding the constants into offsets where - // we can. - for (gep_type_iterator GTI = gep_type_begin(U), E = gep_type_end(U); - GTI != E; ++GTI) { - const Value *Op = GTI.getOperand(); - if (StructType *STy = GTI.getStructTypeOrNull()) { - const StructLayout *SL = DL.getStructLayout(STy); - unsigned Idx = cast<ConstantInt>(Op)->getZExtValue(); - TmpOffset += SL->getElementOffset(Idx); - } else { - uint64_t S = DL.getTypeAllocSize(GTI.getIndexedType()); - for (;;) { - if (const auto *CI = dyn_cast<ConstantInt>(Op)) { - // Constant-offset addressing. - TmpOffset += CI->getSExtValue() * S; - break; - } - if (S == 1 && Addr.isRegBase() && Addr.getReg() == 0) { - // An unscaled add of a register. Set it as the new base. - unsigned Reg = getRegForValue(Op); - if (Reg == 0) - return false; - Addr.setReg(Reg); - break; - } - if (canFoldAddIntoGEP(U, Op)) { - // A compatible add with a constant operand. Fold the constant. - auto *CI = cast<ConstantInt>(cast<AddOperator>(Op)->getOperand(1)); - TmpOffset += CI->getSExtValue() * S; - // Iterate on the other operand. - Op = cast<AddOperator>(Op)->getOperand(0); - continue; - } - // Unsupported - goto unsupported_gep; - } - } - } - // Don't fold in negative offsets. - if (int64_t(TmpOffset) >= 0) { - // Try to grab the base operand now. - Addr.setOffset(TmpOffset); - if (computeAddress(U->getOperand(0), Addr)) - return true; - } - // We failed, restore everything and try the other options. - Addr = SavedAddr; - unsupported_gep: - break; - } - case Instruction::Alloca: { - const auto *AI = cast<AllocaInst>(Obj); - DenseMap<const AllocaInst *, int>::iterator SI = - FuncInfo.StaticAllocaMap.find(AI); - if (SI != FuncInfo.StaticAllocaMap.end()) { - if (Addr.isSet()) { - return false; - } - Addr.setKind(Address::FrameIndexBase); - Addr.setFI(SI->second); - return true; - } - break; - } - case Instruction::Add: { - // Adds of constants are common and easy enough. - const Value *LHS = U->getOperand(0); - const Value *RHS = U->getOperand(1); - - if (isa<ConstantInt>(LHS)) - std::swap(LHS, RHS); - - if (const auto *CI = dyn_cast<ConstantInt>(RHS)) { - uint64_t TmpOffset = Addr.getOffset() + CI->getSExtValue(); - if (int64_t(TmpOffset) >= 0) { - Addr.setOffset(TmpOffset); - return computeAddress(LHS, Addr); - } - } - - Address Backup = Addr; - if (computeAddress(LHS, Addr) && computeAddress(RHS, Addr)) - return true; - Addr = Backup; - - break; - } - case Instruction::Sub: { - // Subs of constants are common and easy enough. - const Value *LHS = U->getOperand(0); - const Value *RHS = U->getOperand(1); - - if (const auto *CI = dyn_cast<ConstantInt>(RHS)) { - int64_t TmpOffset = Addr.getOffset() - CI->getSExtValue(); - if (TmpOffset >= 0) { - Addr.setOffset(TmpOffset); - return computeAddress(LHS, Addr); - } - } - break; - } - } - if (Addr.isSet()) { - return false; - } - unsigned Reg = getRegForValue(Obj); - if (Reg == 0) - return false; - Addr.setReg(Reg); - return Addr.getReg() != 0; -} - -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; - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), Reg) - .addImm(0); - Addr.setReg(Reg); - } - } -} - -void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr, - const MachineInstrBuilder &MIB, - MachineMemOperand *MMO) { - // Set the alignment operand (this is rewritten in SetP2AlignOperands). - // TODO: Disable SetP2AlignOperands for FastISel and just do it here. - MIB.addImm(0); - - if (const GlobalValue *GV = Addr.getGlobalValue()) - MIB.addGlobalAddress(GV, Addr.getOffset()); - else - MIB.addImm(Addr.getOffset()); - - if (Addr.isRegBase()) - MIB.addReg(Addr.getReg()); - else - MIB.addFrameIndex(Addr.getFI()); - - MIB.addMemOperand(MMO); -} - -unsigned WebAssemblyFastISel::maskI1Value(unsigned Reg, const Value *V) { - return zeroExtendToI32(Reg, V, MVT::i1); -} - -unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V, bool &Not) { - if (const auto *ICmp = dyn_cast<ICmpInst>(V)) - if (const ConstantInt *C = dyn_cast<ConstantInt>(ICmp->getOperand(1))) - if (ICmp->isEquality() && C->isZero() && C->getType()->isIntegerTy(32)) { - Not = ICmp->isTrueWhenEqual(); - return getRegForValue(ICmp->getOperand(0)); - } - - Value *NotV; - if (match(V, m_Not(m_Value(NotV))) && V->getType()->isIntegerTy(32)) { - Not = true; - return getRegForValue(NotV); - } - - Not = false; - unsigned Reg = getRegForValue(V); - if (Reg == 0) - return 0; - return maskI1Value(Reg, V); -} - -unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V, - MVT::SimpleValueType From) { - if (Reg == 0) - return 0; - - switch (From) { - case MVT::i1: - // 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; - case MVT::i32: - return copyValue(Reg); - default: - return 0; - } - - unsigned Imm = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::CONST_I32), Imm) - .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); - - return Result; -} - -unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V, - MVT::SimpleValueType From) { - if (Reg == 0) - return 0; - - switch (From) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - break; - case MVT::i32: - return copyValue(Reg); - default: - return 0; - } - - unsigned Imm = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::CONST_I32), Imm) - .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); - - unsigned Right = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::SHR_S_I32), Right) - .addReg(Left) - .addReg(Imm); - - return Right; -} - -unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V, - MVT::SimpleValueType From, - MVT::SimpleValueType To) { - if (To == MVT::i64) { - if (From == MVT::i64) - return copyValue(Reg); - - Reg = zeroExtendToI32(Reg, V, From); - - unsigned Result = createResultReg(&WebAssembly::I64RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::I64_EXTEND_U_I32), Result) - .addReg(Reg); - return Result; - } - - if (To == MVT::i32) - return zeroExtendToI32(Reg, V, From); - - return 0; -} - -unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V, - MVT::SimpleValueType From, - MVT::SimpleValueType To) { - if (To == MVT::i64) { - if (From == MVT::i64) - return copyValue(Reg); - - Reg = signExtendToI32(Reg, V, From); - - unsigned Result = createResultReg(&WebAssembly::I64RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::I64_EXTEND_S_I32), Result) - .addReg(Reg); - return Result; - } - - if (To == MVT::i32) - return signExtendToI32(Reg, V, From); - - return 0; -} - -unsigned WebAssemblyFastISel::getRegForUnsignedValue(const Value *V) { - MVT::SimpleValueType From = getSimpleType(V->getType()); - MVT::SimpleValueType To = getLegalType(From); - unsigned VReg = getRegForValue(V); - if (VReg == 0) - return 0; - return zeroExtend(VReg, V, From, To); -} - -unsigned WebAssemblyFastISel::getRegForSignedValue(const Value *V) { - MVT::SimpleValueType From = getSimpleType(V->getType()); - MVT::SimpleValueType To = getLegalType(From); - unsigned VReg = getRegForValue(V); - if (VReg == 0) - return 0; - return signExtend(VReg, V, From, To); -} - -unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *V, - bool IsSigned) { - return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(V); -} - -unsigned WebAssemblyFastISel::notValue(unsigned Reg) { - assert(MRI.getRegClass(Reg) == &WebAssembly::I32RegClass); - - unsigned NotReg = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::EQZ_I32), NotReg) - .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); - return ResultReg; -} - -unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) { - DenseMap<const AllocaInst *, int>::iterator SI = - 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; - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) - .addFrameIndex(SI->second); - return ResultReg; - } - - return 0; -} - -unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) { - if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) { - if (TLI.isPositionIndependent()) - return 0; - if (GV->isThreadLocal()) - return 0; - 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); - return ResultReg; - } - - // Let target-independent code handle it. - return 0; -} - -bool WebAssemblyFastISel::fastLowerArguments() { - if (!FuncInfo.CanLowerReturn) - return false; - - const Function *F = FuncInfo.Fn; - if (F->isVarArg()) - return false; - - unsigned I = 0; - for (auto const &Arg : F->args()) { - const AttributeList &Attrs = F->getAttributes(); - if (Attrs.hasParamAttribute(I, Attribute::ByVal) || - Attrs.hasParamAttribute(I, Attribute::SwiftSelf) || - Attrs.hasParamAttribute(I, Attribute::SwiftError) || - Attrs.hasParamAttribute(I, Attribute::InAlloca) || - Attrs.hasParamAttribute(I, Attribute::Nest)) - return false; - - Type *ArgTy = Arg.getType(); - if (ArgTy->isStructTy() || ArgTy->isArrayTy()) - return false; - if (!Subtarget->hasSIMD128() && ArgTy->isVectorTy()) - return false; - - unsigned Opc; - const TargetRegisterClass *RC; - switch (getSimpleType(ArgTy)) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - case MVT::i32: - Opc = WebAssembly::ARGUMENT_i32; - RC = &WebAssembly::I32RegClass; - break; - case MVT::i64: - Opc = WebAssembly::ARGUMENT_i64; - RC = &WebAssembly::I64RegClass; - break; - case MVT::f32: - Opc = WebAssembly::ARGUMENT_f32; - RC = &WebAssembly::F32RegClass; - break; - case MVT::f64: - Opc = WebAssembly::ARGUMENT_f64; - RC = &WebAssembly::F64RegClass; - break; - case MVT::v16i8: - Opc = WebAssembly::ARGUMENT_v16i8; - RC = &WebAssembly::V128RegClass; - break; - case MVT::v8i16: - Opc = WebAssembly::ARGUMENT_v8i16; - RC = &WebAssembly::V128RegClass; - break; - case MVT::v4i32: - 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::exnref: - Opc = WebAssembly::ARGUMENT_exnref; - RC = &WebAssembly::EXNREFRegClass; - break; - default: - return false; - } - unsigned ResultReg = createResultReg(RC); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) - .addImm(I); - updateValueMap(&Arg, ResultReg); - - ++I; - } - - MRI.addLiveIn(WebAssembly::ARGUMENTS); - - auto *MFI = MF->getInfo<WebAssemblyFunctionInfo>(); - for (auto const &Arg : F->args()) { - MVT::SimpleValueType ArgTy = getLegalType(getSimpleType(Arg.getType())); - if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { - MFI->clearParamsAndResults(); - return false; - } - MFI->addParam(ArgTy); - } - - if (!F->getReturnType()->isVoidTy()) { - MVT::SimpleValueType RetTy = - getLegalType(getSimpleType(F->getReturnType())); - if (RetTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { - MFI->clearParamsAndResults(); - return false; - } - MFI->addResult(RetTy); - } - - return true; -} - -bool WebAssemblyFastISel::selectCall(const Instruction *I) { - const auto *Call = cast<CallInst>(I); - - // TODO: Support tail calls in FastISel - if (Call->isMustTailCall() || Call->isInlineAsm() || - Call->getFunctionType()->isVarArg()) - return false; - - Function *Func = Call->getCalledFunction(); - if (Func && Func->isIntrinsic()) - return false; - - bool IsDirect = Func != nullptr; - if (!IsDirect && isa<ConstantExpr>(Call->getCalledValue())) - return false; - - FunctionType *FuncTy = Call->getFunctionType(); - unsigned Opc; - bool IsVoid = FuncTy->getReturnType()->isVoidTy(); - unsigned ResultReg; - if (IsVoid) { - Opc = IsDirect ? WebAssembly::CALL_VOID : WebAssembly::PCALL_INDIRECT_VOID; - } else { - if (!Subtarget->hasSIMD128() && Call->getType()->isVectorTy()) - return false; - - MVT::SimpleValueType RetTy = getSimpleType(Call->getType()); - switch (RetTy) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - case MVT::i32: - Opc = IsDirect ? WebAssembly::CALL_i32 : WebAssembly::PCALL_INDIRECT_i32; - ResultReg = createResultReg(&WebAssembly::I32RegClass); - break; - case MVT::i64: - Opc = IsDirect ? WebAssembly::CALL_i64 : WebAssembly::PCALL_INDIRECT_i64; - ResultReg = createResultReg(&WebAssembly::I64RegClass); - break; - case MVT::f32: - Opc = IsDirect ? WebAssembly::CALL_f32 : WebAssembly::PCALL_INDIRECT_f32; - ResultReg = createResultReg(&WebAssembly::F32RegClass); - break; - case MVT::f64: - Opc = IsDirect ? WebAssembly::CALL_f64 : WebAssembly::PCALL_INDIRECT_f64; - ResultReg = createResultReg(&WebAssembly::F64RegClass); - break; - case MVT::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; - ResultReg = createResultReg(&WebAssembly::V128RegClass); - break; - case MVT::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; - ResultReg = createResultReg(&WebAssembly::V128RegClass); - break; - case MVT::v2f64: - Opc = IsDirect ? WebAssembly::CALL_v2f64 - : WebAssembly::PCALL_INDIRECT_v2f64; - ResultReg = createResultReg(&WebAssembly::V128RegClass); - break; - case MVT::exnref: - Opc = IsDirect ? WebAssembly::CALL_exnref - : WebAssembly::PCALL_INDIRECT_exnref; - ResultReg = createResultReg(&WebAssembly::EXNREFRegClass); - break; - default: - return false; - } - } - - SmallVector<unsigned, 8> Args; - for (unsigned I = 0, E = Call->getNumArgOperands(); I < E; ++I) { - Value *V = Call->getArgOperand(I); - MVT::SimpleValueType ArgTy = getSimpleType(V->getType()); - if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) - return false; - - const AttributeList &Attrs = Call->getAttributes(); - if (Attrs.hasParamAttribute(I, Attribute::ByVal) || - Attrs.hasParamAttribute(I, Attribute::SwiftSelf) || - Attrs.hasParamAttribute(I, Attribute::SwiftError) || - Attrs.hasParamAttribute(I, Attribute::InAlloca) || - Attrs.hasParamAttribute(I, Attribute::Nest)) - return false; - - unsigned Reg; - - if (Attrs.hasParamAttribute(I, Attribute::SExt)) - Reg = getRegForSignedValue(V); - else if (Attrs.hasParamAttribute(I, Attribute::ZExt)) - Reg = getRegForUnsignedValue(V); - else - Reg = getRegForValue(V); - - if (Reg == 0) - return false; - - Args.push_back(Reg); - } - - unsigned CalleeReg = 0; - if (!IsDirect) { - CalleeReg = getRegForValue(Call->getCalledValue()); - if (!CalleeReg) - return false; - } - - auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)); - - if (!IsVoid) - MIB.addReg(ResultReg, RegState::Define); - - if (IsDirect) - MIB.addGlobalAddress(Func); - else - MIB.addReg(CalleeReg); - - for (unsigned ArgReg : Args) - MIB.addReg(ArgReg); - - if (!IsVoid) - updateValueMap(Call, ResultReg); - return true; -} - -bool WebAssemblyFastISel::selectSelect(const Instruction *I) { - const auto *Select = cast<SelectInst>(I); - - bool Not; - unsigned CondReg = getRegForI1Value(Select->getCondition(), Not); - if (CondReg == 0) - return false; - - unsigned TrueReg = getRegForValue(Select->getTrueValue()); - if (TrueReg == 0) - return false; - - unsigned FalseReg = getRegForValue(Select->getFalseValue()); - if (FalseReg == 0) - return false; - - if (Not) - std::swap(TrueReg, FalseReg); - - unsigned Opc; - const TargetRegisterClass *RC; - switch (getSimpleType(Select->getType())) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - case MVT::i32: - Opc = WebAssembly::SELECT_I32; - RC = &WebAssembly::I32RegClass; - break; - case MVT::i64: - Opc = WebAssembly::SELECT_I64; - RC = &WebAssembly::I64RegClass; - break; - case MVT::f32: - Opc = WebAssembly::SELECT_F32; - RC = &WebAssembly::F32RegClass; - break; - case MVT::f64: - Opc = WebAssembly::SELECT_F64; - RC = &WebAssembly::F64RegClass; - break; - case MVT::exnref: - Opc = WebAssembly::SELECT_EXNREF; - RC = &WebAssembly::EXNREFRegClass; - break; - default: - return false; - } - - unsigned ResultReg = createResultReg(RC); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) - .addReg(TrueReg) - .addReg(FalseReg) - .addReg(CondReg); - - updateValueMap(Select, ResultReg); - return true; -} - -bool WebAssemblyFastISel::selectTrunc(const Instruction *I) { - const auto *Trunc = cast<TruncInst>(I); - - unsigned Reg = getRegForValue(Trunc->getOperand(0)); - if (Reg == 0) - return false; - - if (Trunc->getOperand(0)->getType()->isIntegerTy(64)) { - unsigned Result = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::I32_WRAP_I64), Result) - .addReg(Reg); - Reg = Result; - } - - updateValueMap(Trunc, Reg); - return true; -} - -bool WebAssemblyFastISel::selectZExt(const Instruction *I) { - const auto *ZExt = cast<ZExtInst>(I); - - const Value *Op = ZExt->getOperand(0); - MVT::SimpleValueType From = getSimpleType(Op->getType()); - MVT::SimpleValueType To = getLegalType(getSimpleType(ZExt->getType())); - unsigned In = getRegForValue(Op); - if (In == 0) - return false; - unsigned Reg = zeroExtend(In, Op, From, To); - if (Reg == 0) - return false; - - updateValueMap(ZExt, Reg); - return true; -} - -bool WebAssemblyFastISel::selectSExt(const Instruction *I) { - const auto *SExt = cast<SExtInst>(I); - - const Value *Op = SExt->getOperand(0); - MVT::SimpleValueType From = getSimpleType(Op->getType()); - MVT::SimpleValueType To = getLegalType(getSimpleType(SExt->getType())); - unsigned In = getRegForValue(Op); - if (In == 0) - return false; - unsigned Reg = signExtend(In, Op, From, To); - if (Reg == 0) - return false; - - updateValueMap(SExt, Reg); - return true; -} - -bool WebAssemblyFastISel::selectICmp(const Instruction *I) { - const auto *ICmp = cast<ICmpInst>(I); - - bool I32 = getSimpleType(ICmp->getOperand(0)->getType()) != MVT::i64; - unsigned Opc; - bool IsSigned = false; - switch (ICmp->getPredicate()) { - case ICmpInst::ICMP_EQ: - Opc = I32 ? WebAssembly::EQ_I32 : WebAssembly::EQ_I64; - break; - case ICmpInst::ICMP_NE: - Opc = I32 ? WebAssembly::NE_I32 : WebAssembly::NE_I64; - break; - case ICmpInst::ICMP_UGT: - Opc = I32 ? WebAssembly::GT_U_I32 : WebAssembly::GT_U_I64; - break; - case ICmpInst::ICMP_UGE: - Opc = I32 ? WebAssembly::GE_U_I32 : WebAssembly::GE_U_I64; - break; - case ICmpInst::ICMP_ULT: - Opc = I32 ? WebAssembly::LT_U_I32 : WebAssembly::LT_U_I64; - break; - case ICmpInst::ICMP_ULE: - Opc = I32 ? WebAssembly::LE_U_I32 : WebAssembly::LE_U_I64; - break; - case ICmpInst::ICMP_SGT: - Opc = I32 ? WebAssembly::GT_S_I32 : WebAssembly::GT_S_I64; - IsSigned = true; - break; - case ICmpInst::ICMP_SGE: - Opc = I32 ? WebAssembly::GE_S_I32 : WebAssembly::GE_S_I64; - IsSigned = true; - break; - case ICmpInst::ICMP_SLT: - Opc = I32 ? WebAssembly::LT_S_I32 : WebAssembly::LT_S_I64; - IsSigned = true; - break; - case ICmpInst::ICMP_SLE: - Opc = I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64; - IsSigned = true; - break; - default: - return false; - } - - unsigned LHS = getRegForPromotedValue(ICmp->getOperand(0), IsSigned); - if (LHS == 0) - return false; - - unsigned RHS = getRegForPromotedValue(ICmp->getOperand(1), IsSigned); - if (RHS == 0) - return false; - - unsigned ResultReg = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) - .addReg(LHS) - .addReg(RHS); - updateValueMap(ICmp, ResultReg); - return true; -} - -bool WebAssemblyFastISel::selectFCmp(const Instruction *I) { - const auto *FCmp = cast<FCmpInst>(I); - - unsigned LHS = getRegForValue(FCmp->getOperand(0)); - if (LHS == 0) - return false; - - unsigned RHS = getRegForValue(FCmp->getOperand(1)); - if (RHS == 0) - return false; - - bool F32 = getSimpleType(FCmp->getOperand(0)->getType()) != MVT::f64; - unsigned Opc; - bool Not = false; - switch (FCmp->getPredicate()) { - case FCmpInst::FCMP_OEQ: - Opc = F32 ? WebAssembly::EQ_F32 : WebAssembly::EQ_F64; - break; - case FCmpInst::FCMP_UNE: - Opc = F32 ? WebAssembly::NE_F32 : WebAssembly::NE_F64; - break; - case FCmpInst::FCMP_OGT: - Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; - break; - case FCmpInst::FCMP_OGE: - Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; - break; - case FCmpInst::FCMP_OLT: - Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; - break; - case FCmpInst::FCMP_OLE: - Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; - break; - case FCmpInst::FCMP_UGT: - Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; - Not = true; - break; - case FCmpInst::FCMP_UGE: - Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; - Not = true; - break; - case FCmpInst::FCMP_ULT: - Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; - Not = true; - break; - case FCmpInst::FCMP_ULE: - Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; - Not = true; - break; - default: - return false; - } - - unsigned ResultReg = createResultReg(&WebAssembly::I32RegClass); - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) - .addReg(LHS) - .addReg(RHS); - - if (Not) - ResultReg = notValue(ResultReg); - - updateValueMap(FCmp, ResultReg); - return true; -} - -bool WebAssemblyFastISel::selectBitCast(const Instruction *I) { - // Target-independent code can handle this, except it doesn't set the dead - // flag on the ARGUMENTS clobber, so we have to do that manually in order - // to satisfy code that expects this of isBitcast() instructions. - EVT VT = TLI.getValueType(DL, I->getOperand(0)->getType()); - EVT RetVT = TLI.getValueType(DL, I->getType()); - if (!VT.isSimple() || !RetVT.isSimple()) - return false; - - unsigned In = getRegForValue(I->getOperand(0)); - if (In == 0) - return false; - - if (VT == RetVT) { - // No-op bitcast. - updateValueMap(I, In); - return true; - } - - unsigned Reg = fastEmit_ISD_BITCAST_r(VT.getSimpleVT(), RetVT.getSimpleVT(), - In, I->getOperand(0)->hasOneUse()); - if (!Reg) - return false; - MachineBasicBlock::iterator Iter = FuncInfo.InsertPt; - --Iter; - assert(Iter->isBitcast()); - Iter->setPhysRegsDeadExcept(ArrayRef<unsigned>(), TRI); - updateValueMap(I, Reg); - return true; -} - -bool WebAssemblyFastISel::selectLoad(const Instruction *I) { - const auto *Load = cast<LoadInst>(I); - if (Load->isAtomic()) - return false; - if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy()) - return false; - - Address Addr; - if (!computeAddress(Load->getPointerOperand(), Addr)) - return false; - - // TODO: Fold a following sign-/zero-extend into the load instruction. - - unsigned Opc; - const TargetRegisterClass *RC; - switch (getSimpleType(Load->getType())) { - case MVT::i1: - case MVT::i8: - Opc = WebAssembly::LOAD8_U_I32; - RC = &WebAssembly::I32RegClass; - break; - case MVT::i16: - Opc = WebAssembly::LOAD16_U_I32; - RC = &WebAssembly::I32RegClass; - break; - case MVT::i32: - Opc = WebAssembly::LOAD_I32; - RC = &WebAssembly::I32RegClass; - break; - case MVT::i64: - Opc = WebAssembly::LOAD_I64; - RC = &WebAssembly::I64RegClass; - break; - case MVT::f32: - Opc = WebAssembly::LOAD_F32; - RC = &WebAssembly::F32RegClass; - break; - case MVT::f64: - Opc = WebAssembly::LOAD_F64; - RC = &WebAssembly::F64RegClass; - break; - default: - return false; - } - - materializeLoadStoreOperands(Addr); - - unsigned ResultReg = createResultReg(RC); - auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), - ResultReg); - - addLoadStoreOperands(Addr, MIB, createMachineMemOperandFor(Load)); - - updateValueMap(Load, ResultReg); - return true; -} - -bool WebAssemblyFastISel::selectStore(const Instruction *I) { - const auto *Store = cast<StoreInst>(I); - if (Store->isAtomic()) - return false; - if (!Subtarget->hasSIMD128() && - Store->getValueOperand()->getType()->isVectorTy()) - return false; - - Address Addr; - if (!computeAddress(Store->getPointerOperand(), Addr)) - return false; - - unsigned Opc; - bool VTIsi1 = false; - switch (getSimpleType(Store->getValueOperand()->getType())) { - case MVT::i1: - VTIsi1 = true; - LLVM_FALLTHROUGH; - case MVT::i8: - Opc = WebAssembly::STORE8_I32; - break; - case MVT::i16: - Opc = WebAssembly::STORE16_I32; - break; - case MVT::i32: - Opc = WebAssembly::STORE_I32; - break; - case MVT::i64: - Opc = WebAssembly::STORE_I64; - break; - case MVT::f32: - Opc = WebAssembly::STORE_F32; - break; - case MVT::f64: - Opc = WebAssembly::STORE_F64; - break; - default: - return false; - } - - materializeLoadStoreOperands(Addr); - - unsigned ValueReg = getRegForValue(Store->getValueOperand()); - if (ValueReg == 0) - return false; - if (VTIsi1) - ValueReg = maskI1Value(ValueReg, Store->getValueOperand()); - - auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)); - - addLoadStoreOperands(Addr, MIB, createMachineMemOperandFor(Store)); - - MIB.addReg(ValueReg); - return true; -} - -bool WebAssemblyFastISel::selectBr(const Instruction *I) { - const auto *Br = cast<BranchInst>(I); - if (Br->isUnconditional()) { - MachineBasicBlock *MSucc = FuncInfo.MBBMap[Br->getSuccessor(0)]; - fastEmitBranch(MSucc, Br->getDebugLoc()); - return true; - } - - MachineBasicBlock *TBB = FuncInfo.MBBMap[Br->getSuccessor(0)]; - MachineBasicBlock *FBB = FuncInfo.MBBMap[Br->getSuccessor(1)]; - - bool Not; - unsigned CondReg = getRegForI1Value(Br->getCondition(), Not); - if (CondReg == 0) - return false; - - unsigned Opc = WebAssembly::BR_IF; - if (Not) - Opc = WebAssembly::BR_UNLESS; - - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)) - .addMBB(TBB) - .addReg(CondReg); - - finishCondBranch(Br->getParent(), TBB, FBB); - return true; -} - -bool WebAssemblyFastISel::selectRet(const Instruction *I) { - if (!FuncInfo.CanLowerReturn) - return false; - - const auto *Ret = cast<ReturnInst>(I); - - if (Ret->getNumOperands() == 0) { - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::RETURN_VOID)); - return true; - } - - Value *RV = Ret->getOperand(0); - if (!Subtarget->hasSIMD128() && RV->getType()->isVectorTy()) - return false; - - unsigned Opc; - switch (getSimpleType(RV->getType())) { - case MVT::i1: - case MVT::i8: - case MVT::i16: - case MVT::i32: - Opc = WebAssembly::RETURN_I32; - break; - case MVT::i64: - Opc = WebAssembly::RETURN_I64; - break; - case MVT::f32: - Opc = WebAssembly::RETURN_F32; - break; - case MVT::f64: - Opc = WebAssembly::RETURN_F64; - break; - case MVT::v16i8: - Opc = WebAssembly::RETURN_v16i8; - break; - case MVT::v8i16: - Opc = WebAssembly::RETURN_v8i16; - break; - 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::exnref: - Opc = WebAssembly::RETURN_EXNREF; - break; - default: - return false; - } - - unsigned Reg; - if (FuncInfo.Fn->getAttributes().hasAttribute(0, Attribute::SExt)) - Reg = getRegForSignedValue(RV); - else if (FuncInfo.Fn->getAttributes().hasAttribute(0, Attribute::ZExt)) - Reg = getRegForUnsignedValue(RV); - else - Reg = getRegForValue(RV); - - if (Reg == 0) - return false; - - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)).addReg(Reg); - return true; -} - -bool WebAssemblyFastISel::selectUnreachable(const Instruction *I) { - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, - TII.get(WebAssembly::UNREACHABLE)); - return true; -} - -bool WebAssemblyFastISel::fastSelectInstruction(const Instruction *I) { - switch (I->getOpcode()) { - case Instruction::Call: - 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; - } - - // Fall back to target-independent instruction selection. - return selectOperator(I, I->getOpcode()); -} - -FastISel *WebAssembly::createFastISel(FunctionLoweringInfo &FuncInfo, - const TargetLibraryInfo *LibInfo) { - return new WebAssemblyFastISel(FuncInfo, LibInfo); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp deleted file mode 100644 index b7fc65401fc4..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFixFunctionBitcasts.cpp +++ /dev/null @@ -1,321 +0,0 @@ -//===-- WebAssemblyFixFunctionBitcasts.cpp - Fix function bitcasts --------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Fix bitcasted functions. -/// -/// WebAssembly requires caller and callee signatures to match, however in LLVM, -/// some amount of slop is vaguely permitted. Detect mismatch by looking for -/// bitcasts of functions and rewrite them to use wrapper functions instead. -/// -/// This doesn't catch all cases, such as when a function's address is taken in -/// one place and casted in another, but it works for many common cases. -/// -/// Note that LLVM already optimizes away function bitcasts in common cases by -/// dropping arguments as needed, so this pass only ends up getting used in less -/// common cases. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "llvm/IR/CallSite.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Operator.h" -#include "llvm/Pass.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-fix-function-bitcasts" - -namespace { -class FixFunctionBitcasts final : public ModulePass { - StringRef getPassName() const override { - return "WebAssembly Fix Function Bitcasts"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - ModulePass::getAnalysisUsage(AU); - } - - bool runOnModule(Module &M) override; - -public: - static char ID; - FixFunctionBitcasts() : ModulePass(ID) {} -}; -} // End anonymous namespace - -char FixFunctionBitcasts::ID = 0; -INITIALIZE_PASS(FixFunctionBitcasts, DEBUG_TYPE, - "Fix mismatching bitcasts for WebAssembly", false, false) - -ModulePass *llvm::createWebAssemblyFixFunctionBitcasts() { - return new FixFunctionBitcasts(); -} - -// Recursively descend the def-use lists from V to find non-bitcast users of -// bitcasts of V. -static void findUses(Value *V, Function &F, - SmallVectorImpl<std::pair<Use *, Function *>> &Uses, - SmallPtrSetImpl<Constant *> &ConstantBCs) { - for (Use &U : V->uses()) { - if (auto *BC = dyn_cast<BitCastOperator>(U.getUser())) - findUses(BC, F, Uses, ConstantBCs); - else if (U.get()->getType() != F.getType()) { - CallSite CS(U.getUser()); - if (!CS) - // Skip uses that aren't immediately called - continue; - Value *Callee = CS.getCalledValue(); - if (Callee != V) - // Skip calls where the function isn't the callee - continue; - if (isa<Constant>(U.get())) { - // Only add constant bitcasts to the list once; they get RAUW'd - auto C = ConstantBCs.insert(cast<Constant>(U.get())); - if (!C.second) - continue; - } - Uses.push_back(std::make_pair(&U, &F)); - } - } -} - -// Create a wrapper function with type Ty that calls F (which may have a -// different type). Attempt to support common bitcasted function idioms: -// - Call with more arguments than needed: arguments are dropped -// - Call with fewer arguments than needed: arguments are filled in with undef -// - Return value is not needed: drop it -// - Return value needed but not present: supply an undef -// -// 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, - 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; - Function::arg_iterator AI = Wrapper->arg_begin(); - 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) { - 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; - } - } - } - - if (WrapperNeeded && !TypeMismatch) { - 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); - - 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; -} - -// Test whether a main function with type FuncTy should be rewritten to have -// type MainTy. -static bool shouldFixMainFunction(FunctionType *FuncTy, FunctionType *MainTy) { - // Only fix the main function if it's the standard zero-arg form. That way, - // the standard cases will work as expected, and users will see signature - // mismatches from the linker for non-standard cases. - return FuncTy->getReturnType() == MainTy->getReturnType() && - FuncTy->getNumParams() == 0 && - !FuncTy->isVarArg(); -} - -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; - SmallPtrSet<Constant *, 2> ConstantBCs; - - // Collect all the places that need wrappers. - for (Function &F : M) { - findUses(&F, F, Uses, ConstantBCs); - - // If we have a "main" function, and its type isn't - // "int main(int argc, char *argv[])", create an artificial call with it - // bitcasted to that type so that we generate a wrapper for it, so that - // the C runtime can call it. - if (F.getName() == "main") { - Main = &F; - LLVMContext &C = M.getContext(); - Type *MainArgTys[] = {Type::getInt32Ty(C), - PointerType::get(Type::getInt8PtrTy(C), 0)}; - FunctionType *MainTy = FunctionType::get(Type::getInt32Ty(C), MainArgTys, - /*isVarArg=*/false); - if (shouldFixMainFunction(F.getFunctionType(), MainTy)) { - 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(MainTy, Casted, Args, "call_main"); - Use *UseMain = &CallMain->getOperandUse(2); - Uses.push_back(std::make_pair(UseMain, &F)); - } - } - } - - DenseMap<std::pair<Function *, FunctionType *>, Function *> Wrappers; - - for (auto &UseFunc : Uses) { - Use *U = UseFunc.first; - Function *F = UseFunc.second; - auto *PTy = cast<PointerType>(U->get()->getType()); - auto *Ty = dyn_cast<FunctionType>(PTy->getElementType()); - - // If the function is casted to something like i8* as a "generic pointer" - // to be later casted to something else, we can't generate a wrapper for it. - // Just ignore such casts for now. - if (!Ty) - continue; - - auto Pair = Wrappers.insert(std::make_pair(std::make_pair(F, Ty), nullptr)); - if (Pair.second) - Pair.first->second = createWrapper(F, Ty); - - Function *Wrapper = Pair.first->second; - if (!Wrapper) - continue; - - if (isa<Constant>(U->get())) - U->get()->replaceAllUsesWith(Wrapper); - else - U->set(Wrapper); - } - - // If we created a wrapper for main, rename the wrapper so that it's the - // one that gets called from startup. - if (CallMain) { - Main->setName("__original_main"); - auto *MainWrapper = - cast<Function>(CallMain->getCalledValue()->stripPointerCasts()); - delete CallMain; - if (Main->isDeclaration()) { - // The wrapper is not needed in this case as we don't need to export - // it to anyone else. - MainWrapper->eraseFromParent(); - } else { - // Otherwise give the wrapper the same linkage as the original main - // function, so that it can be called from the same places. - MainWrapper->setName("main"); - MainWrapper->setLinkage(Main->getLinkage()); - MainWrapper->setVisibility(Main->getVisibility()); - } - } - - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp deleted file mode 100644 index 7d8e86d9b2c0..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow.cpp +++ /dev/null @@ -1,501 +0,0 @@ -//=- WebAssemblyFixIrreducibleControlFlow.cpp - Fix irreducible control flow -// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements a pass that removes irreducible control flow. -/// Irreducible control flow means multiple-entry loops, which this pass -/// transforms to have a single entry. -/// -/// Note that LLVM has a generic pass that lowers irreducible control flow, but -/// it linearizes control flow, turning diamonds into two triangles, which is -/// both unnecessary and undesirable for WebAssembly. -/// -/// The big picture: We recursively process each "region", defined as a group -/// of blocks with a single entry and no branches back to that entry. A region -/// may be the entire function body, or the inner part of a loop, i.e., the -/// loop's body without branches back to the loop entry. In each region we fix -/// up multi-entry loops by adding a new block 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 the loop containing the previous multiple entries. After -/// ensuring all the loops in a region are reducible, we recurse into them. The -/// total time complexity of this pass is: -/// -/// O(NumBlocks * NumNestedLoops * NumIrreducibleLoops + -/// NumLoops * NumLoops) -/// -/// This pass 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 duplicate code in order to resolve -/// irreducibility. The graph algorithms for finding loops and entries and so -/// forth are also similar to the Relooper. The main differences between this -/// pass and the Relooper are: -/// -/// * We just care about irreducibility, so we just look at loops. -/// * The Relooper emits structured control flow (with ifs etc.), while we -/// emit a CFG. -/// -/// [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 -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-fix-irreducible-control-flow" - -namespace { - -using BlockVector = SmallVector<MachineBasicBlock *, 4>; -using BlockSet = SmallPtrSet<MachineBasicBlock *, 4>; - -// Calculates reachability in a region. Ignores branches to blocks outside of -// the region, and ignores branches to the region entry (for the case where -// the region is the inner part of a loop). -class ReachabilityGraph { -public: - ReachabilityGraph(MachineBasicBlock *Entry, const BlockSet &Blocks) - : Entry(Entry), Blocks(Blocks) { -#ifndef NDEBUG - // The region must have a single entry. - for (auto *MBB : Blocks) { - if (MBB != Entry) { - for (auto *Pred : MBB->predecessors()) { - assert(inRegion(Pred)); - } - } - } -#endif - calculate(); - } - - bool canReach(MachineBasicBlock *From, MachineBasicBlock *To) const { - assert(inRegion(From) && inRegion(To)); - auto I = Reachable.find(From); - if (I == Reachable.end()) - return false; - return I->second.count(To); - } - - // "Loopers" are blocks that are in a loop. We detect these by finding blocks - // that can reach themselves. - const BlockSet &getLoopers() const { return Loopers; } - - // Get all blocks that are loop entries. - const BlockSet &getLoopEntries() const { return LoopEntries; } - - // Get all blocks that enter a particular loop from outside. - const BlockSet &getLoopEnterers(MachineBasicBlock *LoopEntry) const { - assert(inRegion(LoopEntry)); - auto I = LoopEnterers.find(LoopEntry); - assert(I != LoopEnterers.end()); - return I->second; - } - -private: - MachineBasicBlock *Entry; - const BlockSet &Blocks; - - BlockSet Loopers, LoopEntries; - DenseMap<MachineBasicBlock *, BlockSet> LoopEnterers; - - bool inRegion(MachineBasicBlock *MBB) const { return Blocks.count(MBB); } - - // Maps a block to all the other blocks it can reach. - DenseMap<MachineBasicBlock *, BlockSet> Reachable; - - void calculate() { - // Reachability computation work list. 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; - - // Add all relevant direct branches. - for (auto *MBB : Blocks) { - for (auto *Succ : MBB->successors()) { - if (Succ != Entry && inRegion(Succ)) { - Reachable[MBB].insert(Succ); - WorkList.emplace_back(MBB, Succ); - } - } - } - - while (!WorkList.empty()) { - MachineBasicBlock *MBB, *Succ; - std::tie(MBB, Succ) = WorkList.pop_back_val(); - assert(inRegion(MBB) && Succ != Entry && inRegion(Succ)); - if (MBB != Entry) { - // We recently added MBB => Succ, and that means we may have enabled - // Pred => MBB => Succ. - for (auto *Pred : MBB->predecessors()) { - if (Reachable[Pred].insert(Succ).second) { - WorkList.emplace_back(Pred, Succ); - } - } - } - } - - // Blocks that can return to themselves are in a loop. - for (auto *MBB : Blocks) { - if (canReach(MBB, MBB)) { - Loopers.insert(MBB); - } - } - assert(!Loopers.count(Entry)); - - // Find the loop entries - loopers reachable from blocks not in that loop - - // and those outside blocks that reach them, the "loop enterers". - for (auto *Looper : Loopers) { - for (auto *Pred : Looper->predecessors()) { - // Pred can reach Looper. If Looper can reach Pred, it is in the loop; - // otherwise, it is a block that enters into the loop. - if (!canReach(Looper, Pred)) { - LoopEntries.insert(Looper); - LoopEnterers[Looper].insert(Pred); - } - } - } - } -}; - -// Finds the blocks in a single-entry loop, given the loop entry and the -// list of blocks that enter the loop. -class LoopBlocks { -public: - LoopBlocks(MachineBasicBlock *Entry, const BlockSet &Enterers) - : Entry(Entry), Enterers(Enterers) { - calculate(); - } - - BlockSet &getBlocks() { return Blocks; } - -private: - MachineBasicBlock *Entry; - const BlockSet &Enterers; - - BlockSet Blocks; - - void calculate() { - // Going backwards from the loop entry, if we ignore the blocks entering - // from outside, we will traverse all the blocks in the loop. - BlockVector WorkList; - BlockSet AddedToWorkList; - Blocks.insert(Entry); - for (auto *Pred : Entry->predecessors()) { - if (!Enterers.count(Pred)) { - WorkList.push_back(Pred); - AddedToWorkList.insert(Pred); - } - } - - while (!WorkList.empty()) { - auto *MBB = WorkList.pop_back_val(); - assert(!Enterers.count(MBB)); - if (Blocks.insert(MBB).second) { - for (auto *Pred : MBB->predecessors()) { - if (!AddedToWorkList.count(Pred)) { - WorkList.push_back(Pred); - AddedToWorkList.insert(Pred); - } - } - } - } - } -}; - -class WebAssemblyFixIrreducibleControlFlow final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Fix Irreducible Control Flow"; - } - - bool runOnMachineFunction(MachineFunction &MF) override; - - bool processRegion(MachineBasicBlock *Entry, BlockSet &Blocks, - MachineFunction &MF); - - void makeSingleEntryLoop(BlockSet &Entries, BlockSet &Blocks, - MachineFunction &MF, const ReachabilityGraph &Graph); - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyFixIrreducibleControlFlow() : MachineFunctionPass(ID) {} -}; - -bool WebAssemblyFixIrreducibleControlFlow::processRegion( - MachineBasicBlock *Entry, BlockSet &Blocks, MachineFunction &MF) { - bool Changed = false; - - // Remove irreducibility before processing child loops, which may take - // multiple iterations. - while (true) { - ReachabilityGraph Graph(Entry, Blocks); - - bool FoundIrreducibility = false; - - for (auto *LoopEntry : Graph.getLoopEntries()) { - // Find mutual entries - all entries which can reach this one, and - // are reached by it (that always includes LoopEntry itself). All mutual - // entries must be in the same loop, so if we have more than one, then we - // have irreducible control flow. - // - // Note that irreducibility may involve inner loops, e.g. imagine A - // starts one loop, and it has B inside it which starts an inner loop. - // If we add a branch from all the way on the outside to B, then in a - // sense B is no longer an "inner" loop, semantically speaking. We will - // fix that irreducibility by adding a block that dispatches to either - // either A or B, so B will no longer be an inner loop in our output. - // (A fancier approach might try to keep it as such.) - // - // Note that we still need to recurse into inner loops later, to handle - // the case where the irreducibility is entirely nested - we would not - // be able to identify that at this point, since the enclosing loop is - // a group of blocks all of whom can reach each other. (We'll see the - // irreducibility after removing branches to the top of that enclosing - // loop.) - BlockSet MutualLoopEntries; - MutualLoopEntries.insert(LoopEntry); - for (auto *OtherLoopEntry : Graph.getLoopEntries()) { - if (OtherLoopEntry != LoopEntry && - Graph.canReach(LoopEntry, OtherLoopEntry) && - Graph.canReach(OtherLoopEntry, LoopEntry)) { - MutualLoopEntries.insert(OtherLoopEntry); - } - } - - if (MutualLoopEntries.size() > 1) { - makeSingleEntryLoop(MutualLoopEntries, Blocks, MF, Graph); - FoundIrreducibility = true; - Changed = true; - break; - } - } - // Only go on to actually process the inner loops when we are done - // removing irreducible control flow and changing the graph. Modifying - // the graph as we go is possible, and that might let us avoid looking at - // the already-fixed loops again if we are careful, but all that is - // complex and bug-prone. Since irreducible loops are rare, just starting - // another iteration is best. - if (FoundIrreducibility) { - continue; - } - - for (auto *LoopEntry : Graph.getLoopEntries()) { - LoopBlocks InnerBlocks(LoopEntry, Graph.getLoopEnterers(LoopEntry)); - // Each of these calls to processRegion may change the graph, but are - // guaranteed not to interfere with each other. The only changes we make - // to the graph are to add blocks on the way to a loop entry. As the - // loops are disjoint, that means we may only alter branches that exit - // another loop, which are ignored when recursing into that other loop - // anyhow. - if (processRegion(LoopEntry, InnerBlocks.getBlocks(), MF)) { - Changed = true; - } - } - - return Changed; - } -} - -// Given a set of entries to a single loop, create a single entry for that -// loop by creating a dispatch block for them, routing control flow using -// a helper variable. Also updates Blocks with any new blocks created, so -// that we properly track all the blocks in the region. But this does not update -// ReachabilityGraph; this will be updated in the caller of this function as -// needed. -void WebAssemblyFixIrreducibleControlFlow::makeSingleEntryLoop( - BlockSet &Entries, BlockSet &Blocks, MachineFunction &MF, - const ReachabilityGraph &Graph) { - assert(Entries.size() >= 2); - - // Sort the entries to ensure a deterministic build. - BlockVector SortedEntries(Entries.begin(), Entries.end()); - llvm::sort(SortedEntries, - [&](const MachineBasicBlock *A, const MachineBasicBlock *B) { - auto ANum = A->getNumber(); - auto BNum = B->getNumber(); - return ANum < BNum; - }); - -#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); - Blocks.insert(Dispatch); - - // Add the jump table. - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - MachineInstrBuilder MIB = - BuildMI(Dispatch, DebugLoc(), TII.get(WebAssembly::BR_TABLE_I32)); - - // Add the register which will be used to tell the jump table which block to - // jump to. - MachineRegisterInfo &MRI = MF.getRegInfo(); - unsigned Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - MIB.addReg(Reg); - - // Compute the indices in the superheader, one for each bad block, and - // add them as successors. - DenseMap<MachineBasicBlock *, unsigned> Indices; - for (auto *Entry : SortedEntries) { - auto Pair = Indices.insert(std::make_pair(Entry, 0)); - assert(Pair.second); - - unsigned Index = MIB.getInstr()->getNumExplicitOperands() - 1; - Pair.first->second = Index; - - MIB.addMBB(Entry); - Dispatch->addSuccessor(Entry); - } - - // 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.) - - BlockVector AllPreds; - for (auto *Entry : SortedEntries) { - for (auto *Pred : Entry->predecessors()) { - if (Pred != Dispatch) { - AllPreds.push_back(Pred); - } - } - } - - // This set stores predecessors within this loop. - DenseSet<MachineBasicBlock *> InLoop; - for (auto *Pred : AllPreds) { - for (auto *Entry : Pred->successors()) { - if (!Entries.count(Entry)) - continue; - if (Graph.canReach(Entry, Pred)) { - InLoop.insert(Pred); - break; - } - } - } - - // Record if each entry has a layout predecessor. This map stores - // <<Predecessor is within the loop?, loop entry>, layout predecessor> - std::map<std::pair<bool, MachineBasicBlock *>, MachineBasicBlock *> - EntryToLayoutPred; - for (auto *Pred : AllPreds) - for (auto *Entry : Pred->successors()) - if (Entries.count(Entry) && Pred->isLayoutSuccessor(Entry)) - EntryToLayoutPred[std::make_pair(InLoop.count(Pred), Entry)] = Pred; - - // We need to create at most two routing blocks per entry: one for - // predecessors outside the loop and one for predecessors inside the loop. - // This map stores - // <<Predecessor is within the loop?, loop entry>, routing block> - std::map<std::pair<bool, MachineBasicBlock *>, MachineBasicBlock *> Map; - for (auto *Pred : AllPreds) { - bool PredInLoop = InLoop.count(Pred); - for (auto *Entry : Pred->successors()) { - if (!Entries.count(Entry) || - Map.count(std::make_pair(InLoop.count(Pred), Entry))) - continue; - // If there exists a layout predecessor of this entry and this predecessor - // is not that, we rather create a routing block after that layout - // predecessor to save a branch. - if (EntryToLayoutPred.count(std::make_pair(PredInLoop, Entry)) && - EntryToLayoutPred[std::make_pair(PredInLoop, Entry)] != Pred) - continue; - - // This is a successor we need to rewrite. - MachineBasicBlock *Routing = MF.CreateMachineBasicBlock(); - MF.insert(Pred->isLayoutSuccessor(Entry) - ? MachineFunction::iterator(Entry) - : MF.end(), - Routing); - Blocks.insert(Routing); - - // Set the jump table's register of the index of the block we wish to - // jump to, and jump to the jump table. - BuildMI(Routing, DebugLoc(), TII.get(WebAssembly::CONST_I32), Reg) - .addImm(Indices[Entry]); - BuildMI(Routing, DebugLoc(), TII.get(WebAssembly::BR)).addMBB(Dispatch); - Routing->addSuccessor(Dispatch); - Map[std::make_pair(PredInLoop, Entry)] = Routing; - } - } - - for (auto *Pred : AllPreds) { - bool PredInLoop = InLoop.count(Pred); - // Remap the terminator operands and the successor list. - for (MachineInstr &Term : Pred->terminators()) - for (auto &Op : Term.explicit_uses()) - if (Op.isMBB() && Indices.count(Op.getMBB())) - Op.setMBB(Map[std::make_pair(PredInLoop, Op.getMBB())]); - - for (auto *Succ : Pred->successors()) { - if (!Entries.count(Succ)) - continue; - auto *Routing = Map[std::make_pair(PredInLoop, Succ)]; - Pred->replaceSuccessor(Succ, Routing); - } - } - - // Create a fake default label, because br_table requires one. - MIB.addMBB(MIB.getInstr() - ->getOperand(MIB.getInstr()->getNumExplicitOperands() - 1) - .getMBB()); -} - -} // 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" - "********** Function: " - << MF.getName() << '\n'); - - // Start the recursive process on the entire function body. - BlockSet AllBlocks; - for (auto &MBB : MF) { - AllBlocks.insert(&MBB); - } - - if (LLVM_UNLIKELY(processRegion(&*MF.begin(), AllBlocks, MF))) { - // We rewrote part of the function; recompute relevant things. - MF.getRegInfo().invalidateLiveness(); - MF.RenumberBlocks(); - return true; - } - - return false; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp deleted file mode 100644 index 5299068efdd4..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ /dev/null @@ -1,261 +0,0 @@ -//===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the WebAssembly implementation of -/// TargetFrameLowering class. -/// -/// On WebAssembly, there aren't a lot of things to do here. There are no -/// callee-saved registers to save, and no spill slots. -/// -/// The stack grows downward. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyFrameLowering.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssemblyInstrInfo.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyTargetMachine.h" -#include "WebAssemblyUtilities.h" -#include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/MachineFunction.h" -#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; - -#define DEBUG_TYPE "wasm-frame-info" - -// TODO: wasm64 -// TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions - -/// We need a base pointer in the case of having items on the stack that -/// 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 { - const auto *RegInfo = - MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); - return RegInfo->needsStackRealignment(MF); -} - -/// Return true if the specified function should have a dedicated frame pointer -/// register. -bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const { - const MachineFrameInfo &MFI = MF.getFrameInfo(); - - // When we have var-sized objects, we move the stack pointer by an unknown - // amount, and need to emit a frame pointer to restore the stack to where we - // were on function entry. - // If we already need a base pointer, we use that to fix up the stack pointer. - // If there are no fixed-size objects, we would have no use of a frame - // pointer, and thus should not emit one. - bool HasFixedSizedObjects = MFI.getStackSize() > 0; - bool NeedsFixedReference = !hasBP(MF) || HasFixedSizedObjects; - - return MFI.isFrameAddressTaken() || - (MFI.hasVarSizedObjects() && NeedsFixedReference) || - MFI.hasStackMap() || MFI.hasPatchPoint(); -} - -/// Under normal circumstances, when a frame pointer is not required, we reserve -/// argument space for call sites in the function immediately on entry to the -/// current function. This eliminates the need for add/sub sp brackets around -/// call sites. Returns true if the call frame is included as part of the stack -/// frame. -bool WebAssemblyFrameLowering::hasReservedCallFrame( - const MachineFunction &MF) const { - 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 { - return needsSPForLocalFrame(MF) || needsPrologForEH(MF); -} - -/// Returns true if the local user-space stack pointer needs to be written back -/// 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 { - 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; -} - -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::GLOBAL_SET_I32)) - .addExternalSymbol(SPSymbol) - .addReg(SrcReg); -} - -MachineBasicBlock::iterator -WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( - MachineFunction &MF, MachineBasicBlock &MBB, - MachineBasicBlock::iterator I) const { - assert(!I->getOperand(0).getImm() && (hasFP(MF) || hasBP(MF)) && - "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)) { - DebugLoc DL = I->getDebugLoc(); - writeSPToGlobal(WebAssembly::SP32, MF, MBB, I, DL); - } - return MBB.erase(I); -} - -void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, - MachineBasicBlock &MBB) const { - // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions - auto &MFI = MF.getFrameInfo(); - assert(MFI.getCalleeSavedInfo().empty() && - "WebAssembly should not have callee-saved registers"); - - if (!needsSP(MF)) - return; - uint64_t StackSize = MFI.getStackSize(); - - const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - auto &MRI = MF.getRegInfo(); - - auto InsertPt = MBB.begin(); - while (InsertPt != MBB.end() && - WebAssembly::isArgument(InsertPt->getOpcode())) - ++InsertPt; - DebugLoc DL; - - const TargetRegisterClass *PtrRC = - MRI.getTargetRegisterInfo()->getPointerRegClass(MF); - unsigned SPReg = WebAssembly::SP32; - if (StackSize) - SPReg = MRI.createVirtualRegister(PtrRC); - - const char *ES = "__stack_pointer"; - auto *SPSymbol = MF.createExternalSymbolName(ES); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::GLOBAL_GET_I32), SPReg) - .addExternalSymbol(SPSymbol); - - bool HasBP = hasBP(MF); - if (HasBP) { - auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); - unsigned BasePtr = MRI.createVirtualRegister(PtrRC); - FI->setBasePointerVreg(BasePtr); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), BasePtr) - .addReg(SPReg); - } - if (StackSize) { - // Subtract the frame size - unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) - .addImm(StackSize); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32), - WebAssembly::SP32) - .addReg(SPReg) - .addReg(OffsetReg); - } - if (HasBP) { - unsigned BitmaskReg = MRI.createVirtualRegister(PtrRC); - unsigned Alignment = MFI.getMaxAlignment(); - assert((1u << countTrailingZeros(Alignment)) == Alignment && - "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), - WebAssembly::SP32) - .addReg(WebAssembly::SP32) - .addReg(BitmaskReg); - } - if (hasFP(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) - .addReg(WebAssembly::SP32); - } - if (StackSize && needsSPWriteback(MF)) { - writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPt, DL); - } -} - -void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, - MachineBasicBlock &MBB) const { - 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(); - DebugLoc DL; - - if (InsertPt != MBB.end()) - DL = InsertPt->getDebugLoc(); - - // Restore the stack pointer. If we had fixed-size locals, add the offset - // subtracted in the prolog. - unsigned SPReg = 0; - if (hasBP(MF)) { - auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); - SPReg = FI->getBasePointerVreg(); - } else if (StackSize) { - const TargetRegisterClass *PtrRC = - MRI.getTargetRegisterInfo()->getPointerRegClass(MF); - unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); - 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); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), SPReg) - .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32) - .addReg(OffsetReg); - } else { - SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32; - } - - writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h deleted file mode 100644 index daddd4ca16ff..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h +++ /dev/null @@ -1,65 +0,0 @@ -// WebAssemblyFrameLowering.h - TargetFrameLowering for WebAssembly -*- C++ -*-/ -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This class implements WebAssembly-specific bits of -/// TargetFrameLowering class. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYFRAMELOWERING_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYFRAMELOWERING_H - -#include "llvm/CodeGen/TargetFrameLowering.h" - -namespace llvm { -class MachineFrameInfo; - -class WebAssemblyFrameLowering final : public TargetFrameLowering { -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 __stack_pointer - /// global). - // TODO: (ABI) Revisit and decide how large it should be. - static const size_t RedZoneSize = 128; - - WebAssemblyFrameLowering() - : TargetFrameLowering(StackGrowsDown, /*StackAlignment=*/16, - /*LocalAreaOffset=*/0, - /*TransientStackAlignment=*/16, - /*StackRealignable=*/true) {} - - 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; - void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; - - bool hasFP(const MachineFunction &MF) const override; - bool hasReservedCallFrame(const MachineFunction &MF) const override; - - 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 needsSPForLocalFrame(const MachineFunction &MF) const; - bool needsSP(const MachineFunction &MF) const; - bool needsSPWriteback(const MachineFunction &MF) const; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISD.def deleted file mode 100644 index 77217f16a727..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISD.def +++ /dev/null @@ -1,36 +0,0 @@ -//- WebAssemblyISD.def - WebAssembly ISD ---------------------------*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file describes the various WebAssembly ISD node types. -/// -//===----------------------------------------------------------------------===// - -// NOTE: NO INCLUDE GUARD DESIRED! - -HANDLE_NODETYPE(CALL1) -HANDLE_NODETYPE(CALL0) -HANDLE_NODETYPE(RET_CALL) -HANDLE_NODETYPE(RETURN) -HANDLE_NODETYPE(ARGUMENT) -// A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol -HANDLE_NODETYPE(Wrapper) -// A special wapper used in PIC code for __memory_base/__table_base relcative -// access. -HANDLE_NODETYPE(WrapperPIC) -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) -HANDLE_NODETYPE(MEMORY_COPY) -HANDLE_NODETYPE(MEMORY_FILL) - -// add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here... diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp deleted file mode 100644 index 26339eaef37d..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ /dev/null @@ -1,260 +0,0 @@ -//- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines an instruction selector for the WebAssembly target. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyTargetMachine.h" -#include "llvm/CodeGen/SelectionDAGISel.h" -#include "llvm/IR/DiagnosticInfo.h" -#include "llvm/IR/Function.h" // To access function attributes. -#include "llvm/Support/Debug.h" -#include "llvm/Support/KnownBits.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-isel" - -//===--------------------------------------------------------------------===// -/// WebAssembly-specific code to select WebAssembly machine instructions for -/// SelectionDAG operations. -/// -namespace { -class WebAssemblyDAGToDAGISel final : public SelectionDAGISel { - /// Keep a pointer to the WebAssemblySubtarget around so that we can make the - /// right decision when generating code for different targets. - const WebAssemblySubtarget *Subtarget; - - bool ForCodeSize; - -public: - WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM, - CodeGenOpt::Level OptLevel) - : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr), ForCodeSize(false) { - } - - StringRef getPassName() const override { - return "WebAssembly Instruction Selection"; - } - - bool runOnMachineFunction(MachineFunction &MF) override { - LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n" - "********** Function: " - << MF.getName() << '\n'); - - ForCodeSize = MF.getFunction().hasOptSize(); - Subtarget = &MF.getSubtarget<WebAssemblySubtarget>(); - return SelectionDAGISel::runOnMachineFunction(MF); - } - - void Select(SDNode *Node) override; - - bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, - std::vector<SDValue> &OutOps) override; - -// Include the pieces autogenerated from the target description. -#include "WebAssemblyGenDAGISel.inc" - -private: - // add select functions here... -}; -} // end anonymous namespace - -void WebAssemblyDAGToDAGISel::Select(SDNode *Node) { - // If we have a custom node, we already have selected! - if (Node->isMachineOpcode()) { - LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); - Node->setNodeId(-1); - return; - } - - // Few custom selection stuff. - SDLoc DL(Node); - MachineFunction &MF = CurDAG->getMachineFunction(); - switch (Node->getOpcode()) { - case ISD::ATOMIC_FENCE: { - if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics()) - break; - - uint64_t SyncScopeID = - cast<ConstantSDNode>(Node->getOperand(2).getNode())->getZExtValue(); - switch (SyncScopeID) { - case SyncScope::SingleThread: { - // We lower a single-thread fence to a pseudo compiler barrier instruction - // preventing instruction reordering. This will not be emitted in final - // binary. - MachineSDNode *Fence = - CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE, - DL, // debug loc - MVT::Other, // outchain type - Node->getOperand(0) // inchain - ); - ReplaceNode(Node, Fence); - CurDAG->RemoveDeadNode(Node); - return; - } - - case SyncScope::System: { - // For non-emscripten systems, we have not decided on what we should - // traslate fences to yet. - if (!Subtarget->getTargetTriple().isOSEmscripten()) - report_fatal_error( - "ATOMIC_FENCE is not yet supported in non-emscripten OSes"); - - // Wasm does not have a fence instruction, but because all atomic - // instructions in wasm are sequentially consistent, we translate a - // fence to an idempotent atomic RMW instruction to a linear memory - // address. All atomic instructions in wasm are sequentially consistent, - // but this is to ensure a fence also prevents reordering of non-atomic - // instructions in the VM. Even though LLVM IR's fence instruction does - // not say anything about its relationship with non-atomic instructions, - // we think this is more user-friendly. - // - // While any address can work, here we use a value stored in - // __stack_pointer wasm global because there's high chance that area is - // in cache. - // - // So the selected instructions will be in the form of: - // %addr = get_global $__stack_pointer - // %0 = i32.const 0 - // i32.atomic.rmw.or %addr, %0 - SDValue StackPtrSym = CurDAG->getTargetExternalSymbol( - "__stack_pointer", TLI->getPointerTy(CurDAG->getDataLayout())); - MachineSDNode *GetGlobal = - CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, // opcode - DL, // debug loc - MVT::i32, // result type - StackPtrSym // __stack_pointer symbol - ); - - SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32); - auto *MMO = MF.getMachineMemOperand( - MachinePointerInfo::getUnknownStack(MF), - // FIXME Volatile isn't really correct, but currently all LLVM - // atomic instructions are treated as volatiles in the backend, so - // we should be consistent. - MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad | - MachineMemOperand::MOStore, - 4, 4, AAMDNodes(), nullptr, SyncScope::System, - AtomicOrdering::SequentiallyConsistent); - MachineSDNode *Const0 = - CurDAG->getMachineNode(WebAssembly::CONST_I32, DL, MVT::i32, Zero); - MachineSDNode *AtomicRMW = CurDAG->getMachineNode( - WebAssembly::ATOMIC_RMW_OR_I32, // opcode - DL, // debug loc - MVT::i32, // result type - MVT::Other, // outchain type - { - Zero, // alignment - Zero, // offset - SDValue(GetGlobal, 0), // __stack_pointer - SDValue(Const0, 0), // OR with 0 to make it idempotent - Node->getOperand(0) // inchain - }); - - CurDAG->setNodeMemRefs(AtomicRMW, {MMO}); - ReplaceUses(SDValue(Node, 0), SDValue(AtomicRMW, 1)); - CurDAG->RemoveDeadNode(Node); - return; - } - default: - llvm_unreachable("Unknown scope!"); - } - } - - case ISD::GlobalTLSAddress: { - const auto *GA = cast<GlobalAddressSDNode>(Node); - - if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) - report_fatal_error("cannot use thread-local storage without bulk memory", - false); - - // Currently Emscripten does not support dynamic linking with threads. - // Therefore, if we have thread-local storage, only the local-exec model - // is possible. - // TODO: remove this and implement proper TLS models once Emscripten - // supports dynamic linking with threads. - if (GA->getGlobal()->getThreadLocalMode() != - GlobalValue::LocalExecTLSModel && - !Subtarget->getTargetTriple().isOSEmscripten()) { - report_fatal_error("only -ftls-model=local-exec is supported for now on " - "non-Emscripten OSes: variable " + - GA->getGlobal()->getName(), - false); - } - - MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); - assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); - - SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); - SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( - GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); - - MachineSDNode *TLSBase = CurDAG->getMachineNode(WebAssembly::GLOBAL_GET_I32, - DL, MVT::i32, TLSBaseSym); - MachineSDNode *TLSOffset = CurDAG->getMachineNode( - WebAssembly::CONST_I32, DL, MVT::i32, TLSOffsetSym); - MachineSDNode *TLSAddress = - CurDAG->getMachineNode(WebAssembly::ADD_I32, DL, MVT::i32, - SDValue(TLSBase, 0), SDValue(TLSOffset, 0)); - ReplaceNode(Node, TLSAddress); - return; - } - - case ISD::INTRINSIC_WO_CHAIN: { - unsigned IntNo = cast<ConstantSDNode>(Node->getOperand(0))->getZExtValue(); - switch (IntNo) { - case Intrinsic::wasm_tls_size: { - MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout()); - assert(PtrVT == MVT::i32 && "only wasm32 is supported for now"); - - MachineSDNode *TLSSize = CurDAG->getMachineNode( - WebAssembly::GLOBAL_GET_I32, DL, PtrVT, - CurDAG->getTargetExternalSymbol("__tls_size", MVT::i32)); - ReplaceNode(Node, TLSSize); - return; - } - } - break; - } - - default: - break; - } - - // Select the default instruction. - SelectCode(Node); -} - -bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( - const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { - switch (ConstraintID) { - case InlineAsm::Constraint_i: - case InlineAsm::Constraint_m: - // We just support simple memory operands that just have a single address - // operand and need no special handling. - OutOps.push_back(Op); - return false; - default: - break; - } - - return true; -} - -/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready -/// for instruction scheduling. -FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, - CodeGenOpt::Level OptLevel) { - return new WebAssemblyDAGToDAGISel(TM, OptLevel); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp deleted file mode 100644 index 4064a983099c..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ /dev/null @@ -1,1457 +0,0 @@ -//=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements the WebAssemblyTargetLowering class. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyISelLowering.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyTargetMachine.h" -#include "llvm/CodeGen/Analysis.h" -#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" -#include "llvm/IR/Intrinsics.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Target/TargetOptions.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-lower" - -WebAssemblyTargetLowering::WebAssemblyTargetLowering( - const TargetMachine &TM, const WebAssemblySubtarget &STI) - : TargetLowering(TM), Subtarget(&STI) { - auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32; - - // Booleans always contain 0 or 1. - setBooleanContents(ZeroOrOneBooleanContent); - // Except in SIMD vectors - setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); - // We don't know the microarchitecture here, so just reduce register pressure. - setSchedulingPreference(Sched::RegPressure); - // Tell ISel that we have a stack pointer. - setStackPointerRegisterToSaveRestore( - Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32); - // Set up the register classes. - addRegisterClass(MVT::i32, &WebAssembly::I32RegClass); - addRegisterClass(MVT::i64, &WebAssembly::I64RegClass); - addRegisterClass(MVT::f32, &WebAssembly::F32RegClass); - addRegisterClass(MVT::f64, &WebAssembly::F64RegClass); - if (Subtarget->hasSIMD128()) { - addRegisterClass(MVT::v16i8, &WebAssembly::V128RegClass); - 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()); - - setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); - setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); - setOperationAction(ISD::JumpTable, MVTPtr, Custom); - setOperationAction(ISD::BlockAddress, MVTPtr, Custom); - setOperationAction(ISD::BRIND, MVT::Other, Custom); - - // Take the default expansion for va_arg, va_copy, and va_end. There is no - // default action for va_start, so we do that custom. - setOperationAction(ISD::VASTART, MVT::Other, Custom); - setOperationAction(ISD::VAARG, MVT::Other, Expand); - setOperationAction(ISD::VACOPY, MVT::Other, Expand); - setOperationAction(ISD::VAEND, MVT::Other, Expand); - - 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. - for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE, - 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}) - 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 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); - setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand); - setTruncStoreAction(T, MVT::f16, Expand); - } - - // 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); - } - - // SIMD-specific configuration - if (Subtarget->hasSIMD128()) { - // Support saturating add for i8x16 and i16x8 - for (auto Op : {ISD::SADDSAT, ISD::UADDSAT}) - for (auto T : {MVT::v16i8, MVT::v8i16}) - setOperationAction(Op, T, Legal); - - // Custom lower BUILD_VECTORs to minimize number of replace_lanes - for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) - setOperationAction(ISD::BUILD_VECTOR, T, Custom); - if (Subtarget->hasUnimplementedSIMD128()) - for (auto T : {MVT::v2i64, MVT::v2f64}) - setOperationAction(ISD::BUILD_VECTOR, T, Custom); - - // We have custom shuffle lowering to expose the shuffle mask - for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) - setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); - if (Subtarget->hasUnimplementedSIMD128()) - for (auto T: {MVT::v2i64, MVT::v2f64}) - setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); - - // Custom lowering since wasm shifts must have a scalar shift amount - for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL}) { - for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) - setOperationAction(Op, T, Custom); - if (Subtarget->hasUnimplementedSIMD128()) - setOperationAction(Op, MVT::v2i64, Custom); - } - - // Custom lower lane accesses to expand out variable indices - for (auto Op : {ISD::EXTRACT_VECTOR_ELT, ISD::INSERT_VECTOR_ELT}) { - for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) - setOperationAction(Op, T, Custom); - if (Subtarget->hasUnimplementedSIMD128()) - for (auto T : {MVT::v2i64, MVT::v2f64}) - setOperationAction(Op, T, Custom); - } - - // There is no i64x2.mul instruction - setOperationAction(ISD::MUL, MVT::v2i64, Expand); - - // There are no vector select instructions - 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); - } - - // Expand integer operations supported for scalars but not SIMD - for (auto Op : {ISD::CTLZ, ISD::CTTZ, ISD::CTPOP, ISD::SDIV, ISD::UDIV, - ISD::SREM, ISD::UREM, ISD::ROTL, ISD::ROTR}) { - for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) - setOperationAction(Op, T, Expand); - if (Subtarget->hasUnimplementedSIMD128()) - setOperationAction(Op, MVT::v2i64, Expand); - } - - // Expand float operations supported for scalars but not SIMD - for (auto Op : {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, - ISD::FCOPYSIGN, ISD::FLOG, ISD::FLOG2, ISD::FLOG10, - ISD::FEXP, ISD::FEXP2, ISD::FRINT}) { - setOperationAction(Op, MVT::v4f32, Expand); - if (Subtarget->hasUnimplementedSIMD128()) - setOperationAction(Op, MVT::v2f64, Expand); - } - - // Expand additional SIMD ops that V8 hasn't implemented yet - if (!Subtarget->hasUnimplementedSIMD128()) { - setOperationAction(ISD::FSQRT, MVT::v4f32, Expand); - setOperationAction(ISD::FDIV, MVT::v4f32, 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, 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); - setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); - setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand); - - setOperationAction(ISD::FrameIndex, MVT::i32, Custom); - setOperationAction(ISD::CopyToReg, MVT::Other, Custom); - - // Expand these forms; we pattern-match the forms that we can handle in isel. - for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) - for (auto Op : {ISD::BR_CC, ISD::SELECT_CC}) - setOperationAction(Op, T, Expand); - - // We have custom switch handling. - setOperationAction(ISD::BR_JT, MVT::Other, Custom); - - // WebAssembly doesn't have: - // - 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); - } - } - } - } - - // Don't do anything clever with build_pairs - setOperationAction(ISD::BUILD_PAIR, MVT::i64, Expand); - - // 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); - - if (Subtarget->hasBulkMemory()) { - // Use memory.copy and friends over multiple loads and stores - MaxStoresPerMemcpy = 1; - MaxStoresPerMemcpyOptSize = 1; - MaxStoresPerMemmove = 1; - MaxStoresPerMemmoveOptSize = 1; - MaxStoresPerMemset = 1; - MaxStoresPerMemsetOptSize = 1; - } - - // Override the __gnu_f2h_ieee/__gnu_h2f_ieee names so that the f32 name is - // consistent with the f64 and f128 names. - setLibcallName(RTLIB::FPEXT_F16_F32, "__extendhfsf2"); - setLibcallName(RTLIB::FPROUND_F32_F16, "__truncsfhf2"); - - // Define the emscripten name for return address helper. - // TODO: when implementing other WASM backends, make this generic or only do - // this on emscripten depending on what they end up doing. - setLibcallName(RTLIB::RETURN_ADDRESS, "emscripten_return_address"); - - // Always convert switches to br_tables unless there is only one case, which - // is equivalent to a simple branch. This reduces code size for wasm, and we - // defer possible jump table optimizations to the VM. - setMinimumJumpTableEntries(2); -} - -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); -} - -MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, - EVT VT) const { - unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1); - if (BitWidth > 1 && BitWidth < 8) - BitWidth = 8; - - if (BitWidth > 64) { - // The shift will be lowered to a libcall, and compiler-rt libcalls expect - // the count to be an i32. - BitWidth = 32; - assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) && - "32-bit shift counts ought to be enough for anyone"); - } - - MVT Result = MVT::getIntegerVT(BitWidth); - assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE && - "Unable to represent scalar shift amount type"); - return Result; -} - -// 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) { - MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); - - unsigned OutReg = MI.getOperand(0).getReg(); - unsigned InReg = MI.getOperand(1).getReg(); - - unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32; - unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32; - unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32; - unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32; - unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; - unsigned Eqz = WebAssembly::EQZ_I32; - unsigned And = WebAssembly::AND_I32; - int64_t Limit = Int64 ? INT64_MIN : INT32_MIN; - int64_t Substitute = IsUnsigned ? 0 : Limit; - double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit; - auto &Context = BB->getParent()->getFunction().getContext(); - Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context); - - const BasicBlock *LLVMBB = BB->getBasicBlock(); - MachineFunction *F = BB->getParent(); - MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB); - MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVMBB); - MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB); - - MachineFunction::iterator It = ++BB->getIterator(); - F->insert(It, FalseMBB); - F->insert(It, TrueMBB); - F->insert(It, DoneMBB); - - // Transfer the remainder of BB and its successor edges to DoneMBB. - DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end()); - DoneMBB->transferSuccessorsAndUpdatePHIs(BB); - - BB->addSuccessor(TrueMBB); - BB->addSuccessor(FalseMBB); - TrueMBB->addSuccessor(DoneMBB); - FalseMBB->addSuccessor(DoneMBB); - - unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg; - Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); - Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); - CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); - TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); - - MI.eraseFromParent(); - // For signed numbers, we can do a single comparison to determine whether - // fabs(x) is within range. - if (IsUnsigned) { - Tmp0 = InReg; - } else { - 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); - - // 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 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); - CmpReg = AndReg; - } - - 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(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg) - .addReg(FalseReg) - .addMBB(FalseMBB) - .addReg(TrueReg) - .addMBB(TrueMBB); - - return DoneMBB; -} - -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"); - case WebAssembly::FP_TO_SINT_I32_F32: - return LowerFPToInt(MI, DL, BB, TII, false, false, false, - WebAssembly::I32_TRUNC_S_F32); - case WebAssembly::FP_TO_UINT_I32_F32: - return LowerFPToInt(MI, DL, BB, TII, true, false, false, - WebAssembly::I32_TRUNC_U_F32); - case WebAssembly::FP_TO_SINT_I64_F32: - return LowerFPToInt(MI, DL, BB, TII, false, true, false, - WebAssembly::I64_TRUNC_S_F32); - case WebAssembly::FP_TO_UINT_I64_F32: - return LowerFPToInt(MI, DL, BB, TII, true, true, false, - WebAssembly::I64_TRUNC_U_F32); - case WebAssembly::FP_TO_SINT_I32_F64: - return LowerFPToInt(MI, DL, BB, TII, false, false, true, - WebAssembly::I32_TRUNC_S_F64); - case WebAssembly::FP_TO_UINT_I32_F64: - return LowerFPToInt(MI, DL, BB, TII, true, false, true, - WebAssembly::I32_TRUNC_U_F64); - case WebAssembly::FP_TO_SINT_I64_F64: - return LowerFPToInt(MI, DL, BB, TII, false, true, true, - WebAssembly::I64_TRUNC_S_F64); - 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"); - } -} - -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: \ - return "WebAssemblyISD::" #NODE; -#include "WebAssemblyISD.def" -#undef HANDLE_NODETYPE - } - return nullptr; -} - -std::pair<unsigned, const TargetRegisterClass *> -WebAssemblyTargetLowering::getRegForInlineAsmConstraint( - const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { - // First, see if this is a constraint that directly corresponds to a - // 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; - } - } - - return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); -} - -bool WebAssemblyTargetLowering::isCheapToSpeculateCttz() const { - // Assume ctz is a relatively cheap operation. - return true; -} - -bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const { - // Assume clz is a relatively cheap operation. - return true; -} - -bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL, - const AddrMode &AM, - 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; - - // WebAssembly has no scale register operands. - if (AM.Scale != 0) - return false; - - // Everything else is legal. - return true; -} - -bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses( - EVT /*VT*/, unsigned /*AddrSpace*/, unsigned /*Align*/, - MachineMemOperand::Flags /*Flags*/, bool *Fast) const { - // WebAssembly supports unaligned accesses, though it should be declared - // with the p2align attribute on loads and stores which do so, and there - // may be a performance impact. We tell LLVM they're "fast" because - // 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; - return true; -} - -bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT, - AttributeList Attr) const { - // The current thinking is that wasm engines will perform this optimization, - // so we can save on code size. - return true; -} - -EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL, - LLVMContext &C, - EVT VT) const { - if (VT.isVector()) - return VT.changeVectorElementTypeToInteger(); - - 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. -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// Lowering Code -//===----------------------------------------------------------------------===// - -static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg) { - MachineFunction &MF = DAG.getMachineFunction(); - DAG.getContext()->diagnose( - DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc())); -} - -// Test whether the given calling convention is supported. -static bool callingConvSupported(CallingConv::ID CallConv) { - // We currently support the language-independent target-independent - // conventions. We don't yet have a way to annotate calls with properties like - // "cold", and we don't have any call-clobbered registers, so these are mostly - // all handled the same. - return CallConv == CallingConv::C || CallConv == CallingConv::Fast || - CallConv == CallingConv::Cold || - CallConv == CallingConv::PreserveMost || - CallConv == CallingConv::PreserveAll || - CallConv == CallingConv::CXX_FAST_TLS; -} - -SDValue -WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, - SmallVectorImpl<SDValue> &InVals) const { - SelectionDAG &DAG = CLI.DAG; - SDLoc DL = CLI.DL; - SDValue Chain = CLI.Chain; - SDValue Callee = CLI.Callee; - MachineFunction &MF = DAG.getMachineFunction(); - auto Layout = MF.getDataLayout(); - - CallingConv::ID CallConv = CLI.CallConv; - if (!callingConvSupported(CallConv)) - fail(DL, DAG, - "WebAssembly doesn't support language-specific or target-specific " - "calling conventions yet"); - if (CLI.IsPatchPoint) - fail(DL, DAG, "WebAssembly doesn't support patch point yet"); - - // Fail if tail calls are required but not enabled - if (!Subtarget->hasTailCall()) { - if ((CallConv == CallingConv::Fast && CLI.IsTailCall && - MF.getTarget().Options.GuaranteedTailCallOpt) || - (CLI.CS && CLI.CS.isMustTailCall())) - fail(DL, DAG, "WebAssembly 'tail-call' feature not enabled"); - CLI.IsTailCall = false; - } - - SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; - if (Ins.size() > 1) - fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); - - SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; - SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; - unsigned NumFixedArgs = 0; - for (unsigned I = 0; I < Outs.size(); ++I) { - const ISD::OutputArg &Out = Outs[I]; - SDValue &OutVal = OutVals[I]; - if (Out.Flags.isNest()) - fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); - if (Out.Flags.isInAlloca()) - fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); - if (Out.Flags.isInConsecutiveRegs()) - fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); - if (Out.Flags.isInConsecutiveRegsLast()) - fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); - if (Out.Flags.isByVal() && Out.Flags.getByValSize() != 0) { - auto &MFI = MF.getFrameInfo(); - int FI = MFI.CreateStackObject(Out.Flags.getByValSize(), - Out.Flags.getByValAlign(), - /*isSS=*/false); - SDValue SizeNode = - DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32); - SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); - Chain = DAG.getMemcpy( - Chain, DL, FINode, OutVal, SizeNode, Out.Flags.getByValAlign(), - /*isVolatile*/ false, /*AlwaysInline=*/false, - /*isTailCall*/ false, MachinePointerInfo(), MachinePointerInfo()); - OutVal = FINode; - } - // Count the number of fixed args *after* legalization. - NumFixedArgs += Out.IsFixed; - } - - bool IsVarArg = CLI.IsVarArg; - auto PtrVT = getPointerTy(Layout); - - // Analyze operands of the call, assigning locations to each operand. - SmallVector<CCValAssign, 16> ArgLocs; - CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); - - if (IsVarArg) { - // Outgoing non-fixed arguments are placed in a buffer. First - // compute their offsets and the total amount of buffer space needed. - for (unsigned I = NumFixedArgs; I < Outs.size(); ++I) { - const ISD::OutputArg &Out = Outs[I]; - SDValue &Arg = OutVals[I]; - EVT VT = Arg.getValueType(); - assert(VT != MVT::iPTR && "Legalized args should be concrete"); - Type *Ty = VT.getTypeForEVT(*DAG.getContext()); - unsigned Align = std::max(Out.Flags.getOrigAlign(), - Layout.getABITypeAlignment(Ty)); - unsigned Offset = CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), - Align); - CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(), - Offset, VT.getSimpleVT(), - CCValAssign::Full)); - } - } - - unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); - - SDValue FINode; - if (IsVarArg && NumBytes) { - // For non-fixed arguments, next emit stores to store the argument values - // to the stack buffer at the offsets computed above. - int FI = MF.getFrameInfo().CreateStackObject(NumBytes, - Layout.getStackAlignment(), - /*isSS=*/false); - unsigned ValNo = 0; - SmallVector<SDValue, 8> Chains; - for (SDValue Arg : - make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { - assert(ArgLocs[ValNo].getValNo() == ValNo && - "ArgLocs should remain in order and only hold varargs args"); - unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); - 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)); - } - if (!Chains.empty()) - Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); - } else if (IsVarArg) { - FINode = DAG.getIntPtrConstant(0, DL); - } - - if (Callee->getOpcode() == ISD::GlobalAddress) { - // If the callee is a GlobalAddress node (quite common, every direct call - // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress - // doesn't at MO_GOT which is not needed for direct calls. - GlobalAddressSDNode* GA = cast<GlobalAddressSDNode>(Callee); - Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, - getPointerTy(DAG.getDataLayout()), - GA->getOffset()); - Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL, - getPointerTy(DAG.getDataLayout()), Callee); - } - - // Compute the operands for the CALLn node. - SmallVector<SDValue, 16> Ops; - Ops.push_back(Chain); - Ops.push_back(Callee); - - // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs - // isn't reliable. - Ops.append(OutVals.begin(), - IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); - // Add a pointer to the vararg buffer. - if (IsVarArg) - Ops.push_back(FINode); - - SmallVector<EVT, 8> InTys; - for (const auto &In : Ins) { - assert(!In.Flags.isByVal() && "byval is not valid for return values"); - assert(!In.Flags.isNest() && "nest is not valid for return values"); - if (In.Flags.isInAlloca()) - fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values"); - if (In.Flags.isInConsecutiveRegs()) - fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values"); - if (In.Flags.isInConsecutiveRegsLast()) - fail(DL, DAG, - "WebAssembly hasn't implemented cons regs last return values"); - // Ignore In.getOrigAlign() because all our arguments are passed in - // registers. - InTys.push_back(In.VT); - } - - if (CLI.IsTailCall) { - // ret_calls do not return values to the current frame - SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); - return DAG.getNode(WebAssemblyISD::RET_CALL, DL, NodeTys, Ops); - } - - InTys.push_back(MVT::Other); - SDVTList InTyList = DAG.getVTList(InTys); - SDValue Res = - DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1, - DL, InTyList, Ops); - if (Ins.empty()) { - Chain = Res; - } else { - InVals.push_back(Res); - Chain = Res.getValue(1); - } - - return Chain; -} - -bool WebAssemblyTargetLowering::CanLowerReturn( - CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, - const SmallVectorImpl<ISD::OutputArg> &Outs, - LLVMContext & /*Context*/) const { - // WebAssembly can't currently handle returning tuples. - return Outs.size() <= 1; -} - -SDValue WebAssemblyTargetLowering::LowerReturn( - SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, - const SmallVectorImpl<ISD::OutputArg> &Outs, - const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, - SelectionDAG &DAG) const { - assert(Outs.size() <= 1 && "WebAssembly can only return up to one value"); - if (!callingConvSupported(CallConv)) - fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); - - SmallVector<SDValue, 4> RetOps(1, Chain); - RetOps.append(OutVals.begin(), OutVals.end()); - Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps); - - // Record the number and types of the return values. - for (const ISD::OutputArg &Out : Outs) { - assert(!Out.Flags.isByVal() && "byval is not valid for return values"); - assert(!Out.Flags.isNest() && "nest is not valid for return values"); - assert(Out.IsFixed && "non-fixed return value is not valid"); - if (Out.Flags.isInAlloca()) - fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); - if (Out.Flags.isInConsecutiveRegs()) - fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); - if (Out.Flags.isInConsecutiveRegsLast()) - fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); - } - - return Chain; -} - -SDValue WebAssemblyTargetLowering::LowerFormalArguments( - SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, - const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, - SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { - if (!callingConvSupported(CallConv)) - fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); - - MachineFunction &MF = DAG.getMachineFunction(); - auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>(); - - // Set up the incoming ARGUMENTS value, which serves to represent the liveness - // of the incoming values before they're represented by virtual registers. - MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); - - for (const ISD::InputArg &In : Ins) { - if (In.Flags.isInAlloca()) - fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); - if (In.Flags.isNest()) - fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); - if (In.Flags.isInConsecutiveRegs()) - fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); - if (In.Flags.isInConsecutiveRegsLast()) - 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)); - - // Record the number and types of arguments. - MFI->addParam(In.VT); - } - - // Varargs are copied into a buffer allocated by the caller, and a pointer to - // the buffer is passed as an argument. - if (IsVarArg) { - MVT PtrVT = getPointerTy(MF.getDataLayout()); - unsigned VarargVreg = - MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrVT)); - MFI->setVarargBufferVreg(VarargVreg); - Chain = DAG.getCopyToReg( - Chain, DL, VarargVreg, - DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT, - DAG.getTargetConstant(Ins.size(), DL, MVT::i32))); - MFI->addParam(PtrVT); - } - - // Record the number and types of arguments and results. - SmallVector<MVT, 4> Params; - SmallVector<MVT, 4> 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; -} - -void WebAssemblyTargetLowering::ReplaceNodeResults( - SDNode *N, SmallVectorImpl<SDValue> &Results, SelectionDAG &DAG) const { - switch (N->getOpcode()) { - case ISD::SIGN_EXTEND_INREG: - // Do not add any results, signifying that N should not be custom lowered - // after all. This happens because simd128 turns on custom lowering for - // SIGN_EXTEND_INREG, but for non-vector sign extends the result might be an - // illegal type. - break; - default: - llvm_unreachable( - "ReplaceNodeResults not implemented for this op for WebAssembly!"); - } -} - -//===----------------------------------------------------------------------===// -// Custom lowering hooks. -//===----------------------------------------------------------------------===// - -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: - return LowerRETURNADDR(Op, DAG); - case ISD::FRAMEADDR: - return LowerFRAMEADDR(Op, DAG); - case ISD::CopyToReg: - return LowerCopyToReg(Op, DAG); - case ISD::EXTRACT_VECTOR_ELT: - case ISD::INSERT_VECTOR_ELT: - return LowerAccessVectorElement(Op, DAG); - case ISD::INTRINSIC_VOID: - case ISD::INTRINSIC_WO_CHAIN: - case ISD::INTRINSIC_W_CHAIN: - return LowerIntrinsic(Op, DAG); - case ISD::SIGN_EXTEND_INREG: - return LowerSIGN_EXTEND_INREG(Op, DAG); - case ISD::BUILD_VECTOR: - return LowerBUILD_VECTOR(Op, DAG); - case ISD::VECTOR_SHUFFLE: - return LowerVECTOR_SHUFFLE(Op, DAG); - case ISD::SHL: - case ISD::SRA: - case ISD::SRL: - return LowerShift(Op, DAG); - } -} - -SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, - SelectionDAG &DAG) const { - SDValue Src = Op.getOperand(2); - if (isa<FrameIndexSDNode>(Src.getNode())) { - // CopyToReg nodes don't support FrameIndex operands. Other targets select - // 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 - // 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); - return Op.getNode()->getNumValues() == 1 - ? DAG.getCopyToReg(Chain, DL, Reg, Copy) - : DAG.getCopyToReg(Chain, DL, Reg, Copy, - Op.getNumOperands() == 4 ? Op.getOperand(3) - : SDValue()); - } - return SDValue(); -} - -SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op, - SelectionDAG &DAG) const { - int FI = cast<FrameIndexSDNode>(Op)->getIndex(); - return DAG.getTargetFrameIndex(FI, Op.getValueType()); -} - -SDValue WebAssemblyTargetLowering::LowerRETURNADDR(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - - if (!Subtarget->getTargetTriple().isOSEmscripten()) { - fail(DL, DAG, - "Non-Emscripten WebAssembly hasn't implemented " - "__builtin_return_address"); - return SDValue(); - } - - if (verifyReturnAddressArgumentIsConstant(Op, DAG)) - return SDValue(); - - unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); - return makeLibCall(DAG, RTLIB::RETURN_ADDRESS, Op.getValueType(), - {DAG.getConstant(Depth, DL, MVT::i32)}, false, DL) - .first; -} - -SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, - SelectionDAG &DAG) const { - // Non-zero depths are not supported by WebAssembly currently. Use the - // legalizer's default expansion, which is to return 0 (what this function is - // documented to do). - if (Op.getConstantOperandVal(0) > 0) - return SDValue(); - - DAG.getMachineFunction().getFrameInfo().setFrameAddressIsTaken(true); - EVT VT = Op.getValueType(); - unsigned FP = - Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); - return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); -} - -SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - const auto *GA = cast<GlobalAddressSDNode>(Op); - EVT VT = Op.getValueType(); - assert(GA->getTargetFlags() == 0 && - "Unexpected target flags on generic GlobalAddressSDNode"); - if (GA->getAddressSpace() != 0) - fail(DL, DAG, "WebAssembly only expects the 0 address space"); - - unsigned OperandFlags = 0; - if (isPositionIndependent()) { - const GlobalValue *GV = GA->getGlobal(); - if (getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) { - MachineFunction &MF = DAG.getMachineFunction(); - MVT PtrVT = getPointerTy(MF.getDataLayout()); - const char *BaseName; - if (GV->getValueType()->isFunctionTy()) { - BaseName = MF.createExternalSymbolName("__table_base"); - OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL; - } - else { - BaseName = MF.createExternalSymbolName("__memory_base"); - OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL; - } - SDValue BaseAddr = - DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, - DAG.getTargetExternalSymbol(BaseName, PtrVT)); - - SDValue SymAddr = DAG.getNode( - WebAssemblyISD::WrapperPIC, DL, VT, - DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), - OperandFlags)); - - return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr); - } else { - OperandFlags = WebAssemblyII::MO_GOT; - } - } - - return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, - DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, - GA->getOffset(), OperandFlags)); -} - -SDValue -WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - const auto *ES = cast<ExternalSymbolSDNode>(Op); - EVT VT = Op.getValueType(); - assert(ES->getTargetFlags() == 0 && - "Unexpected target flags on generic ExternalSymbolSDNode"); - return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, - DAG.getTargetExternalSymbol(ES->getSymbol(), VT)); -} - -SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op, - SelectionDAG &DAG) const { - // There's no need for a Wrapper node because we always incorporate a jump - // table operand into a BR_TABLE instruction, rather than ever - // materializing it in a register. - const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); - return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(), - JT->getTargetFlags()); -} - -SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - SDValue Chain = Op.getOperand(0); - const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1)); - SDValue Index = Op.getOperand(2); - assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); - - SmallVector<SDValue, 8> Ops; - Ops.push_back(Chain); - Ops.push_back(Index); - - MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo(); - const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs; - - // Add an operand for each case. - 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 - // delete the guard). - Ops.push_back(DAG.getBasicBlock(MBBs[0])); - - return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops); -} - -SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout()); - - auto *MFI = DAG.getMachineFunction().getInfo<WebAssemblyFunctionInfo>(); - const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); - - SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, - MFI->getVarargBufferVreg(), PtrVT); - return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), - MachinePointerInfo(SV), 0); -} - -SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, - SelectionDAG &DAG) const { - MachineFunction &MF = DAG.getMachineFunction(); - unsigned IntNo; - switch (Op.getOpcode()) { - case ISD::INTRINSIC_VOID: - case ISD::INTRINSIC_W_CHAIN: - IntNo = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue(); - break; - case ISD::INTRINSIC_WO_CHAIN: - IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); - break; - default: - llvm_unreachable("Invalid intrinsic"); - } - SDLoc DL(Op); - - switch (IntNo) { - default: - return SDValue(); // Don't custom lower most intrinsics. - - case Intrinsic::wasm_lsda: { - 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)); - } - - case Intrinsic::wasm_throw: { - // We only support C++ exceptions for now - int Tag = cast<ConstantSDNode>(Op.getOperand(2).getNode())->getZExtValue(); - if (Tag != CPP_EXCEPTION) - llvm_unreachable("Invalid tag!"); - 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)); - return DAG.getNode(WebAssemblyISD::THROW, DL, - MVT::Other, // outchain type - { - Op.getOperand(0), // inchain - SymNode, // exception symbol - Op.getOperand(3) // thrown value - }); - } - } -} - -SDValue -WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - // 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) { - const SDValue &Extract = Op.getOperand(0); - MVT VecT = Extract.getOperand(0).getSimpleValueType(); - MVT ExtractedLaneT = static_cast<VTSDNode *>(Op.getOperand(1).getNode()) - ->getVT() - .getSimpleVT(); - MVT ExtractedVecT = - MVT::getVectorVT(ExtractedLaneT, 128 / ExtractedLaneT.getSizeInBits()); - if (ExtractedVecT == VecT) - return Op; - // Bitcast vector to appropriate type to ensure ISel pattern coverage - const SDValue &Index = Extract.getOperand(1); - unsigned IndexVal = - static_cast<ConstantSDNode *>(Index.getNode())->getZExtValue(); - unsigned Scale = - ExtractedVecT.getVectorNumElements() / VecT.getVectorNumElements(); - assert(Scale > 1); - SDValue NewIndex = - DAG.getConstant(IndexVal * Scale, DL, Index.getValueType()); - SDValue NewExtract = DAG.getNode( - ISD::EXTRACT_VECTOR_ELT, DL, Extract.getValueType(), - DAG.getBitcast(ExtractedVecT, Extract.getOperand(0)), NewIndex); - return DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, Op.getValueType(), - NewExtract, Op.getOperand(1)); - } - // Otherwise expand - return SDValue(); -} - -SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, - SelectionDAG &DAG) const { - SDLoc DL(Op); - const EVT VecT = Op.getValueType(); - const EVT LaneT = Op.getOperand(0).getValueType(); - const size_t Lanes = Op.getNumOperands(); - auto IsConstant = [](const SDValue &V) { - return V.getOpcode() == ISD::Constant || V.getOpcode() == ISD::ConstantFP; - }; - - // Find the most common operand, which is approximately the best to splat - using Entry = std::pair<SDValue, size_t>; - SmallVector<Entry, 16> ValueCounts; - size_t NumConst = 0, NumDynamic = 0; - for (const SDValue &Lane : Op->op_values()) { - if (Lane.isUndef()) { - continue; - } else if (IsConstant(Lane)) { - NumConst++; - } else { - NumDynamic++; - } - auto CountIt = std::find_if(ValueCounts.begin(), ValueCounts.end(), - [&Lane](Entry A) { return A.first == Lane; }); - if (CountIt == ValueCounts.end()) { - ValueCounts.emplace_back(Lane, 1); - } else { - CountIt->second++; - } - } - auto CommonIt = - std::max_element(ValueCounts.begin(), ValueCounts.end(), - [](Entry A, Entry B) { return A.second < B.second; }); - assert(CommonIt != ValueCounts.end() && "Unexpected all-undef build_vector"); - SDValue SplatValue = CommonIt->first; - size_t NumCommon = CommonIt->second; - - // If v128.const is available, consider using it instead of a splat - if (Subtarget->hasUnimplementedSIMD128()) { - // {i32,i64,f32,f64}.const opcode, and value - const size_t ConstBytes = 1 + std::max(size_t(4), 16 / Lanes); - // SIMD prefix and opcode - const size_t SplatBytes = 2; - const size_t SplatConstBytes = SplatBytes + ConstBytes; - // SIMD prefix, opcode, and lane index - const size_t ReplaceBytes = 3; - const size_t ReplaceConstBytes = ReplaceBytes + ConstBytes; - // SIMD prefix, v128.const opcode, and 128-bit value - const size_t VecConstBytes = 18; - // Initial v128.const and a replace_lane for each non-const operand - const size_t ConstInitBytes = VecConstBytes + NumDynamic * ReplaceBytes; - // Initial splat and all necessary replace_lanes - const size_t SplatInitBytes = - IsConstant(SplatValue) - // Initial constant splat - ? (SplatConstBytes + - // Constant replace_lanes - (NumConst - NumCommon) * ReplaceConstBytes + - // Dynamic replace_lanes - (NumDynamic * ReplaceBytes)) - // Initial dynamic splat - : (SplatBytes + - // Constant replace_lanes - (NumConst * ReplaceConstBytes) + - // Dynamic replace_lanes - (NumDynamic - NumCommon) * ReplaceBytes); - if (ConstInitBytes < SplatInitBytes) { - // Create build_vector that will lower to initial v128.const - SmallVector<SDValue, 16> ConstLanes; - for (const SDValue &Lane : Op->op_values()) { - if (IsConstant(Lane)) { - ConstLanes.push_back(Lane); - } else if (LaneT.isFloatingPoint()) { - ConstLanes.push_back(DAG.getConstantFP(0, DL, LaneT)); - } else { - ConstLanes.push_back(DAG.getConstant(0, DL, LaneT)); - } - } - SDValue Result = DAG.getBuildVector(VecT, DL, ConstLanes); - // Add replace_lane instructions for non-const lanes - for (size_t I = 0; I < Lanes; ++I) { - const SDValue &Lane = Op->getOperand(I); - if (!Lane.isUndef() && !IsConstant(Lane)) - Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VecT, Result, Lane, - DAG.getConstant(I, DL, MVT::i32)); - } - return Result; - } - } - // Use a splat for the initial vector - SDValue Result = DAG.getSplatBuildVector(VecT, DL, SplatValue); - // Add replace_lane instructions for other values - for (size_t I = 0; I < Lanes; ++I) { - const SDValue &Lane = Op->getOperand(I); - if (Lane != SplatValue) - Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VecT, Result, Lane, - DAG.getConstant(I, DL, MVT::i32)); - } - return Result; -} - -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 (int M : Mask) { - for (size_t J = 0; J < LaneBytes; ++J) { - // Lower undefs (represented by -1 in mask) to zero - uint64_t ByteIndex = M == -1 ? 0 : (uint64_t)M * 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 - auto *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)); -} - -//===----------------------------------------------------------------------===// -// WebAssembly Optimization Hooks -//===----------------------------------------------------------------------===// diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h deleted file mode 100644 index b3c7f3defd5f..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ /dev/null @@ -1,122 +0,0 @@ -//- WebAssemblyISelLowering.h - WebAssembly DAG Lowering Interface -*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines the interfaces that WebAssembly uses to lower LLVM -/// code into a selection DAG. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYISELLOWERING_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYISELLOWERING_H - -#include "llvm/CodeGen/TargetLowering.h" - -namespace llvm { - -namespace WebAssemblyISD { - -enum NodeType : unsigned { - FIRST_NUMBER = ISD::BUILTIN_OP_END, -#define HANDLE_NODETYPE(NODE) NODE, -#include "WebAssemblyISD.def" -#undef HANDLE_NODETYPE -}; - -} // end namespace WebAssemblyISD - -class WebAssemblySubtarget; -class WebAssemblyTargetMachine; - -class WebAssemblyTargetLowering final : public TargetLowering { -public: - WebAssemblyTargetLowering(const TargetMachine &TM, - const WebAssemblySubtarget &STI); - -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; - MVT getScalarShiftAmountTy(const DataLayout &DL, EVT) const override; - MachineBasicBlock * - 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; - bool isCheapToSpeculateCttz() const override; - bool isCheapToSpeculateCtlz() const override; - bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, - unsigned AS, - Instruction *I = nullptr) const override; - bool allowsMisalignedMemoryAccesses(EVT, unsigned AddrSpace, unsigned Align, - MachineMemOperand::Flags Flags, - bool *Fast) const override; - bool isIntDivCheap(EVT VT, AttributeList Attr) const override; - - 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; - bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, - bool isVarArg, - const SmallVectorImpl<ISD::OutputArg> &Outs, - LLVMContext &Context) const override; - SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, - const SmallVectorImpl<ISD::OutputArg> &Outs, - const SmallVectorImpl<SDValue> &OutVals, const SDLoc &dl, - SelectionDAG &DAG) const override; - SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, - bool IsVarArg, - const SmallVectorImpl<ISD::InputArg> &Ins, - const SDLoc &DL, SelectionDAG &DAG, - SmallVectorImpl<SDValue> &InVals) const override; - - void ReplaceNodeResults(SDNode *N, SmallVectorImpl<SDValue> &Results, - SelectionDAG &DAG) const override; - - const char *getClearCacheBuiltinName() const override { - report_fatal_error("llvm.clear_cache is not supported on wasm"); - } - - // Custom lowering hooks. - SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; - SDValue LowerFrameIndex(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerIntrinsic(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerSIGN_EXTEND_INREG(SDValue Op, SelectionDAG &DAG) const; - SDValue LowerBUILD_VECTOR(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 llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td deleted file mode 100644 index e85aa57efc42..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td +++ /dev/null @@ -1,899 +0,0 @@ -// WebAssemblyInstrAtomics.td-WebAssembly Atomic codegen support-*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly Atomic operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -let UseNamedOperandTable = 1 in -multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s, - list<dag> pattern_r, string asmstr_r = "", - string asmstr_s = "", bits<32> atomic_op = -1> { - defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s, - !or(0xfe00, !and(0xff, atomic_op))>, - Requires<[HasAtomics]>; -} - -multiclass ATOMIC_NRI<dag oops, dag iops, list<dag> pattern, string asmstr = "", - bits<32> atomic_op = -1> { - defm "" : NRI<oops, iops, pattern, asmstr, - !or(0xfe00, !and(0xff, atomic_op))>, - Requires<[HasAtomics]>; -} - -//===----------------------------------------------------------------------===// -// Atomic wait / notify -//===----------------------------------------------------------------------===// - -let hasSideEffects = 1 in { -defm ATOMIC_NOTIFY : - ATOMIC_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}", 0x00>; -let mayLoad = 1 in { -defm ATOMIC_WAIT_I32 : - ATOMIC_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}", 0x01>; -defm ATOMIC_WAIT_I64 : - ATOMIC_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}", 0x02>; -} // mayLoad = 1 -} // hasSideEffects = 1 - -let Predicates = [HasAtomics] in { -// Select notifys with no constant offset. -def NotifyPatNoOffset : - Pat<(i32 (int_wasm_atomic_notify I32:$addr, I32:$count)), - (ATOMIC_NOTIFY 0, 0, I32:$addr, I32:$count)>; - -// Select notifys with a constant offset. - -// Pattern with address + immediate offset -class NotifyPatImmOff<PatFrag operand> : - Pat<(i32 (int_wasm_atomic_notify (operand I32:$addr, imm:$off), I32:$count)), - (ATOMIC_NOTIFY 0, imm:$off, I32:$addr, I32:$count)>; -def : NotifyPatImmOff<regPlusImm>; -def : NotifyPatImmOff<or_is_add>; - -def NotifyPatGlobalAddr : - Pat<(i32 (int_wasm_atomic_notify (regPlusGA I32:$addr, - (WebAssemblywrapper tglobaladdr:$off)), - I32:$count)), - (ATOMIC_NOTIFY 0, tglobaladdr:$off, I32:$addr, I32:$count)>; - -// Select notifys with just a constant offset. -def NotifyPatOffsetOnly : - Pat<(i32 (int_wasm_atomic_notify imm:$off, I32:$count)), - (ATOMIC_NOTIFY 0, imm:$off, (CONST_I32 0), I32:$count)>; - -def NotifyPatGlobalAddrOffOnly : - Pat<(i32 (int_wasm_atomic_notify (WebAssemblywrapper tglobaladdr:$off), - I32:$count)), - (ATOMIC_NOTIFY 0, tglobaladdr:$off, (CONST_I32 0), I32:$count)>; - -// 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>; - -// 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>; -} // Predicates = [HasAtomics] - -//===----------------------------------------------------------------------===// -// Atomic loads -//===----------------------------------------------------------------------===// - -multiclass AtomicLoad<WebAssemblyRegClass rc, string name, int atomic_op> { - defm "" : WebAssemblyLoad<rc, name, !or(0xfe00, !and(0xff, atomic_op))>, - Requires<[HasAtomics]>; -} - -defm ATOMIC_LOAD_I32 : AtomicLoad<I32, "i32.atomic.load", 0x10>; -defm ATOMIC_LOAD_I64 : AtomicLoad<I64, "i64.atomic.load", 0x11>; - -// Select loads with no constant offset. -let Predicates = [HasAtomics] in { -def : LoadPatNoOffset<i32, atomic_load_32, ATOMIC_LOAD_I32>; -def : LoadPatNoOffset<i64, atomic_load_64, ATOMIC_LOAD_I64>; - -// Select loads with a constant offset. - -// Pattern with address + immediate offset -def : LoadPatImmOff<i32, atomic_load_32, regPlusImm, ATOMIC_LOAD_I32>; -def : LoadPatImmOff<i64, atomic_load_64, regPlusImm, ATOMIC_LOAD_I64>; -def : LoadPatImmOff<i32, atomic_load_32, or_is_add, ATOMIC_LOAD_I32>; -def : LoadPatImmOff<i64, atomic_load_64, or_is_add, ATOMIC_LOAD_I64>; - -def : LoadPatGlobalAddr<i32, atomic_load_32, ATOMIC_LOAD_I32>; -def : LoadPatGlobalAddr<i64, atomic_load_64, ATOMIC_LOAD_I64>; - -// Select loads with just a constant offset. -def : LoadPatOffsetOnly<i32, atomic_load_32, ATOMIC_LOAD_I32>; -def : LoadPatOffsetOnly<i64, atomic_load_64, ATOMIC_LOAD_I64>; - -def : LoadPatGlobalAddrOffOnly<i32, atomic_load_32, ATOMIC_LOAD_I32>; -def : LoadPatGlobalAddrOffOnly<i64, atomic_load_64, ATOMIC_LOAD_I64>; - -} // Predicates = [HasAtomics] - -// Extending loads. Note that there are only zero-extending atomic loads, no -// sign-extending loads. -defm ATOMIC_LOAD8_U_I32 : AtomicLoad<I32, "i32.atomic.load8_u", 0x12>; -defm ATOMIC_LOAD16_U_I32 : AtomicLoad<I32, "i32.atomic.load16_u", 0x13>; -defm ATOMIC_LOAD8_U_I64 : AtomicLoad<I64, "i64.atomic.load8_u", 0x14>; -defm ATOMIC_LOAD16_U_I64 : AtomicLoad<I64, "i64.atomic.load16_u", 0x15>; -defm ATOMIC_LOAD32_U_I64 : AtomicLoad<I64, "i64.atomic.load32_u", 0x16>; - -// Fragments for extending loads. These are different from regular loads because -// the SDNodes are derived from AtomicSDNode rather than LoadSDNode and -// therefore don't have the extension type field. So instead of matching that, -// we match the patterns that the type legalizer expands them to. - -// We directly match zext patterns and select the zext atomic loads. -// i32 (zext (i8 (atomic_load_8))) gets legalized to -// i32 (and (i32 (atomic_load_8)), 255) -// These can be selected to a single zero-extending atomic load instruction. -def zext_aload_8_32 : - PatFrag<(ops node:$addr), (and (i32 (atomic_load_8 node:$addr)), 255)>; -def zext_aload_16_32 : - PatFrag<(ops node:$addr), (and (i32 (atomic_load_16 node:$addr)), 65535)>; -// Unlike regular loads, extension to i64 is handled differently than i32. -// i64 (zext (i8 (atomic_load_8))) gets legalized to -// i64 (and (i64 (anyext (i32 (atomic_load_8)))), 255) -def zext_aload_8_64 : - PatFrag<(ops node:$addr), - (and (i64 (anyext (i32 (atomic_load_8 node:$addr)))), 255)>; -def zext_aload_16_64 : - PatFrag<(ops node:$addr), - (and (i64 (anyext (i32 (atomic_load_16 node:$addr)))), 65535)>; -def zext_aload_32_64 : - PatFrag<(ops node:$addr), - (zext (i32 (atomic_load node:$addr)))>; - -// We don't have single sext atomic load instructions. So for sext loads, we -// match bare subword loads (for 32-bit results) and anyext loads (for 64-bit -// results) and select a zext load; the next instruction will be sext_inreg -// which is selected by itself. -def sext_aload_8_64 : - PatFrag<(ops node:$addr), (anyext (i32 (atomic_load_8 node:$addr)))>; -def sext_aload_16_64 : - PatFrag<(ops node:$addr), (anyext (i32 (atomic_load_16 node:$addr)))>; - -let Predicates = [HasAtomics] in { -// Select zero-extending loads with no constant offset. -def : LoadPatNoOffset<i32, zext_aload_8_32, ATOMIC_LOAD8_U_I32>; -def : LoadPatNoOffset<i32, zext_aload_16_32, ATOMIC_LOAD16_U_I32>; -def : LoadPatNoOffset<i64, zext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatNoOffset<i64, zext_aload_16_64, ATOMIC_LOAD16_U_I64>; -def : LoadPatNoOffset<i64, zext_aload_32_64, ATOMIC_LOAD32_U_I64>; - -// Select sign-extending loads with no constant offset -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_i32_s - -// Zero-extending loads with constant offset -def : LoadPatImmOff<i32, zext_aload_8_32, regPlusImm, ATOMIC_LOAD8_U_I32>; -def : LoadPatImmOff<i32, zext_aload_16_32, regPlusImm, ATOMIC_LOAD16_U_I32>; -def : LoadPatImmOff<i32, zext_aload_8_32, or_is_add, ATOMIC_LOAD8_U_I32>; -def : LoadPatImmOff<i32, zext_aload_16_32, or_is_add, ATOMIC_LOAD16_U_I32>; -def : LoadPatImmOff<i64, zext_aload_8_64, regPlusImm, ATOMIC_LOAD8_U_I64>; -def : LoadPatImmOff<i64, zext_aload_16_64, regPlusImm, ATOMIC_LOAD16_U_I64>; -def : LoadPatImmOff<i64, zext_aload_32_64, regPlusImm, ATOMIC_LOAD32_U_I64>; -def : LoadPatImmOff<i64, zext_aload_8_64, or_is_add, ATOMIC_LOAD8_U_I64>; -def : LoadPatImmOff<i64, zext_aload_16_64, or_is_add, ATOMIC_LOAD16_U_I64>; -def : LoadPatImmOff<i64, zext_aload_32_64, or_is_add, ATOMIC_LOAD32_U_I64>; - -// Sign-extending loads with constant offset -def : LoadPatImmOff<i32, atomic_load_8, regPlusImm, ATOMIC_LOAD8_U_I32>; -def : LoadPatImmOff<i32, atomic_load_16, regPlusImm, ATOMIC_LOAD16_U_I32>; -def : LoadPatImmOff<i32, atomic_load_8, or_is_add, ATOMIC_LOAD8_U_I32>; -def : LoadPatImmOff<i32, atomic_load_16, or_is_add, ATOMIC_LOAD16_U_I32>; -def : LoadPatImmOff<i64, sext_aload_8_64, regPlusImm, ATOMIC_LOAD8_U_I64>; -def : LoadPatImmOff<i64, sext_aload_16_64, regPlusImm, ATOMIC_LOAD16_U_I64>; -def : LoadPatImmOff<i64, sext_aload_8_64, or_is_add, ATOMIC_LOAD8_U_I64>; -def : LoadPatImmOff<i64, sext_aload_16_64, or_is_add, ATOMIC_LOAD16_U_I64>; -// No 32->64 patterns, just use i32.atomic.load and i64.extend_s/i64 - -def : LoadPatGlobalAddr<i32, zext_aload_8_32, ATOMIC_LOAD8_U_I32>; -def : LoadPatGlobalAddr<i32, zext_aload_16_32, ATOMIC_LOAD16_U_I32>; -def : LoadPatGlobalAddr<i64, zext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatGlobalAddr<i64, zext_aload_16_64, ATOMIC_LOAD16_U_I64>; -def : LoadPatGlobalAddr<i64, zext_aload_32_64, ATOMIC_LOAD32_U_I64>; -def : LoadPatGlobalAddr<i32, atomic_load_8, ATOMIC_LOAD8_U_I32>; -def : LoadPatGlobalAddr<i32, atomic_load_16, ATOMIC_LOAD16_U_I32>; -def : LoadPatGlobalAddr<i64, sext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatGlobalAddr<i64, sext_aload_16_64, ATOMIC_LOAD16_U_I64>; - -// Extending loads with just a constant offset -def : LoadPatOffsetOnly<i32, zext_aload_8_32, ATOMIC_LOAD8_U_I32>; -def : LoadPatOffsetOnly<i32, zext_aload_16_32, ATOMIC_LOAD16_U_I32>; -def : LoadPatOffsetOnly<i64, zext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatOffsetOnly<i64, zext_aload_16_64, ATOMIC_LOAD16_U_I64>; -def : LoadPatOffsetOnly<i64, zext_aload_32_64, ATOMIC_LOAD32_U_I64>; -def : LoadPatOffsetOnly<i32, atomic_load_8, ATOMIC_LOAD8_U_I32>; -def : LoadPatOffsetOnly<i32, atomic_load_16, ATOMIC_LOAD16_U_I32>; -def : LoadPatOffsetOnly<i64, sext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatOffsetOnly<i64, sext_aload_16_64, ATOMIC_LOAD16_U_I64>; - -def : LoadPatGlobalAddrOffOnly<i32, zext_aload_8_32, ATOMIC_LOAD8_U_I32>; -def : LoadPatGlobalAddrOffOnly<i32, zext_aload_16_32, ATOMIC_LOAD16_U_I32>; -def : LoadPatGlobalAddrOffOnly<i64, zext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, zext_aload_16_64, ATOMIC_LOAD16_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, zext_aload_32_64, ATOMIC_LOAD32_U_I64>; -def : LoadPatGlobalAddrOffOnly<i32, atomic_load_8, ATOMIC_LOAD8_U_I32>; -def : LoadPatGlobalAddrOffOnly<i32, atomic_load_16, ATOMIC_LOAD16_U_I32>; -def : LoadPatGlobalAddrOffOnly<i64, sext_aload_8_64, ATOMIC_LOAD8_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, sext_aload_16_64, ATOMIC_LOAD16_U_I64>; - -} // Predicates = [HasAtomics] - -//===----------------------------------------------------------------------===// -// Atomic stores -//===----------------------------------------------------------------------===// - -multiclass AtomicStore<WebAssemblyRegClass rc, string name, int atomic_op> { - defm "" : WebAssemblyStore<rc, name, !or(0xfe00, !and(0xff, atomic_op))>, - Requires<[HasAtomics]>; -} - -defm ATOMIC_STORE_I32 : AtomicStore<I32, "i32.atomic.store", 0x17>; -defm ATOMIC_STORE_I64 : AtomicStore<I64, "i64.atomic.store", 0x18>; - -// We need an 'atomic' version of store patterns because store and atomic_store -// nodes have different operand orders: -// store: (store $val, $ptr) -// atomic_store: (store $ptr, $val) - -let Predicates = [HasAtomics] in { - -// Select stores with no constant offset. -class AStorePatNoOffset<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind I32:$addr, ty:$val), (inst 0, 0, I32:$addr, ty:$val)>; -def : AStorePatNoOffset<i32, atomic_store_32, ATOMIC_STORE_I32>; -def : AStorePatNoOffset<i64, atomic_store_64, ATOMIC_STORE_I64>; - -// Select stores with a constant offset. - -// Pattern with address + immediate offset -class AStorePatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> : - Pat<(kind (operand I32:$addr, imm:$off), ty:$val), - (inst 0, imm:$off, I32:$addr, ty:$val)>; -def : AStorePatImmOff<i32, atomic_store_32, regPlusImm, ATOMIC_STORE_I32>; -def : AStorePatImmOff<i64, atomic_store_64, regPlusImm, ATOMIC_STORE_I64>; -def : AStorePatImmOff<i32, atomic_store_32, or_is_add, ATOMIC_STORE_I32>; -def : AStorePatImmOff<i64, atomic_store_64, or_is_add, ATOMIC_STORE_I64>; - -class AStorePatGlobalAddr<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)), - ty:$val), - (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>; -def : AStorePatGlobalAddr<i32, atomic_store_32, ATOMIC_STORE_I32>; -def : AStorePatGlobalAddr<i64, atomic_store_64, ATOMIC_STORE_I64>; - -// Select stores with just a constant offset. -class AStorePatOffsetOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind imm:$off, ty:$val), (inst 0, imm:$off, (CONST_I32 0), ty:$val)>; -def : AStorePatOffsetOnly<i32, atomic_store_32, ATOMIC_STORE_I32>; -def : AStorePatOffsetOnly<i64, atomic_store_64, ATOMIC_STORE_I64>; - -class AStorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind (WebAssemblywrapper tglobaladdr:$off), ty:$val), - (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>; -def : AStorePatGlobalAddrOffOnly<i32, atomic_store_32, ATOMIC_STORE_I32>; -def : AStorePatGlobalAddrOffOnly<i64, atomic_store_64, ATOMIC_STORE_I64>; - -} // Predicates = [HasAtomics] - -// Truncating stores. -defm ATOMIC_STORE8_I32 : AtomicStore<I32, "i32.atomic.store8", 0x19>; -defm ATOMIC_STORE16_I32 : AtomicStore<I32, "i32.atomic.store16", 0x1a>; -defm ATOMIC_STORE8_I64 : AtomicStore<I64, "i64.atomic.store8", 0x1b>; -defm ATOMIC_STORE16_I64 : AtomicStore<I64, "i64.atomic.store16", 0x1c>; -defm ATOMIC_STORE32_I64 : AtomicStore<I64, "i64.atomic.store32", 0x1d>; - -// Fragments for truncating stores. - -// We don't have single truncating atomic store instructions. For 32-bit -// instructions, we just need to match bare atomic stores. On the other hand, -// truncating stores from i64 values are once truncated to i32 first. -class trunc_astore_64<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), - (kind node:$addr, (i32 (trunc (i64 node:$val))))>; -def trunc_astore_8_64 : trunc_astore_64<atomic_store_8>; -def trunc_astore_16_64 : trunc_astore_64<atomic_store_16>; -def trunc_astore_32_64 : trunc_astore_64<atomic_store_32>; - -let Predicates = [HasAtomics] in { - -// Truncating stores with no constant offset -def : AStorePatNoOffset<i32, atomic_store_8, ATOMIC_STORE8_I32>; -def : AStorePatNoOffset<i32, atomic_store_16, ATOMIC_STORE16_I32>; -def : AStorePatNoOffset<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>; -def : AStorePatNoOffset<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>; -def : AStorePatNoOffset<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>; - -// Truncating stores with a constant offset -def : AStorePatImmOff<i32, atomic_store_8, regPlusImm, ATOMIC_STORE8_I32>; -def : AStorePatImmOff<i32, atomic_store_16, regPlusImm, ATOMIC_STORE16_I32>; -def : AStorePatImmOff<i64, trunc_astore_8_64, regPlusImm, ATOMIC_STORE8_I64>; -def : AStorePatImmOff<i64, trunc_astore_16_64, regPlusImm, ATOMIC_STORE16_I64>; -def : AStorePatImmOff<i64, trunc_astore_32_64, regPlusImm, ATOMIC_STORE32_I64>; -def : AStorePatImmOff<i32, atomic_store_8, or_is_add, ATOMIC_STORE8_I32>; -def : AStorePatImmOff<i32, atomic_store_16, or_is_add, ATOMIC_STORE16_I32>; -def : AStorePatImmOff<i64, trunc_astore_8_64, or_is_add, ATOMIC_STORE8_I64>; -def : AStorePatImmOff<i64, trunc_astore_16_64, or_is_add, ATOMIC_STORE16_I64>; -def : AStorePatImmOff<i64, trunc_astore_32_64, or_is_add, ATOMIC_STORE32_I64>; - -def : AStorePatGlobalAddr<i32, atomic_store_8, ATOMIC_STORE8_I32>; -def : AStorePatGlobalAddr<i32, atomic_store_16, ATOMIC_STORE16_I32>; -def : AStorePatGlobalAddr<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>; -def : AStorePatGlobalAddr<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>; -def : AStorePatGlobalAddr<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>; - -// Truncating stores with just a constant offset -def : AStorePatOffsetOnly<i32, atomic_store_8, ATOMIC_STORE8_I32>; -def : AStorePatOffsetOnly<i32, atomic_store_16, ATOMIC_STORE16_I32>; -def : AStorePatOffsetOnly<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>; -def : AStorePatOffsetOnly<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>; -def : AStorePatOffsetOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>; - -def : AStorePatGlobalAddrOffOnly<i32, atomic_store_8, ATOMIC_STORE8_I32>; -def : AStorePatGlobalAddrOffOnly<i32, atomic_store_16, ATOMIC_STORE16_I32>; -def : AStorePatGlobalAddrOffOnly<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>; -def : AStorePatGlobalAddrOffOnly<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>; -def : AStorePatGlobalAddrOffOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>; - -} // Predicates = [HasAtomics] - -//===----------------------------------------------------------------------===// -// Atomic binary read-modify-writes -//===----------------------------------------------------------------------===// - -multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string name, - int atomic_op> { - defm "" : - ATOMIC_I<(outs rc:$dst), - (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val), - (outs), (ins P2Align:$p2align, offset32_op:$off), [], - !strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"), - !strconcat(name, "\t${off}${p2align}"), atomic_op>; -} - -defm ATOMIC_RMW_ADD_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.add", 0x1e>; -defm ATOMIC_RMW_ADD_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.add", 0x1f>; -defm ATOMIC_RMW8_U_ADD_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw8.add_u", 0x20>; -defm ATOMIC_RMW16_U_ADD_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw16.add_u", 0x21>; -defm ATOMIC_RMW8_U_ADD_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw8.add_u", 0x22>; -defm ATOMIC_RMW16_U_ADD_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw16.add_u", 0x23>; -defm ATOMIC_RMW32_U_ADD_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw32.add_u", 0x24>; - -defm ATOMIC_RMW_SUB_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.sub", 0x25>; -defm ATOMIC_RMW_SUB_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.sub", 0x26>; -defm ATOMIC_RMW8_U_SUB_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw8.sub_u", 0x27>; -defm ATOMIC_RMW16_U_SUB_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw16.sub_u", 0x28>; -defm ATOMIC_RMW8_U_SUB_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw8.sub_u", 0x29>; -defm ATOMIC_RMW16_U_SUB_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw16.sub_u", 0x2a>; -defm ATOMIC_RMW32_U_SUB_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw32.sub_u", 0x2b>; - -defm ATOMIC_RMW_AND_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.and", 0x2c>; -defm ATOMIC_RMW_AND_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.and", 0x2d>; -defm ATOMIC_RMW8_U_AND_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw8.and_u", 0x2e>; -defm ATOMIC_RMW16_U_AND_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw16.and_u", 0x2f>; -defm ATOMIC_RMW8_U_AND_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw8.and_u", 0x30>; -defm ATOMIC_RMW16_U_AND_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw16.and_u", 0x31>; -defm ATOMIC_RMW32_U_AND_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw32.and_u", 0x32>; - -defm ATOMIC_RMW_OR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.or", 0x33>; -defm ATOMIC_RMW_OR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.or", 0x34>; -defm ATOMIC_RMW8_U_OR_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw8.or_u", 0x35>; -defm ATOMIC_RMW16_U_OR_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw16.or_u", 0x36>; -defm ATOMIC_RMW8_U_OR_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw8.or_u", 0x37>; -defm ATOMIC_RMW16_U_OR_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw16.or_u", 0x38>; -defm ATOMIC_RMW32_U_OR_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw32.or_u", 0x39>; - -defm ATOMIC_RMW_XOR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.xor", 0x3a>; -defm ATOMIC_RMW_XOR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.xor", 0x3b>; -defm ATOMIC_RMW8_U_XOR_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw8.xor_u", 0x3c>; -defm ATOMIC_RMW16_U_XOR_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw16.xor_u", 0x3d>; -defm ATOMIC_RMW8_U_XOR_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw8.xor_u", 0x3e>; -defm ATOMIC_RMW16_U_XOR_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw16.xor_u", 0x3f>; -defm ATOMIC_RMW32_U_XOR_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xor_u", 0x40>; - -defm ATOMIC_RMW_XCHG_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw.xchg", 0x41>; -defm ATOMIC_RMW_XCHG_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw.xchg", 0x42>; -defm ATOMIC_RMW8_U_XCHG_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw8.xchg_u", 0x43>; -defm ATOMIC_RMW16_U_XCHG_I32 : - WebAssemblyBinRMW<I32, "i32.atomic.rmw16.xchg_u", 0x44>; -defm ATOMIC_RMW8_U_XCHG_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw8.xchg_u", 0x45>; -defm ATOMIC_RMW16_U_XCHG_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw16.xchg_u", 0x46>; -defm ATOMIC_RMW32_U_XCHG_I64 : - WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xchg_u", 0x47>; - -// Select binary RMWs with no constant offset. -class BinRMWPatNoOffset<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind I32:$addr, ty:$val)), (inst 0, 0, I32:$addr, ty:$val)>; - -// Select binary RMWs with a constant offset. - -// Pattern with address + immediate offset -class BinRMWPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> : - Pat<(ty (kind (operand I32:$addr, imm:$off), ty:$val)), - (inst 0, imm:$off, I32:$addr, ty:$val)>; - -class BinRMWPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)), - ty:$val)), - (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>; - -// Select binary RMWs with just a constant offset. -class BinRMWPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind imm:$off, ty:$val)), - (inst 0, imm:$off, (CONST_I32 0), ty:$val)>; - -class BinRMWPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off), ty:$val)), - (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>; - -// Patterns for various addressing modes. -multiclass BinRMWPattern<PatFrag rmw_32, PatFrag rmw_64, NI inst_32, - NI inst_64> { - def : BinRMWPatNoOffset<i32, rmw_32, inst_32>; - def : BinRMWPatNoOffset<i64, rmw_64, inst_64>; - - def : BinRMWPatImmOff<i32, rmw_32, regPlusImm, inst_32>; - def : BinRMWPatImmOff<i64, rmw_64, regPlusImm, inst_64>; - def : BinRMWPatImmOff<i32, rmw_32, or_is_add, inst_32>; - def : BinRMWPatImmOff<i64, rmw_64, or_is_add, inst_64>; - - def : BinRMWPatGlobalAddr<i32, rmw_32, inst_32>; - def : BinRMWPatGlobalAddr<i64, rmw_64, inst_64>; - - def : BinRMWPatOffsetOnly<i32, rmw_32, inst_32>; - def : BinRMWPatOffsetOnly<i64, rmw_64, inst_64>; - - def : BinRMWPatGlobalAddrOffOnly<i32, rmw_32, inst_32>; - def : BinRMWPatGlobalAddrOffOnly<i64, rmw_64, inst_64>; -} - -let Predicates = [HasAtomics] in { -defm : BinRMWPattern<atomic_load_add_32, atomic_load_add_64, ATOMIC_RMW_ADD_I32, - ATOMIC_RMW_ADD_I64>; -defm : BinRMWPattern<atomic_load_sub_32, atomic_load_sub_64, ATOMIC_RMW_SUB_I32, - ATOMIC_RMW_SUB_I64>; -defm : BinRMWPattern<atomic_load_and_32, atomic_load_and_64, ATOMIC_RMW_AND_I32, - ATOMIC_RMW_AND_I64>; -defm : BinRMWPattern<atomic_load_or_32, atomic_load_or_64, ATOMIC_RMW_OR_I32, - ATOMIC_RMW_OR_I64>; -defm : BinRMWPattern<atomic_load_xor_32, atomic_load_xor_64, ATOMIC_RMW_XOR_I32, - ATOMIC_RMW_XOR_I64>; -defm : BinRMWPattern<atomic_swap_32, atomic_swap_64, ATOMIC_RMW_XCHG_I32, - ATOMIC_RMW_XCHG_I64>; -} // Predicates = [HasAtomics] - -// Truncating & zero-extending binary RMW patterns. -// These are combined patterns of truncating store patterns and zero-extending -// load patterns above. -class zext_bin_rmw_8_32<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), - (and (i32 (kind node:$addr, node:$val)), 255)>; -class zext_bin_rmw_16_32<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), - (and (i32 (kind node:$addr, node:$val)), 65535)>; -class zext_bin_rmw_8_64<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), - (and (i64 (anyext (i32 (kind node:$addr, - (i32 (trunc (i64 node:$val))))))), 255)>; -class zext_bin_rmw_16_64<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), - (and (i64 (anyext (i32 (kind node:$addr, - (i32 (trunc (i64 node:$val))))))), 65535)>; -class zext_bin_rmw_32_64<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), - (zext (i32 (kind node:$addr, (i32 (trunc (i64 node:$val))))))>; - -// Truncating & sign-extending binary RMW patterns. -// These are combined patterns of truncating store patterns and sign-extending -// load patterns above. 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_bin_rmw_8_32<PatFrag kind> : - PatFrag<(ops node:$addr, node:$val), (kind node:$addr, node:$val)>; -class sext_bin_rmw_16_32<PatFrag kind> : sext_bin_rmw_8_32<kind>; -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_i32_s - -// Patterns for various addressing modes for truncating-extending binary RMWs. -multiclass BinRMWTruncExtPattern< - 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 binary RMWs with no constant offset - def : BinRMWPatNoOffset<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatNoOffset<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatNoOffset<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatNoOffset<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>; - def : BinRMWPatNoOffset<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>; - - def : BinRMWPatNoOffset<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatNoOffset<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatNoOffset<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatNoOffset<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>; - - // Truncating-extending binary RMWs with a constant offset - def : BinRMWPatImmOff<i32, zext_bin_rmw_8_32<rmw_8>, regPlusImm, inst8_32>; - def : BinRMWPatImmOff<i32, zext_bin_rmw_16_32<rmw_16>, regPlusImm, inst16_32>; - def : BinRMWPatImmOff<i64, zext_bin_rmw_8_64<rmw_8>, regPlusImm, inst8_64>; - def : BinRMWPatImmOff<i64, zext_bin_rmw_16_64<rmw_16>, regPlusImm, inst16_64>; - def : BinRMWPatImmOff<i64, zext_bin_rmw_32_64<rmw_32>, regPlusImm, inst32_64>; - def : BinRMWPatImmOff<i32, zext_bin_rmw_8_32<rmw_8>, or_is_add, inst8_32>; - def : BinRMWPatImmOff<i32, zext_bin_rmw_16_32<rmw_16>, or_is_add, inst16_32>; - def : BinRMWPatImmOff<i64, zext_bin_rmw_8_64<rmw_8>, or_is_add, inst8_64>; - def : BinRMWPatImmOff<i64, zext_bin_rmw_16_64<rmw_16>, or_is_add, inst16_64>; - def : BinRMWPatImmOff<i64, zext_bin_rmw_32_64<rmw_32>, or_is_add, inst32_64>; - - def : BinRMWPatImmOff<i32, sext_bin_rmw_8_32<rmw_8>, regPlusImm, inst8_32>; - def : BinRMWPatImmOff<i32, sext_bin_rmw_16_32<rmw_16>, regPlusImm, inst16_32>; - def : BinRMWPatImmOff<i64, sext_bin_rmw_8_64<rmw_8>, regPlusImm, inst8_64>; - def : BinRMWPatImmOff<i64, sext_bin_rmw_16_64<rmw_16>, regPlusImm, inst16_64>; - def : BinRMWPatImmOff<i32, sext_bin_rmw_8_32<rmw_8>, or_is_add, inst8_32>; - def : BinRMWPatImmOff<i32, sext_bin_rmw_16_32<rmw_16>, or_is_add, inst16_32>; - def : BinRMWPatImmOff<i64, sext_bin_rmw_8_64<rmw_8>, or_is_add, inst8_64>; - def : BinRMWPatImmOff<i64, sext_bin_rmw_16_64<rmw_16>, or_is_add, inst16_64>; - - def : BinRMWPatGlobalAddr<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatGlobalAddr<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatGlobalAddr<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatGlobalAddr<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>; - def : BinRMWPatGlobalAddr<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>; - - def : BinRMWPatGlobalAddr<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatGlobalAddr<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatGlobalAddr<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatGlobalAddr<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>; - - // Truncating-extending binary RMWs with just a constant offset - def : BinRMWPatOffsetOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatOffsetOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatOffsetOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatOffsetOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>; - def : BinRMWPatOffsetOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>; - - def : BinRMWPatOffsetOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatOffsetOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatOffsetOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatOffsetOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>; - - def : BinRMWPatGlobalAddrOffOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatGlobalAddrOffOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>; - def : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>; - - def : BinRMWPatGlobalAddrOffOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>; - def : BinRMWPatGlobalAddrOffOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>; - def : BinRMWPatGlobalAddrOffOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>; - def : BinRMWPatGlobalAddrOffOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>; -} - -let Predicates = [HasAtomics] in { -defm : BinRMWTruncExtPattern< - atomic_load_add_8, atomic_load_add_16, atomic_load_add_32, atomic_load_add_64, - ATOMIC_RMW8_U_ADD_I32, ATOMIC_RMW16_U_ADD_I32, - ATOMIC_RMW8_U_ADD_I64, ATOMIC_RMW16_U_ADD_I64, ATOMIC_RMW32_U_ADD_I64>; -defm : BinRMWTruncExtPattern< - atomic_load_sub_8, atomic_load_sub_16, atomic_load_sub_32, atomic_load_sub_64, - ATOMIC_RMW8_U_SUB_I32, ATOMIC_RMW16_U_SUB_I32, - ATOMIC_RMW8_U_SUB_I64, ATOMIC_RMW16_U_SUB_I64, ATOMIC_RMW32_U_SUB_I64>; -defm : BinRMWTruncExtPattern< - atomic_load_and_8, atomic_load_and_16, atomic_load_and_32, atomic_load_and_64, - ATOMIC_RMW8_U_AND_I32, ATOMIC_RMW16_U_AND_I32, - ATOMIC_RMW8_U_AND_I64, ATOMIC_RMW16_U_AND_I64, ATOMIC_RMW32_U_AND_I64>; -defm : BinRMWTruncExtPattern< - atomic_load_or_8, atomic_load_or_16, atomic_load_or_32, atomic_load_or_64, - ATOMIC_RMW8_U_OR_I32, ATOMIC_RMW16_U_OR_I32, - ATOMIC_RMW8_U_OR_I64, ATOMIC_RMW16_U_OR_I64, ATOMIC_RMW32_U_OR_I64>; -defm : BinRMWTruncExtPattern< - atomic_load_xor_8, atomic_load_xor_16, atomic_load_xor_32, atomic_load_xor_64, - ATOMIC_RMW8_U_XOR_I32, ATOMIC_RMW16_U_XOR_I32, - ATOMIC_RMW8_U_XOR_I64, ATOMIC_RMW16_U_XOR_I64, ATOMIC_RMW32_U_XOR_I64>; -defm : BinRMWTruncExtPattern< - atomic_swap_8, atomic_swap_16, atomic_swap_32, atomic_swap_64, - 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 atomic_op> { - defm "" : - ATOMIC_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}"), atomic_op>; -} - -defm ATOMIC_RMW_CMPXCHG_I32 : - WebAssemblyTerRMW<I32, "i32.atomic.rmw.cmpxchg", 0x48>; -defm ATOMIC_RMW_CMPXCHG_I64 : - WebAssemblyTerRMW<I64, "i64.atomic.rmw.cmpxchg", 0x49>; -defm ATOMIC_RMW8_U_CMPXCHG_I32 : - WebAssemblyTerRMW<I32, "i32.atomic.rmw8.cmpxchg_u", 0x4a>; -defm ATOMIC_RMW16_U_CMPXCHG_I32 : - WebAssemblyTerRMW<I32, "i32.atomic.rmw16.cmpxchg_u", 0x4b>; -defm ATOMIC_RMW8_U_CMPXCHG_I64 : - WebAssemblyTerRMW<I64, "i64.atomic.rmw8.cmpxchg_u", 0x4c>; -defm ATOMIC_RMW16_U_CMPXCHG_I64 : - WebAssemblyTerRMW<I64, "i64.atomic.rmw16.cmpxchg_u", 0x4d>; -defm ATOMIC_RMW32_U_CMPXCHG_I64 : - WebAssemblyTerRMW<I64, "i64.atomic.rmw32.cmpxchg_u", 0x4e>; - -// 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)>; - -// 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)>; - -// 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 : 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>; -} - -let Predicates = [HasAtomics] in -defm : TerRMWPattern<atomic_cmp_swap_32, atomic_cmp_swap_64, - ATOMIC_RMW_CMPXCHG_I32, ATOMIC_RMW_CMPXCHG_I64>; - -// 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>; - - // 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>; -} - -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 fences -//===----------------------------------------------------------------------===// - -// A compiler fence instruction that prevents reordering of instructions. -let Defs = [ARGUMENTS] in { -let isPseudo = 1, hasSideEffects = 1 in -defm COMPILER_FENCE : ATOMIC_NRI<(outs), (ins), [], "compiler_fence">; -} // Defs = [ARGUMENTS] diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td deleted file mode 100644 index f4352e3d12ec..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td +++ /dev/null @@ -1,71 +0,0 @@ -// WebAssemblyInstrBulkMemory.td - bulk memory codegen support --*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly bulk memory codegen constructs. -/// -//===----------------------------------------------------------------------===// - -// Instruction requiring HasBulkMemory and the bulk memory prefix byte -multiclass BULK_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(0xfc00, !and(0xff, simdop))>, - Requires<[HasBulkMemory]>; -} - -// Bespoke types and nodes for bulk memory ops -def wasm_memcpy_t : SDTypeProfile<0, 5, - [SDTCisInt<0>, SDTCisInt<1>, SDTCisPtrTy<2>, SDTCisPtrTy<3>, SDTCisInt<4>] ->; -def wasm_memcpy : SDNode<"WebAssemblyISD::MEMORY_COPY", wasm_memcpy_t, - [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; - -def wasm_memset_t : SDTypeProfile<0, 4, - [SDTCisInt<0>, SDTCisPtrTy<1>, SDTCisInt<2>, SDTCisInt<3>] ->; -def wasm_memset : SDNode<"WebAssemblyISD::MEMORY_FILL", wasm_memset_t, - [SDNPHasChain, SDNPMayStore]>; - -let mayStore = 1, hasSideEffects = 1 in -defm MEMORY_INIT : - BULK_I<(outs), - (ins i32imm_op:$seg, i32imm_op:$idx, I32:$dest, - I32:$offset, I32:$size), - (outs), (ins i32imm_op:$seg, i32imm_op:$idx), - [(int_wasm_memory_init (i32 imm:$seg), (i32 imm:$idx), I32:$dest, - I32:$offset, I32:$size - )], - "memory.init\t$seg, $idx, $dest, $offset, $size", - "memory.init\t$seg, $idx", 0x08>; - -let hasSideEffects = 1 in -defm DATA_DROP : - BULK_I<(outs), (ins i32imm_op:$seg), (outs), (ins i32imm_op:$seg), - [(int_wasm_data_drop (i32 imm:$seg))], - "data.drop\t$seg", "data.drop\t$seg", 0x09>; - -let mayLoad = 1, mayStore = 1 in -defm MEMORY_COPY : - BULK_I<(outs), (ins i32imm_op:$src_idx, i32imm_op:$dst_idx, - I32:$dst, I32:$src, I32:$len), - (outs), (ins i32imm_op:$src_idx, i32imm_op:$dst_idx), - [(wasm_memcpy (i32 imm:$src_idx), (i32 imm:$dst_idx), - I32:$dst, I32:$src, I32:$len - )], - "memory.copy\t$src_idx, $dst_idx, $dst, $src, $len", - "memory.copy\t$src_idx, $dst_idx", 0x0a>; - -let mayStore = 1 in -defm MEMORY_FILL : - BULK_I<(outs), (ins i32imm_op:$idx, I32:$dst, I32:$value, I32:$size), - (outs), (ins i32imm_op:$idx), - [(wasm_memset (i32 imm:$idx), I32:$dst, I32:$value, I32:$size)], - "memory.fill\t$idx, $dst, $value, $size", - "memory.fill\t$idx", 0x0b>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td deleted file mode 100644 index 703c15d58c93..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ /dev/null @@ -1,177 +0,0 @@ -//===- WebAssemblyInstrCall.td-WebAssembly Call codegen support -*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly Call operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -// TODO: addr64: These currently assume the callee address is 32-bit. -// FIXME: add $type to first call_indirect asmstr (and maybe $flags) - -// 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 { -defm ADJCALLSTACKDOWN : NRI<(outs), (ins i32imm:$amt, i32imm:$amt2), - [(WebAssemblycallseq_start timm:$amt, timm:$amt2)]>; -defm ADJCALLSTACKUP : NRI<(outs), (ins i32imm:$amt, i32imm:$amt2), - [(WebAssemblycallseq_end timm:$amt, timm:$amt2)]>; -} // Uses = [SP32, SP64], Defs = [SP32, SP64], isCodeGenOnly = 1 - -multiclass CALL<ValueType vt, WebAssemblyRegClass rt, string prefix, - list<Predicate> preds = []> { - defm CALL_#vt : - I<(outs rt:$dst), (ins function32_op:$callee, variable_ops), - (outs), (ins function32_op:$callee), - [(set (vt rt:$dst), (WebAssemblycall1 (i32 imm:$callee)))], - !strconcat(prefix, "call\t$dst, $callee"), - !strconcat(prefix, "call\t$callee"), - 0x10>, - Requires<preds>; - - let isCodeGenOnly = 1 in - defm PCALL_INDIRECT_#vt : - I<(outs rt:$dst), (ins I32:$callee, variable_ops), - (outs), (ins I32:$callee), - [(set (vt rt:$dst), (WebAssemblycall1 I32:$callee))], - "PSEUDO CALL INDIRECT\t$callee", - "PSEUDO CALL INDIRECT\t$callee">, - Requires<preds>; - - defm CALL_INDIRECT_#vt : - I<(outs rt:$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<preds>; -} - -let Uses = [SP32, SP64], isCall = 1 in { -defm "" : CALL<i32, I32, "i32.">; -defm "" : CALL<i64, I64, "i64.">; -defm "" : CALL<f32, F32, "f32.">; -defm "" : CALL<f64, F64, "f64.">; -defm "" : CALL<exnref, EXNREF, "exnref.", [HasExceptionHandling]>; -defm "" : CALL<v16i8, V128, "v128.", [HasSIMD128]>; -defm "" : CALL<v8i16, V128, "v128.", [HasSIMD128]>; -defm "" : CALL<v4i32, V128, "v128.", [HasSIMD128]>; -defm "" : CALL<v2i64, V128, "v128.", [HasSIMD128]>; -defm "" : CALL<v4f32, V128, "v128.", [HasSIMD128]>; -defm "" : CALL<v2f64, V128, "v128.", [HasSIMD128]>; - -let IsCanonical = 1 in { -defm CALL_VOID : - I<(outs), (ins function32_op:$callee, variable_ops), - (outs), (ins function32_op:$callee), - [(WebAssemblycall0 (i32 imm:$callee))], - "call \t$callee", "call\t$callee", 0x10>; - -let isReturn = 1 in -defm RET_CALL : - I<(outs), (ins function32_op:$callee, variable_ops), - (outs), (ins function32_op:$callee), - [(WebAssemblyretcall (i32 imm:$callee))], - "return_call \t$callee", "return_call\t$callee", 0x12>, - Requires<[HasTailCall]>; - -let isCodeGenOnly = 1 in -defm PCALL_INDIRECT_VOID : - I<(outs), (ins I32:$callee, variable_ops), - (outs), (ins I32:$callee), - [(WebAssemblycall0 I32:$callee)], - "PSEUDO CALL INDIRECT\t$callee", - "PSEUDO CALL INDIRECT\t$callee">; - -defm CALL_INDIRECT_VOID : - I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops), - (outs), (ins TypeIndex:$type, i32imm:$flags), - [], - "call_indirect\t", "call_indirect\t$type", - 0x11>; - -let isReturn = 1 in -defm RET_CALL_INDIRECT : - I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops), - (outs), (ins TypeIndex:$type, i32imm:$flags), - [], - "return_call_indirect\t", "return_call_indirect\t$type", - 0x13>, - Requires<[HasTailCall]>; - -let isCodeGenOnly = 1, isReturn = 1 in -defm PRET_CALL_INDIRECT: - I<(outs), (ins I32:$callee, variable_ops), - (outs), (ins I32:$callee), - [(WebAssemblyretcall I32:$callee)], - "PSEUDO RET_CALL INDIRECT\t$callee", - "PSEUDO RET_CALL INDIRECT\t$callee">, - Requires<[HasTailCall]>; - -} // IsCanonical = 1 -} // Uses = [SP32,SP64], isCall = 1 - -// Patterns for matching a direct call to a global address. -def : Pat<(i32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), - (CALL_i32 tglobaladdr:$callee)>; -def : Pat<(i64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), - (CALL_i64 tglobaladdr:$callee)>; -def : Pat<(f32 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), - (CALL_f32 tglobaladdr:$callee)>; -def : Pat<(f64 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), - (CALL_f64 tglobaladdr:$callee)>; -def : Pat<(v16i8 (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), - (CALL_v16i8 tglobaladdr:$callee)>, Requires<[HasSIMD128]>; -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<(exnref (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), - (CALL_exnref tglobaladdr:$callee)>, - Requires<[HasExceptionHandling]>; -def : Pat<(WebAssemblycall0 (WebAssemblywrapper tglobaladdr:$callee)), - (CALL_VOID tglobaladdr:$callee)>; -def : Pat<(WebAssemblyretcall (WebAssemblywrapper tglobaladdr:$callee)), - (RET_CALL tglobaladdr:$callee)>, Requires<[HasTailCall]>; - -// Patterns for matching a direct call to an external symbol. -def : Pat<(i32 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), - (CALL_i32 texternalsym:$callee)>; -def : Pat<(i64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), - (CALL_i64 texternalsym:$callee)>; -def : Pat<(f32 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), - (CALL_f32 texternalsym:$callee)>; -def : Pat<(f64 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), - (CALL_f64 texternalsym:$callee)>; -def : Pat<(v16i8 (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), - (CALL_v16i8 texternalsym:$callee)>, Requires<[HasSIMD128]>; -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<(exnref (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), - (CALL_exnref texternalsym:$callee)>, - Requires<[HasExceptionHandling]>; -def : Pat<(WebAssemblycall0 (WebAssemblywrapper texternalsym:$callee)), - (CALL_VOID texternalsym:$callee)>; -def : Pat<(WebAssemblyretcall (WebAssemblywrapper texternalsym:$callee)), - (RET_CALL texternalsym:$callee)>, Requires<[HasTailCall]>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td deleted file mode 100644 index 1870c5bc34b0..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ /dev/null @@ -1,191 +0,0 @@ -//===- WebAssemblyInstrControl.td-WebAssembly control-flow ------*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly control-flow code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -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), - (outs), (ins bb_op:$dst), - [(brcond I32:$cond, bb:$dst)], - "br_if \t$dst, $cond", "br_if \t$dst", 0x0d>; -let isCodeGenOnly = 1 in -defm BR_UNLESS : I<(outs), (ins bb_op:$dst, I32:$cond), - (outs), (ins bb_op:$dst), []>; -let isBarrier = 1 in -defm BR : NRI<(outs), (ins bb_op:$dst), - [(br bb:$dst)], - "br \t$dst", 0x0c>; -} // isBranch = 1, isTerminator = 1, hasCtrlDep = 1 - -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)>; - -// 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", OperandType = "OPERAND_BRLIST" in -def brlist : Operand<i32> { - let ParserMatchClass = BrListAsmOperand; - let PrintMethod = "printBrList"; -} - -// 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. -let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { -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 -// IP. -defm NOP : NRI<(outs), (ins), [], "nop", 0x01>; - -// Placemarkers to indicate the start or end of a block or loop scope. -// 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 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, 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>; -// Generic instruction, for disassembler. -let IsCanonical = 1 in -defm END : NRI<(outs), (ins), [], "end", 0x0b>; -let isTerminator = 1, isBarrier = 1 in -defm END_FUNCTION : NRI<(outs), (ins), [], "end_function", 0x0b>; -} // Uses = [VALUE_STACK], Defs = [VALUE_STACK] - -multiclass RETURN<WebAssemblyRegClass vt> { - defm RETURN_#vt : I<(outs), (ins vt:$val), (outs), (ins), - [(WebAssemblyreturn vt:$val)], - "return \t$val", "return", 0x0f>; - // 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 : I<(outs), (ins vt:$val), (outs), (ins), []>; -} - -multiclass SIMD_RETURN<ValueType vt> { - 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 : I<(outs), (ins V128:$val), (outs), (ins), - []>, - Requires<[HasSIMD128]>; -} - -let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { - -let isReturn = 1 in { - defm "": RETURN<I32>; - defm "": RETURN<I64>; - defm "": RETURN<F32>; - defm "": RETURN<F64>; - defm "": RETURN<EXNREF>; - 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>; - - // This is to RETURN_VOID what FALLTHROUGH_RETURN_#vt is to RETURN_#vt. - let isCodeGenOnly = 1 in - defm FALLTHROUGH_RETURN_VOID : NRI<(outs), (ins), []>; -} // isReturn = 1 - -defm UNREACHABLE : NRI<(outs), (ins), [(trap)], "unreachable", 0x00>; -} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 - -//===----------------------------------------------------------------------===// -// Exception handling instructions -//===----------------------------------------------------------------------===// - -let Predicates = [HasExceptionHandling] in { - -// Throwing an exception: throw / rethrow -let isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { -defm THROW : I<(outs), (ins event_op:$tag, variable_ops), - (outs), (ins event_op:$tag), - [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag))], - "throw \t$tag", "throw \t$tag", 0x08>; -defm RETHROW : I<(outs), (ins EXNREF:$exn), (outs), (ins), [], - "rethrow \t$exn", "rethrow", 0x09>; -// Pseudo instruction to be the lowering target of int_wasm_rethrow_in_catch -// intrinsic. Will be converted to the real rethrow instruction later. -let isPseudo = 1 in -defm RETHROW_IN_CATCH : NRI<(outs), (ins), [(int_wasm_rethrow_in_catch)], - "rethrow_in_catch", 0>; -} // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 - -// Region within which an exception is caught: try / end_try -let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in { -defm TRY : NRI<(outs), (ins Signature:$sig), [], "try \t$sig", 0x06>; -defm END_TRY : NRI<(outs), (ins), [], "end_try", 0x0b>; -} // Uses = [VALUE_STACK], Defs = [VALUE_STACK] - -// Catching an exception: catch / extract_exception -let hasCtrlDep = 1, hasSideEffects = 1 in -defm CATCH : I<(outs EXNREF:$dst), (ins), (outs), (ins), [], - "catch \t$dst", "catch", 0x07>; - -// Querying / extracing exception: br_on_exn -// br_on_exn queries an exnref to see if it matches the corresponding exception -// tag index. If true it branches to the given label and pushes the -// corresponding argument values of the exception onto the stack. -let isBranch = 1, isTerminator = 1, hasCtrlDep = 1 in -defm BR_ON_EXN : I<(outs), (ins bb_op:$dst, event_op:$tag, EXNREF:$exn), - (outs), (ins bb_op:$dst, event_op:$tag), [], - "br_on_exn \t$dst, $tag, $exn", "br_on_exn \t$dst, $tag", - 0x0a>; -// This is a pseudo instruction that simulates popping a value from stack, which -// has been pushed by br_on_exn -let isCodeGenOnly = 1, hasSideEffects = 1 in -defm EXTRACT_EXCEPTION_I32 : NRI<(outs I32:$dst), (ins), - [(set I32:$dst, (int_wasm_extract_exception))], - "extract_exception\t$dst">; - -// Pseudo instructions: cleanupret / catchret -let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, - isPseudo = 1, isEHScopeReturn = 1 in { - defm CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "cleanupret", 0>; - defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from), - [(catchret bb:$dst, bb:$from)], "catchret", 0>; -} // isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1, - // isPseudo = 1, isEHScopeReturn = 1 -} // Predicates = [HasExceptionHandling] diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrConv.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrConv.td deleted file mode 100644 index 661fee2715ba..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrConv.td +++ /dev/null @@ -1,231 +0,0 @@ -//===-- WebAssemblyInstrConv.td-WebAssembly Conversion support -*- tablegen -*-= -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly datatype conversions, truncations, reinterpretations, -/// promotions, and demotions operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -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>; - -defm I64_EXTEND_S_I32 : I<(outs I64:$dst), (ins I32:$src), (outs), (ins), - [(set I64:$dst, (sext I32:$src))], - "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_i32_u\t$dst, $src", "i64.extend_i32_u", - 0xad>; - -let Predicates = [HasSignExt] in { -defm I32_EXTEND8_S_I32 : I<(outs I32:$dst), (ins I32:$src), (outs), (ins), - [(set I32:$dst, (sext_inreg I32:$src, i8))], - "i32.extend8_s\t$dst, $src", "i32.extend8_s", - 0xc0>; -defm I32_EXTEND16_S_I32 : I<(outs I32:$dst), (ins I32:$src), (outs), (ins), - [(set I32:$dst, (sext_inreg I32:$src, i16))], - "i32.extend16_s\t$dst, $src", "i32.extend16_s", - 0xc1>; -defm I64_EXTEND8_S_I64 : I<(outs I64:$dst), (ins I64:$src), (outs), (ins), - [(set I64:$dst, (sext_inreg I64:$src, i8))], - "i64.extend8_s\t$dst, $src", "i64.extend8_s", - 0xc2>; -defm I64_EXTEND16_S_I64 : I<(outs I64:$dst), (ins I64:$src), (outs), (ins), - [(set I64:$dst, (sext_inreg I64:$src, i16))], - "i64.extend16_s\t$dst, $src", "i64.extend16_s", - 0xc3>; -defm I64_EXTEND32_S_I64 : I<(outs I64:$dst), (ins I64:$src), (outs), (ins), - [(set I64:$dst, (sext_inreg I64:$src, i32))], - "i64.extend32_s\t$dst, $src", "i64.extend32_s", - 0xc4>; -} // Predicates = [HasSignExt] - -// 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)>; - -// 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_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_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_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_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_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_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_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_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 { -defm FP_TO_SINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins), - [(set I32:$dst, (fp_to_sint F32:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_UINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src), (outs), (ins), - [(set I32:$dst, (fp_to_uint F32:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_SINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src), (outs), (ins), - [(set I64:$dst, (fp_to_sint F32:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_UINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src), (outs), (ins), - [(set I64:$dst, (fp_to_uint F32:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_SINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src), (outs), (ins), - [(set I32:$dst, (fp_to_sint F64:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_UINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src), (outs), (ins), - [(set I32:$dst, (fp_to_uint F64:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_SINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins), - [(set I64:$dst, (fp_to_sint F64:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -defm FP_TO_UINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins), - [(set I64:$dst, (fp_to_uint F64:$src))], "", "", 0>, - Requires<[NotHasNontrappingFPToInt]>; -} // usesCustomInserter, isCodeGenOnly = 1 - -// 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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", - 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", - 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>; -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>; -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>; -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>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td deleted file mode 100644 index 5c9b34f44734..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td +++ /dev/null @@ -1,130 +0,0 @@ -// WebAssemblyInstrFloat.td-WebAssembly Float codegen support ---*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly Floating-point operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -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>; -defm SUB : BinaryFP<fsub, "sub ", 0x93, 0xa1>; -let isCommutable = 1 in -defm MUL : BinaryFP<fmul, "mul ", 0x94, 0xa2>; -defm DIV : BinaryFP<fdiv, "div ", 0x95, 0xa3>; -defm SQRT : UnaryFP<fsqrt, "sqrt", 0x91, 0x9f>; - -defm ABS : UnaryFP<fabs, "abs ", 0x8b, 0x99>; -defm NEG : UnaryFP<fneg, "neg ", 0x8c, 0x9a>; -defm COPYSIGN : BinaryFP<fcopysign, "copysign", 0x98, 0xa6>; - -let isCommutable = 1 in { -defm MIN : BinaryFP<fminimum, "min ", 0x96, 0xa4>; -defm MAX : BinaryFP<fmaximum, "max ", 0x97, 0xa5>; -} // isCommutable = 1 - -defm CEIL : UnaryFP<fceil, "ceil", 0x8d, 0x9b>; -defm FLOOR : UnaryFP<ffloor, "floor", 0x8e, 0x9c>; -defm TRUNC : UnaryFP<ftrunc, "trunc", 0x8f, 0x9d>; -defm NEAREST : UnaryFP<fnearbyint, "nearest", 0x90, 0x9e>; - -// 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))>; -def : Pat<(fcopysign F32:$lhs, F64:$rhs), - (COPYSIGN_F32 F32:$lhs, (F32_DEMOTE_F64 F64:$rhs))>; - -// WebAssembly doesn't expose inexact exceptions, so map frint to fnearbyint. -def : Pat<(frint f32:$src), (NEAREST_F32 f32:$src)>; -def : Pat<(frint f64:$src), (NEAREST_F64 f64:$src)>; - -let isCommutable = 1 in { -defm EQ : ComparisonFP<SETOEQ, "eq ", 0x5b, 0x61>; -defm NE : ComparisonFP<SETUNE, "ne ", 0x5c, 0x62>; -} // isCommutable = 1 -defm LT : ComparisonFP<SETOLT, "lt ", 0x5d, 0x63>; -defm LE : ComparisonFP<SETOLE, "le ", 0x5f, 0x65>; -defm GT : ComparisonFP<SETOGT, "gt ", 0x5e, 0x64>; -defm GE : ComparisonFP<SETOGE, "ge ", 0x60, 0x66>; - -// 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)>; -def : Pat<(setlt f32:$lhs, f32:$rhs), (LT_F32 f32:$lhs, f32:$rhs)>; -def : Pat<(setle f32:$lhs, f32:$rhs), (LE_F32 f32:$lhs, f32:$rhs)>; -def : Pat<(setgt f32:$lhs, f32:$rhs), (GT_F32 f32:$lhs, f32:$rhs)>; -def : Pat<(setge f32:$lhs, f32:$rhs), (GE_F32 f32:$lhs, f32:$rhs)>; -def : Pat<(seteq f64:$lhs, f64:$rhs), (EQ_F64 f64:$lhs, f64:$rhs)>; -def : Pat<(setne f64:$lhs, f64:$rhs), (NE_F64 f64:$lhs, f64:$rhs)>; -def : Pat<(setlt f64:$lhs, f64:$rhs), (LT_F64 f64:$lhs, f64:$rhs)>; -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)>; - -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))], - "f32.select\t$dst, $lhs, $rhs, $cond", "f32.select", 0x1b>; -defm SELECT_F64 : I<(outs F64:$dst), (ins F64:$lhs, F64:$rhs, I32:$cond), - (outs), (ins), - [(set F64:$dst, (select I32:$cond, F64:$lhs, F64:$rhs))], - "f64.select\t$dst, $lhs, $rhs, $cond", "f64.select", 0x1b>; - -// 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. -def : Pat<(select (i32 (setne I32:$cond, 0)), F32:$lhs, F32:$rhs), - (SELECT_F32 F32:$lhs, F32:$rhs, I32:$cond)>; -def : Pat<(select (i32 (setne I32:$cond, 0)), F64:$lhs, F64:$rhs), - (SELECT_F64 F64:$lhs, F64:$rhs, I32:$cond)>; - -// And again, this time with seteq instead of setne and the arms reversed. -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/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td deleted file mode 100644 index aff4d20d8d82..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td +++ /dev/null @@ -1,66 +0,0 @@ -//=- WebAssemblyInstrFormats.td - WebAssembly Instr. Formats -*- tablegen -*-=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly instruction format definitions. -/// -//===----------------------------------------------------------------------===// - -// WebAssembly Instruction Format. -// We instantiate 2 of these for every actual instruction (register based -// and stack based), see below. -class WebAssemblyInst<bits<32> inst, string asmstr, string stack> : StackRel, - Instruction { - bits<32> Inst = inst; // Instruction encoding. - string StackBased = stack; - string BaseName = NAME; - let Namespace = "WebAssembly"; - let Pattern = []; - let AsmString = asmstr; - // When there are multiple instructions that map to the same encoding (in - // e.g. the disassembler use case) prefer the one where IsCanonical == 1. - bit IsCanonical = 0; -} - -// Normal instructions. Default instantiation of a WebAssemblyInst. -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. -// We have 2 sets of operands (oops & iops) for the register and stack -// 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 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 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 -// there is always an equivalent pair of instructions. -multiclass I<dag oops_r, dag iops_r, dag oops_s, dag iops_s, - list<dag> pattern_r, string asmstr_r = "", string asmstr_s = "", - bits<32> inst = -1> { - 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. -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>; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp deleted file mode 100644 index a86c9af28f0d..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ /dev/null @@ -1,232 +0,0 @@ -//===-- WebAssemblyInstrInfo.cpp - WebAssembly Instruction Information ----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the WebAssembly implementation of the -/// TargetInstrInfo class. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyInstrInfo.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineMemOperand.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-instr-info" - -#define GET_INSTRINFO_CTOR_DTOR -#include "WebAssemblyGenInstrInfo.inc" - -// defines WebAssembly::getNamedOperandIdx -#define GET_INSTRINFO_NAMED_OPS -#include "WebAssemblyGenInstrInfo.inc" - -WebAssemblyInstrInfo::WebAssemblyInstrInfo(const WebAssemblySubtarget &STI) - : WebAssemblyGenInstrInfo(WebAssembly::ADJCALLSTACKDOWN, - WebAssembly::ADJCALLSTACKUP, - WebAssembly::CATCHRET), - RI(STI.getTargetTriple()) {} - -bool WebAssemblyInstrInfo::isReallyTriviallyReMaterializable( - const MachineInstr &MI, AliasAnalysis *AA) const { - switch (MI.getOpcode()) { - case WebAssembly::CONST_I32: - case WebAssembly::CONST_I64: - case WebAssembly::CONST_F32: - case WebAssembly::CONST_F64: - // isReallyTriviallyReMaterializableGeneric misses these because of the - // ARGUMENTS implicit def, so we manualy override it here. - return true; - default: - return false; - } -} - -void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB, - MachineBasicBlock::iterator I, - const DebugLoc &DL, unsigned DestReg, - unsigned SrcReg, bool KillSrc) const { - // This method is called by post-RA expansion, which expects only pregs to - // exist. However we need to handle both here. - auto &MRI = MBB.getParent()->getRegInfo(); - const TargetRegisterClass *RC = - TargetRegisterInfo::isVirtualRegister(DestReg) - ? MRI.getRegClass(DestReg) - : MRI.getTargetRegisterInfo()->getMinimalPhysRegClass(DestReg); - - unsigned CopyOpcode; - if (RC == &WebAssembly::I32RegClass) - CopyOpcode = WebAssembly::COPY_I32; - else if (RC == &WebAssembly::I64RegClass) - CopyOpcode = WebAssembly::COPY_I64; - else if (RC == &WebAssembly::F32RegClass) - CopyOpcode = WebAssembly::COPY_F32; - else if (RC == &WebAssembly::F64RegClass) - CopyOpcode = WebAssembly::COPY_F64; - else if (RC == &WebAssembly::V128RegClass) - CopyOpcode = WebAssembly::COPY_V128; - else if (RC == &WebAssembly::EXNREFRegClass) - CopyOpcode = WebAssembly::COPY_EXNREF; - else - llvm_unreachable("Unexpected register class"); - - BuildMI(MBB, I, DL, get(CopyOpcode), DestReg) - .addReg(SrcReg, KillSrc ? RegState::Kill : 0); -} - -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>(); - if (MFI.isVRegStackified(MI.getOperand(OpIdx1).getReg()) || - MFI.isVRegStackified(MI.getOperand(OpIdx2).getReg())) - return nullptr; - - // Otherwise use the default implementation. - return TargetInstrInfo::commuteInstructionImpl(MI, NewMI, OpIdx1, OpIdx2); -} - -// Branch analysis. -bool WebAssemblyInstrInfo::analyzeBranch(MachineBasicBlock &MBB, - MachineBasicBlock *&TBB, - MachineBasicBlock *&FBB, - SmallVectorImpl<MachineOperand> &Cond, - bool /*AllowModify*/) const { - const auto &MFI = *MBB.getParent()->getInfo<WebAssemblyFunctionInfo>(); - // WebAssembly has control flow that doesn't have explicit branches or direct - // fallthrough (e.g. try/catch), which can't be modeled by analyzeBranch. It - // is created after CFGStackify. - if (MFI.isCFGStackified()) - return true; - - bool HaveCond = false; - for (MachineInstr &MI : MBB.terminators()) { - switch (MI.getOpcode()) { - default: - // Unhandled instruction; bail out. - return true; - case WebAssembly::BR_IF: - if (HaveCond) - return true; - Cond.push_back(MachineOperand::CreateImm(true)); - Cond.push_back(MI.getOperand(1)); - TBB = MI.getOperand(0).getMBB(); - HaveCond = true; - break; - case WebAssembly::BR_UNLESS: - if (HaveCond) - return true; - Cond.push_back(MachineOperand::CreateImm(false)); - Cond.push_back(MI.getOperand(1)); - TBB = MI.getOperand(0).getMBB(); - HaveCond = true; - break; - case WebAssembly::BR: - if (!HaveCond) - TBB = MI.getOperand(0).getMBB(); - else - FBB = MI.getOperand(0).getMBB(); - break; - case WebAssembly::BR_ON_EXN: - if (HaveCond) - return true; - Cond.push_back(MachineOperand::CreateImm(true)); - Cond.push_back(MI.getOperand(2)); - TBB = MI.getOperand(0).getMBB(); - HaveCond = true; - break; - } - if (MI.isBarrier()) - break; - } - - return false; -} - -unsigned WebAssemblyInstrInfo::removeBranch(MachineBasicBlock &MBB, - int *BytesRemoved) const { - assert(!BytesRemoved && "code size not handled"); - - MachineBasicBlock::instr_iterator I = MBB.instr_end(); - unsigned Count = 0; - - while (I != MBB.instr_begin()) { - --I; - if (I->isDebugInstr()) - continue; - if (!I->isTerminator()) - break; - // Remove the branch. - I->eraseFromParent(); - I = MBB.instr_end(); - ++Count; - } - - return Count; -} - -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()) { - if (!TBB) - return 0; - - BuildMI(&MBB, DL, get(WebAssembly::BR)).addMBB(TBB); - return 1; - } - - assert(Cond.size() == 2 && "Expected a flag and a successor block"); - - MachineFunction &MF = *MBB.getParent(); - auto &MRI = MF.getRegInfo(); - bool IsBrOnExn = Cond[1].isReg() && MRI.getRegClass(Cond[1].getReg()) == - &WebAssembly::EXNREFRegClass; - - if (Cond[0].getImm()) { - if (IsBrOnExn) { - const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception"); - BuildMI(&MBB, DL, get(WebAssembly::BR_ON_EXN)) - .addMBB(TBB) - .addExternalSymbol(CPPExnSymbol) - .add(Cond[1]); - } else - BuildMI(&MBB, DL, get(WebAssembly::BR_IF)).addMBB(TBB).add(Cond[1]); - } else { - assert(!IsBrOnExn && "br_on_exn does not have a reversed condition"); - BuildMI(&MBB, DL, get(WebAssembly::BR_UNLESS)).addMBB(TBB).add(Cond[1]); - } - if (!FBB) - return 1; - - BuildMI(&MBB, DL, get(WebAssembly::BR)).addMBB(FBB); - return 2; -} - -bool WebAssemblyInstrInfo::reverseBranchCondition( - SmallVectorImpl<MachineOperand> &Cond) const { - assert(Cond.size() == 2 && "Expected a flag and a condition expression"); - - // br_on_exn's condition cannot be reversed - MachineFunction &MF = *Cond[1].getParent()->getParent()->getParent(); - auto &MRI = MF.getRegInfo(); - if (Cond[1].isReg() && - MRI.getRegClass(Cond[1].getReg()) == &WebAssembly::EXNREFRegClass) - return true; - - Cond.front() = MachineOperand::CreateImm(!Cond.front().getImm()); - return false; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h deleted file mode 100644 index df1051b4f42c..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h +++ /dev/null @@ -1,71 +0,0 @@ -//=- WebAssemblyInstrInfo.h - WebAssembly Instruction Information -*- C++ -*-=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the WebAssembly implementation of the -/// TargetInstrInfo class. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYINSTRINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYINSTRINFO_H - -#include "WebAssemblyRegisterInfo.h" -#include "llvm/CodeGen/TargetInstrInfo.h" - -#define GET_INSTRINFO_HEADER -#include "WebAssemblyGenInstrInfo.inc" - -#define GET_INSTRINFO_OPERAND_ENUM -#include "WebAssemblyGenInstrInfo.inc" - -namespace llvm { - -namespace WebAssembly { - -int16_t getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIndex); - -} - -class WebAssemblySubtarget; - -class WebAssemblyInstrInfo final : public WebAssemblyGenInstrInfo { - const WebAssemblyRegisterInfo RI; - -public: - explicit WebAssemblyInstrInfo(const WebAssemblySubtarget &STI); - - const WebAssemblyRegisterInfo &getRegisterInfo() const { return RI; } - - bool isReallyTriviallyReMaterializable(const MachineInstr &MI, - AliasAnalysis *AA) const override; - - void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, - const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, - bool KillSrc) const override; - MachineInstr *commuteInstructionImpl(MachineInstr &MI, bool NewMI, - unsigned OpIdx1, - unsigned OpIdx2) const override; - - bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, - MachineBasicBlock *&FBB, - SmallVectorImpl<MachineOperand> &Cond, - bool AllowModify = false) const override; - unsigned removeBranch(MachineBasicBlock &MBB, - int *BytesRemoved = nullptr) const override; - unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, - MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond, - const DebugLoc &DL, - int *BytesAdded = nullptr) const override; - bool - reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td deleted file mode 100644 index 73ddbe85d551..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ /dev/null @@ -1,349 +0,0 @@ -// WebAssemblyInstrInfo.td-Describe the WebAssembly Instructions-*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly Instruction definitions. -/// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// WebAssembly Instruction Predicate Definitions. -//===----------------------------------------------------------------------===// - -def IsPIC : Predicate<"TM.isPositionIndependent()">; -def IsNotPIC : Predicate<"!TM.isPositionIndependent()">; - -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 HasMultivalue : - Predicate<"Subtarget->hasMultivalue()">, - AssemblerPredicate<"FeatureMultivalue", "multivalue">; - -def HasNontrappingFPToInt : - Predicate<"Subtarget->hasNontrappingFPToInt()">, - AssemblerPredicate<"FeatureNontrappingFPToInt", "nontrapping-fptoint">; - -def NotHasNontrappingFPToInt : - Predicate<"!Subtarget->hasNontrappingFPToInt()">, - AssemblerPredicate<"!FeatureNontrappingFPToInt", "nontrapping-fptoint">; - -def HasSignExt : - Predicate<"Subtarget->hasSignExt()">, - AssemblerPredicate<"FeatureSignExt", "sign-ext">; - -def HasTailCall : - Predicate<"Subtarget->hasTailCall()">, - AssemblerPredicate<"FeatureTailCall", "tail-call">; - -def HasExceptionHandling : - Predicate<"Subtarget->hasExceptionHandling()">, - AssemblerPredicate<"FeatureExceptionHandling", "exception-handling">; - -def HasBulkMemory : - Predicate<"Subtarget->hasBulkMemory()">, - AssemblerPredicate<"FeatureBulkMemory", "bulk-memory">; - -//===----------------------------------------------------------------------===// -// WebAssembly-specific DAG Node Types. -//===----------------------------------------------------------------------===// - -def SDT_WebAssemblyCallSeqStart : SDCallSeqStart<[SDTCisVT<0, iPTR>, - SDTCisVT<1, iPTR>]>; -def SDT_WebAssemblyCallSeqEnd : - SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>; -def SDT_WebAssemblyCall0 : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; -def SDT_WebAssemblyCall1 : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>; -def SDT_WebAssemblyBrTable : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; -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_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, - SDTCisPtrTy<0>]>; -def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; - -//===----------------------------------------------------------------------===// -// WebAssembly-specific DAG Nodes. -//===----------------------------------------------------------------------===// - -def WebAssemblycallseq_start : - SDNode<"ISD::CALLSEQ_START", SDT_WebAssemblyCallSeqStart, - [SDNPHasChain, SDNPOutGlue]>; -def WebAssemblycallseq_end : - SDNode<"ISD::CALLSEQ_END", SDT_WebAssemblyCallSeqEnd, - [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; -def WebAssemblycall0 : SDNode<"WebAssemblyISD::CALL0", - SDT_WebAssemblyCall0, - [SDNPHasChain, SDNPVariadic]>; -def WebAssemblycall1 : SDNode<"WebAssemblyISD::CALL1", - SDT_WebAssemblyCall1, - [SDNPHasChain, SDNPVariadic]>; -def WebAssemblyretcall : SDNode<"WebAssemblyISD::RET_CALL", - SDT_WebAssemblyCall0, - [SDNPHasChain, SDNPVariadic]>; -def WebAssemblybr_table : SDNode<"WebAssemblyISD::BR_TABLE", - SDT_WebAssemblyBrTable, - [SDNPHasChain, SDNPVariadic]>; -def WebAssemblyargument : SDNode<"WebAssemblyISD::ARGUMENT", - SDT_WebAssemblyArgument>; -def WebAssemblyreturn : SDNode<"WebAssemblyISD::RETURN", - SDT_WebAssemblyReturn, [SDNPHasChain]>; -def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper", - SDT_WebAssemblyWrapper>; -def WebAssemblywrapperPIC : SDNode<"WebAssemblyISD::WrapperPIC", - SDT_WebAssemblyWrapperPIC>; -def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow, - [SDNPHasChain, SDNPVariadic]>; - -//===----------------------------------------------------------------------===// -// WebAssembly-specific Operands. -//===----------------------------------------------------------------------===// - -// Default Operand has AsmOperandClass "Imm" which is for integers (and -// symbols), so specialize one for floats: -def FPImmAsmOperand : AsmOperandClass { - let Name = "FPImm"; - let PredicateMethod = "isFPImm"; -} - -class FPOperand<ValueType ty> : Operand<ty> { - AsmOperandClass ParserMatchClass = FPImmAsmOperand; -} - -let OperandNamespace = "WebAssembly" in { - -let OperandType = "OPERAND_BASIC_BLOCK" in -def bb_op : Operand<OtherVT>; - -let OperandType = "OPERAND_LOCAL" in -def local_op : Operand<i32>; - -let OperandType = "OPERAND_GLOBAL" in -def global_op : Operand<i32>; - -let OperandType = "OPERAND_I32IMM" in -def i32imm_op : Operand<i32>; - -let OperandType = "OPERAND_I64IMM" in -def i64imm_op : Operand<i64>; - -let OperandType = "OPERAND_F32IMM" in -def f32imm_op : FPOperand<f32>; - -let OperandType = "OPERAND_F64IMM" in -def f64imm_op : FPOperand<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>; - -let OperandType = "OPERAND_OFFSET32" in -def offset32_op : Operand<i32>; - -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 -def Signature : Operand<i32> { - let PrintMethod = "printWebAssemblySignatureOperand"; -} - -let OperandType = "OPERAND_TYPEINDEX" in -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. -//===----------------------------------------------------------------------===// - -include "WebAssemblyInstrFormats.td" - -//===----------------------------------------------------------------------===// -// Additional instructions. -//===----------------------------------------------------------------------===// - -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))]>; -} -defm "": ARGUMENT<I32, i32>; -defm "": ARGUMENT<I64, i64>; -defm "": ARGUMENT<F32, f32>; -defm "": ARGUMENT<F64, f64>; -defm "": ARGUMENT<EXNREF, exnref>; - -// 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 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), [], - "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), [], - "local.tee\t$res, $also, $src", "local.tee">; - - // 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 LOCAL_GET_#vt : I<(outs vt:$res), (ins local_op:$local), - (outs), (ins local_op:$local), [], - "local.get\t$res, $local", "local.get\t$local", 0x20>; - - // 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 LOCAL_SET_#vt : I<(outs), (ins local_op:$local, vt:$src), - (outs), (ins local_op:$local), [], - "local.set\t$local, $src", "local.set\t$local", 0x21>; - - // 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 LOCAL_TEE_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src), - (outs), (ins local_op:$local), [], - "local.tee\t$res, $local, $src", "local.tee\t$local", - 0x22>; - - // Unused values must be dropped in some contexts. - defm DROP_#vt : I<(outs), (ins vt:$src), (outs), (ins), [], - "drop\t$src", "drop", 0x1a>; - - let mayLoad = 1 in - defm GLOBAL_GET_#vt : I<(outs vt:$res), (ins global_op:$local), - (outs), (ins global_op:$local), [], - "global.get\t$res, $local", "global.get\t$local", - 0x23>; - - let mayStore = 1 in - defm GLOBAL_SET_#vt : I<(outs), (ins global_op:$local, vt:$src), - (outs), (ins global_op:$local), [], - "global.set\t$local, $src", "global.set\t$local", - 0x24>; - -} // hasSideEffects = 0 -} -defm "" : LOCAL<I32>; -defm "" : LOCAL<I64>; -defm "" : LOCAL<F32>; -defm "" : LOCAL<F64>; -defm "" : LOCAL<V128>, Requires<[HasSIMD128]>; -defm "" : LOCAL<EXNREF>, Requires<[HasExceptionHandling]>; - -let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in { -defm CONST_I32 : I<(outs I32:$res), (ins i32imm_op:$imm), - (outs), (ins i32imm_op:$imm), - [(set I32:$res, imm:$imm)], - "i32.const\t$res, $imm", "i32.const\t$imm", 0x41>; -defm CONST_I64 : I<(outs I64:$res), (ins i64imm_op:$imm), - (outs), (ins i64imm_op:$imm), - [(set I64:$res, imm:$imm)], - "i64.const\t$res, $imm", "i64.const\t$imm", 0x42>; -defm CONST_F32 : I<(outs F32:$res), (ins f32imm_op:$imm), - (outs), (ins f32imm_op:$imm), - [(set F32:$res, fpimm:$imm)], - "f32.const\t$res, $imm", "f32.const\t$imm", 0x43>; -defm CONST_F64 : I<(outs F64:$res), (ins f64imm_op:$imm), - (outs), (ins f64imm_op:$imm), - [(set F64:$res, fpimm:$imm)], - "f64.const\t$res, $imm", "f64.const\t$imm", 0x44>; -} // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 - -def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)), - (CONST_I32 tglobaladdr:$addr)>, Requires<[IsNotPIC]>; - -def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)), - (GLOBAL_GET_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>; - -def : Pat<(i32 (WebAssemblywrapperPIC tglobaladdr:$addr)), - (CONST_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>; - -def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), - (GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC]>; - -def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), - (CONST_I32 texternalsym:$addr)>, Requires<[IsNotPIC]>; - -def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>; -def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>; - -//===----------------------------------------------------------------------===// -// Additional sets of instructions. -//===----------------------------------------------------------------------===// - -include "WebAssemblyInstrMemory.td" -include "WebAssemblyInstrCall.td" -include "WebAssemblyInstrControl.td" -include "WebAssemblyInstrInteger.td" -include "WebAssemblyInstrConv.td" -include "WebAssemblyInstrFloat.td" -include "WebAssemblyInstrAtomics.td" -include "WebAssemblyInstrSIMD.td" -include "WebAssemblyInstrRef.td" -include "WebAssemblyInstrBulkMemory.td" diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInteger.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInteger.td deleted file mode 100644 index 18250cf8ef85..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrInteger.td +++ /dev/null @@ -1,123 +0,0 @@ -// WebAssemblyInstrInteger.td-WebAssembly Integer codegen -------*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly Integer operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -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. -let isCommutable = 1 in -defm ADD : BinaryInt<add, "add ", 0x6a, 0x7c>; -defm SUB : BinaryInt<sub, "sub ", 0x6b, 0x7d>; -let isCommutable = 1 in -defm MUL : BinaryInt<mul, "mul ", 0x6c, 0x7e>; -// Divide and remainder trap on a zero denominator. -let hasSideEffects = 1 in { -defm DIV_S : BinaryInt<sdiv, "div_s", 0x6d, 0x7f>; -defm DIV_U : BinaryInt<udiv, "div_u", 0x6e, 0x80>; -defm REM_S : BinaryInt<srem, "rem_s", 0x6f, 0x81>; -defm REM_U : BinaryInt<urem, "rem_u", 0x70, 0x82>; -} // hasSideEffects = 1 -let isCommutable = 1 in { -defm AND : BinaryInt<and, "and ", 0x71, 0x83>; -defm OR : BinaryInt<or, "or ", 0x72, 0x84>; -defm XOR : BinaryInt<xor, "xor ", 0x73, 0x85>; -} // isCommutable = 1 -defm SHL : BinaryInt<shl, "shl ", 0x74, 0x86>; -defm SHR_S : BinaryInt<sra, "shr_s", 0x75, 0x87>; -defm SHR_U : BinaryInt<srl, "shr_u", 0x76, 0x88>; -defm ROTL : BinaryInt<rotl, "rotl", 0x77, 0x89>; -defm ROTR : BinaryInt<rotr, "rotr", 0x78, 0x8a>; - -let isCommutable = 1 in { -defm EQ : ComparisonInt<SETEQ, "eq ", 0x46, 0x51>; -defm NE : ComparisonInt<SETNE, "ne ", 0x47, 0x52>; -} // isCommutable = 1 -defm LT_S : ComparisonInt<SETLT, "lt_s", 0x48, 0x53>; -defm LT_U : ComparisonInt<SETULT, "lt_u", 0x49, 0x54>; -defm GT_S : ComparisonInt<SETGT, "gt_s", 0x4a, 0x55>; -defm GT_U : ComparisonInt<SETUGT, "gt_u", 0x4b, 0x56>; -defm LE_S : ComparisonInt<SETLE, "le_s", 0x4c, 0x57>; -defm LE_U : ComparisonInt<SETULE, "le_u", 0x4d, 0x58>; -defm GE_S : ComparisonInt<SETGE, "ge_s", 0x4e, 0x59>; -defm GE_U : ComparisonInt<SETUGE, "ge_u", 0x4f, 0x5a>; - -defm CLZ : UnaryInt<ctlz, "clz ", 0x67, 0x79>; -defm CTZ : UnaryInt<cttz, "ctz ", 0x68, 0x7a>; -defm POPCNT : UnaryInt<ctpop, "popcnt", 0x69, 0x7b>; - -defm EQZ_I32 : I<(outs I32:$dst), (ins I32:$src), (outs), (ins), - [(set I32:$dst, (setcc I32:$src, 0, SETEQ))], - "i32.eqz \t$dst, $src", "i32.eqz", 0x45>; -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>; - -// 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)>; - -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))], - "i32.select\t$dst, $lhs, $rhs, $cond", "i32.select", 0x1b>; -defm SELECT_I64 : I<(outs I64:$dst), (ins I64:$lhs, I64:$rhs, I32:$cond), - (outs), (ins), - [(set I64:$dst, (select I32:$cond, I64:$lhs, I64:$rhs))], - "i64.select\t$dst, $lhs, $rhs, $cond", "i64.select", 0x1b>; - -// 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. -def : Pat<(select (i32 (setne I32:$cond, 0)), I32:$lhs, I32:$rhs), - (SELECT_I32 I32:$lhs, I32:$rhs, I32:$cond)>; -def : Pat<(select (i32 (setne I32:$cond, 0)), I64:$lhs, I64:$rhs), - (SELECT_I64 I64:$lhs, I64:$rhs, I32:$cond)>; - -// And again, this time with seteq instead of setne and the arms reversed. -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)>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td deleted file mode 100644 index 6916b165f970..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ /dev/null @@ -1,371 +0,0 @@ -// WebAssemblyInstrMemory.td-WebAssembly Memory codegen support -*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly Memory operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -// TODO: -// - HasAddr64 -// - WebAssemblyTargetLowering having to do with atomics -// - Each has optional alignment. - -// WebAssembly has i8/i16/i32/i64/f32/f64 memory types, but doesn't have i8/i16 -// local types. These memory-only types instead zero- or sign-extend into local -// types when loading, and truncate when storing. - -// WebAssembly constant offsets are performed as unsigned with infinite -// precision, so we need to check for NoUnsignedWrap so that we don't fold an -// offset for an add that needs wrapping. -def regPlusImm : PatFrag<(ops node:$addr, node:$off), - (add node:$addr, node:$off), - [{ return N->getFlags().hasNoUnsignedWrap(); }]>; - -// Treat an 'or' node as an 'add' if the or'ed bits are known to be zero. -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), 0); - KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0); - return (~Known0.Zero & ~Known1.Zero) == 0; -}]>; - -// GlobalAddresses are conceptually unsigned values, so we can also fold them -// into immediate values as long as the add is 'nuw'. -// TODO: We'd like to also match GA offsets but there are cases where the -// register can have a negative value. Find out what more we can do. -def regPlusGA : PatFrag<(ops node:$addr, node:$off), - (add node:$addr, node:$off), - [{ - return N->getFlags().hasNoUnsignedWrap(); -}]>; - -// We don't need a regPlusES because external symbols never have constant -// offsets folded into them, so we can just use add. - -// Defines atomic and non-atomic loads, regular and extending. -multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode> { - let mayLoad = 1, UseNamedOperandTable = 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>; -} - -// Basic load. -// FIXME: When we can break syntax compatibility, reorder the fields in the -// asmstrings to match the binary encoding. -defm LOAD_I32 : WebAssemblyLoad<I32, "i32.load", 0x28>; -defm LOAD_I64 : WebAssemblyLoad<I64, "i64.load", 0x29>; -defm LOAD_F32 : WebAssemblyLoad<F32, "f32.load", 0x2a>; -defm LOAD_F64 : WebAssemblyLoad<F64, "f64.load", 0x2b>; - -// Select loads with no constant offset. -class LoadPatNoOffset<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind I32:$addr)), (inst 0, 0, I32:$addr)>; - -def : LoadPatNoOffset<i32, load, LOAD_I32>; -def : LoadPatNoOffset<i64, load, LOAD_I64>; -def : LoadPatNoOffset<f32, load, LOAD_F32>; -def : LoadPatNoOffset<f64, load, LOAD_F64>; - - -// Select loads with a constant offset. - -// Pattern with address + immediate offset -class LoadPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> : - Pat<(ty (kind (operand I32:$addr, imm:$off))), (inst 0, imm:$off, I32:$addr)>; - -def : LoadPatImmOff<i32, load, regPlusImm, LOAD_I32>; -def : LoadPatImmOff<i64, load, regPlusImm, LOAD_I64>; -def : LoadPatImmOff<f32, load, regPlusImm, LOAD_F32>; -def : LoadPatImmOff<f64, load, regPlusImm, LOAD_F64>; -def : LoadPatImmOff<i32, load, or_is_add, LOAD_I32>; -def : LoadPatImmOff<i64, load, or_is_add, LOAD_I64>; -def : LoadPatImmOff<f32, load, or_is_add, LOAD_F32>; -def : LoadPatImmOff<f64, load, or_is_add, LOAD_F64>; - -class LoadPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)))), - (inst 0, tglobaladdr:$off, I32:$addr)>, Requires<[IsNotPIC]>; - -def : LoadPatGlobalAddr<i32, load, LOAD_I32>; -def : LoadPatGlobalAddr<i64, load, LOAD_I64>; -def : LoadPatGlobalAddr<f32, load, LOAD_F32>; -def : LoadPatGlobalAddr<f64, load, LOAD_F64>; - -// Select loads with just a constant offset. -class LoadPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind imm:$off)), (inst 0, imm:$off, (CONST_I32 0))>; - -def : LoadPatOffsetOnly<i32, load, LOAD_I32>; -def : LoadPatOffsetOnly<i64, load, LOAD_I64>; -def : LoadPatOffsetOnly<f32, load, LOAD_F32>; -def : LoadPatOffsetOnly<f64, load, LOAD_F64>; - -class LoadPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))), - (inst 0, tglobaladdr:$off, (CONST_I32 0))>, Requires<[IsNotPIC]>; - -def : LoadPatGlobalAddrOffOnly<i32, load, LOAD_I32>; -def : LoadPatGlobalAddrOffOnly<i64, load, LOAD_I64>; -def : LoadPatGlobalAddrOffOnly<f32, load, LOAD_F32>; -def : LoadPatGlobalAddrOffOnly<f64, load, LOAD_F64>; - -// Extending load. -defm LOAD8_S_I32 : WebAssemblyLoad<I32, "i32.load8_s", 0x2c>; -defm LOAD8_U_I32 : WebAssemblyLoad<I32, "i32.load8_u", 0x2d>; -defm LOAD16_S_I32 : WebAssemblyLoad<I32, "i32.load16_s", 0x2e>; -defm LOAD16_U_I32 : WebAssemblyLoad<I32, "i32.load16_u", 0x2f>; -defm LOAD8_S_I64 : WebAssemblyLoad<I64, "i64.load8_s", 0x30>; -defm LOAD8_U_I64 : WebAssemblyLoad<I64, "i64.load8_u", 0x31>; -defm LOAD16_S_I64 : WebAssemblyLoad<I64, "i64.load16_s", 0x32>; -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>; - -// Select extending loads with no constant offset. -def : LoadPatNoOffset<i32, sextloadi8, LOAD8_S_I32>; -def : LoadPatNoOffset<i32, zextloadi8, LOAD8_U_I32>; -def : LoadPatNoOffset<i32, sextloadi16, LOAD16_S_I32>; -def : LoadPatNoOffset<i32, zextloadi16, LOAD16_U_I32>; -def : LoadPatNoOffset<i64, sextloadi8, LOAD8_S_I64>; -def : LoadPatNoOffset<i64, zextloadi8, LOAD8_U_I64>; -def : LoadPatNoOffset<i64, sextloadi16, LOAD16_S_I64>; -def : LoadPatNoOffset<i64, zextloadi16, LOAD16_U_I64>; -def : LoadPatNoOffset<i64, sextloadi32, LOAD32_S_I64>; -def : LoadPatNoOffset<i64, zextloadi32, LOAD32_U_I64>; - -// Select extending loads with a constant offset. -def : LoadPatImmOff<i32, sextloadi8, regPlusImm, LOAD8_S_I32>; -def : LoadPatImmOff<i32, zextloadi8, regPlusImm, LOAD8_U_I32>; -def : LoadPatImmOff<i32, sextloadi16, regPlusImm, LOAD16_S_I32>; -def : LoadPatImmOff<i32, zextloadi16, regPlusImm, LOAD16_U_I32>; -def : LoadPatImmOff<i64, sextloadi8, regPlusImm, LOAD8_S_I64>; -def : LoadPatImmOff<i64, zextloadi8, regPlusImm, LOAD8_U_I64>; -def : LoadPatImmOff<i64, sextloadi16, regPlusImm, LOAD16_S_I64>; -def : LoadPatImmOff<i64, zextloadi16, regPlusImm, LOAD16_U_I64>; -def : LoadPatImmOff<i64, sextloadi32, regPlusImm, LOAD32_S_I64>; -def : LoadPatImmOff<i64, zextloadi32, regPlusImm, LOAD32_U_I64>; - -def : LoadPatImmOff<i32, sextloadi8, or_is_add, LOAD8_S_I32>; -def : LoadPatImmOff<i32, zextloadi8, or_is_add, LOAD8_U_I32>; -def : LoadPatImmOff<i32, sextloadi16, or_is_add, LOAD16_S_I32>; -def : LoadPatImmOff<i32, zextloadi16, or_is_add, LOAD16_U_I32>; -def : LoadPatImmOff<i64, sextloadi8, or_is_add, LOAD8_S_I64>; -def : LoadPatImmOff<i64, zextloadi8, or_is_add, LOAD8_U_I64>; -def : LoadPatImmOff<i64, sextloadi16, or_is_add, LOAD16_S_I64>; -def : LoadPatImmOff<i64, zextloadi16, or_is_add, LOAD16_U_I64>; -def : LoadPatImmOff<i64, sextloadi32, or_is_add, LOAD32_S_I64>; -def : LoadPatImmOff<i64, zextloadi32, or_is_add, LOAD32_U_I64>; - -def : LoadPatGlobalAddr<i32, sextloadi8, LOAD8_S_I32>; -def : LoadPatGlobalAddr<i32, zextloadi8, LOAD8_U_I32>; -def : LoadPatGlobalAddr<i32, sextloadi16, LOAD16_S_I32>; -def : LoadPatGlobalAddr<i32, zextloadi8, LOAD16_U_I32>; - -def : LoadPatGlobalAddr<i64, sextloadi8, LOAD8_S_I64>; -def : LoadPatGlobalAddr<i64, zextloadi8, LOAD8_U_I64>; -def : LoadPatGlobalAddr<i64, sextloadi16, LOAD16_S_I64>; -def : LoadPatGlobalAddr<i64, zextloadi16, LOAD16_U_I64>; -def : LoadPatGlobalAddr<i64, sextloadi32, LOAD32_S_I64>; -def : LoadPatGlobalAddr<i64, zextloadi32, LOAD32_U_I64>; - -// Select extending loads with just a constant offset. -def : LoadPatOffsetOnly<i32, sextloadi8, LOAD8_S_I32>; -def : LoadPatOffsetOnly<i32, zextloadi8, LOAD8_U_I32>; -def : LoadPatOffsetOnly<i32, sextloadi16, LOAD16_S_I32>; -def : LoadPatOffsetOnly<i32, zextloadi16, LOAD16_U_I32>; - -def : LoadPatOffsetOnly<i64, sextloadi8, LOAD8_S_I64>; -def : LoadPatOffsetOnly<i64, zextloadi8, LOAD8_U_I64>; -def : LoadPatOffsetOnly<i64, sextloadi16, LOAD16_S_I64>; -def : LoadPatOffsetOnly<i64, zextloadi16, LOAD16_U_I64>; -def : LoadPatOffsetOnly<i64, sextloadi32, LOAD32_S_I64>; -def : LoadPatOffsetOnly<i64, zextloadi32, LOAD32_U_I64>; - -def : LoadPatGlobalAddrOffOnly<i32, sextloadi8, LOAD8_S_I32>; -def : LoadPatGlobalAddrOffOnly<i32, zextloadi8, LOAD8_U_I32>; -def : LoadPatGlobalAddrOffOnly<i32, sextloadi16, LOAD16_S_I32>; -def : LoadPatGlobalAddrOffOnly<i32, zextloadi16, LOAD16_U_I32>; -def : LoadPatGlobalAddrOffOnly<i64, sextloadi8, LOAD8_S_I64>; -def : LoadPatGlobalAddrOffOnly<i64, zextloadi8, LOAD8_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, sextloadi16, LOAD16_S_I64>; -def : LoadPatGlobalAddrOffOnly<i64, zextloadi16, LOAD16_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, sextloadi32, LOAD32_S_I64>; -def : LoadPatGlobalAddrOffOnly<i64, zextloadi32, LOAD32_U_I64>; - -// Resolve "don't care" extending loads to zero-extending loads. This is -// somewhat arbitrary, but zero-extending is conceptually simpler. - -// Select "don't care" extending loads with no constant offset. -def : LoadPatNoOffset<i32, extloadi8, LOAD8_U_I32>; -def : LoadPatNoOffset<i32, extloadi16, LOAD16_U_I32>; -def : LoadPatNoOffset<i64, extloadi8, LOAD8_U_I64>; -def : LoadPatNoOffset<i64, extloadi16, LOAD16_U_I64>; -def : LoadPatNoOffset<i64, extloadi32, LOAD32_U_I64>; - -// Select "don't care" extending loads with a constant offset. -def : LoadPatImmOff<i32, extloadi8, regPlusImm, LOAD8_U_I32>; -def : LoadPatImmOff<i32, extloadi16, regPlusImm, LOAD16_U_I32>; -def : LoadPatImmOff<i64, extloadi8, regPlusImm, LOAD8_U_I64>; -def : LoadPatImmOff<i64, extloadi16, regPlusImm, LOAD16_U_I64>; -def : LoadPatImmOff<i64, extloadi32, regPlusImm, LOAD32_U_I64>; -def : LoadPatImmOff<i32, extloadi8, or_is_add, LOAD8_U_I32>; -def : LoadPatImmOff<i32, extloadi16, or_is_add, LOAD16_U_I32>; -def : LoadPatImmOff<i64, extloadi8, or_is_add, LOAD8_U_I64>; -def : LoadPatImmOff<i64, extloadi16, or_is_add, LOAD16_U_I64>; -def : LoadPatImmOff<i64, extloadi32, or_is_add, LOAD32_U_I64>; -def : LoadPatGlobalAddr<i32, extloadi8, LOAD8_U_I32>; -def : LoadPatGlobalAddr<i32, extloadi16, LOAD16_U_I32>; -def : LoadPatGlobalAddr<i64, extloadi8, LOAD8_U_I64>; -def : LoadPatGlobalAddr<i64, extloadi16, LOAD16_U_I64>; -def : LoadPatGlobalAddr<i64, extloadi32, LOAD32_U_I64>; - -// Select "don't care" extending loads with just a constant offset. -def : LoadPatOffsetOnly<i32, extloadi8, LOAD8_U_I32>; -def : LoadPatOffsetOnly<i32, extloadi16, LOAD16_U_I32>; -def : LoadPatOffsetOnly<i64, extloadi8, LOAD8_U_I64>; -def : LoadPatOffsetOnly<i64, extloadi16, LOAD16_U_I64>; -def : LoadPatOffsetOnly<i64, extloadi32, LOAD32_U_I64>; -def : LoadPatGlobalAddrOffOnly<i32, extloadi8, LOAD8_U_I32>; -def : LoadPatGlobalAddrOffOnly<i32, extloadi16, LOAD16_U_I32>; -def : LoadPatGlobalAddrOffOnly<i64, extloadi8, LOAD8_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, extloadi16, LOAD16_U_I64>; -def : LoadPatGlobalAddrOffOnly<i64, extloadi32, LOAD32_U_I64>; - -// Defines atomic and non-atomic stores, regular and truncating -multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode> { - let mayStore = 1, UseNamedOperandTable = 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>; -} -// Basic store. -// Note: WebAssembly inverts SelectionDAG's usual operand order. -defm STORE_I32 : WebAssemblyStore<I32, "i32.store", 0x36>; -defm STORE_I64 : WebAssemblyStore<I64, "i64.store", 0x37>; -defm STORE_F32 : WebAssemblyStore<F32, "f32.store", 0x38>; -defm STORE_F64 : WebAssemblyStore<F64, "f64.store", 0x39>; - -// 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)>; - -def : StorePatNoOffset<i32, store, STORE_I32>; -def : StorePatNoOffset<i64, store, STORE_I64>; -def : StorePatNoOffset<f32, store, STORE_F32>; -def : StorePatNoOffset<f64, store, STORE_F64>; - -// Select stores with a constant offset. -class StorePatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> : - Pat<(kind ty:$val, (operand I32:$addr, imm:$off)), - (inst 0, imm:$off, I32:$addr, ty:$val)>; - -def : StorePatImmOff<i32, store, regPlusImm, STORE_I32>; -def : StorePatImmOff<i64, store, regPlusImm, STORE_I64>; -def : StorePatImmOff<f32, store, regPlusImm, STORE_F32>; -def : StorePatImmOff<f64, store, regPlusImm, STORE_F64>; -def : StorePatImmOff<i32, store, or_is_add, STORE_I32>; -def : StorePatImmOff<i64, store, or_is_add, STORE_I64>; -def : StorePatImmOff<f32, store, or_is_add, STORE_F32>; -def : StorePatImmOff<f64, store, or_is_add, STORE_F64>; - -class StorePatGlobalAddr<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind ty:$val, - (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off))), - (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>, Requires<[IsNotPIC]>; -def : StorePatGlobalAddr<i32, store, STORE_I32>; -def : StorePatGlobalAddr<i64, store, STORE_I64>; -def : StorePatGlobalAddr<f32, store, STORE_F32>; -def : StorePatGlobalAddr<f64, store, STORE_F64>; - -// Select stores with just a constant offset. -class StorePatOffsetOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind ty:$val, imm:$off), (inst 0, imm:$off, (CONST_I32 0), ty:$val)>; -def : StorePatOffsetOnly<i32, store, STORE_I32>; -def : StorePatOffsetOnly<i64, store, STORE_I64>; -def : StorePatOffsetOnly<f32, store, STORE_F32>; -def : StorePatOffsetOnly<f64, store, STORE_F64>; - -class StorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> : - Pat<(kind ty:$val, (WebAssemblywrapper tglobaladdr:$off)), - (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>, Requires<[IsNotPIC]>; -def : StorePatGlobalAddrOffOnly<i32, store, STORE_I32>; -def : StorePatGlobalAddrOffOnly<i64, store, STORE_I64>; -def : StorePatGlobalAddrOffOnly<f32, store, STORE_F32>; -def : StorePatGlobalAddrOffOnly<f64, store, STORE_F64>; - -// Truncating store. -defm STORE8_I32 : WebAssemblyStore<I32, "i32.store8", 0x3a>; -defm STORE16_I32 : WebAssemblyStore<I32, "i32.store16", 0x3b>; -defm STORE8_I64 : WebAssemblyStore<I64, "i64.store8", 0x3c>; -defm STORE16_I64 : WebAssemblyStore<I64, "i64.store16", 0x3d>; -defm STORE32_I64 : WebAssemblyStore<I64, "i64.store32", 0x3e>; - -// Select truncating stores with no constant offset. -def : StorePatNoOffset<i32, truncstorei8, STORE8_I32>; -def : StorePatNoOffset<i32, truncstorei16, STORE16_I32>; -def : StorePatNoOffset<i64, truncstorei8, STORE8_I64>; -def : StorePatNoOffset<i64, truncstorei16, STORE16_I64>; -def : StorePatNoOffset<i64, truncstorei32, STORE32_I64>; - -// Select truncating stores with a constant offset. -def : StorePatImmOff<i32, truncstorei8, regPlusImm, STORE8_I32>; -def : StorePatImmOff<i32, truncstorei16, regPlusImm, STORE16_I32>; -def : StorePatImmOff<i64, truncstorei8, regPlusImm, STORE8_I64>; -def : StorePatImmOff<i64, truncstorei16, regPlusImm, STORE16_I64>; -def : StorePatImmOff<i64, truncstorei32, regPlusImm, STORE32_I64>; -def : StorePatImmOff<i32, truncstorei8, or_is_add, STORE8_I32>; -def : StorePatImmOff<i32, truncstorei16, or_is_add, STORE16_I32>; -def : StorePatImmOff<i64, truncstorei8, or_is_add, STORE8_I64>; -def : StorePatImmOff<i64, truncstorei16, or_is_add, STORE16_I64>; -def : StorePatImmOff<i64, truncstorei32, or_is_add, STORE32_I64>; - -def : StorePatGlobalAddr<i32, truncstorei8, STORE8_I32>; -def : StorePatGlobalAddr<i32, truncstorei16, STORE16_I32>; -def : StorePatGlobalAddr<i64, truncstorei8, STORE8_I64>; -def : StorePatGlobalAddr<i64, truncstorei16, STORE16_I64>; -def : StorePatGlobalAddr<i64, truncstorei32, STORE32_I64>; - -// Select truncating stores with just a constant offset. -def : StorePatOffsetOnly<i32, truncstorei8, STORE8_I32>; -def : StorePatOffsetOnly<i32, truncstorei16, STORE16_I32>; -def : StorePatOffsetOnly<i64, truncstorei8, STORE8_I64>; -def : StorePatOffsetOnly<i64, truncstorei16, STORE16_I64>; -def : StorePatOffsetOnly<i64, truncstorei32, STORE32_I64>; -def : StorePatGlobalAddrOffOnly<i32, truncstorei8, STORE8_I32>; -def : StorePatGlobalAddrOffOnly<i32, truncstorei16, STORE16_I32>; -def : StorePatGlobalAddrOffOnly<i64, truncstorei8, STORE8_I64>; -def : StorePatGlobalAddrOffOnly<i64, truncstorei16, STORE16_I64>; -def : StorePatGlobalAddrOffOnly<i64, truncstorei32, STORE32_I64>; - -// Current memory size. -defm MEMORY_SIZE_I32 : I<(outs I32:$dst), (ins i32imm:$flags), - (outs), (ins i32imm:$flags), - [(set I32:$dst, - (int_wasm_memory_size (i32 imm:$flags)))], - "memory.size\t$dst, $flags", "memory.size\t$flags", - 0x3f>, - Requires<[HasAddr32]>; - -// Grow memory. -defm MEMORY_GROW_I32 : I<(outs I32:$dst), (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", 0x40>, - Requires<[HasAddr32]>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td deleted file mode 100644 index afe89de60b36..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td +++ /dev/null @@ -1,25 +0,0 @@ -// WebAssemblyInstrRef.td - WebAssembly reference type codegen --*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly refence type operand codegen constructs. -/// -//===----------------------------------------------------------------------===// - -defm SELECT_EXNREF : I<(outs EXNREF:$dst), - (ins EXNREF:$lhs, EXNREF:$rhs, I32:$cond), - (outs), (ins), - [(set EXNREF:$dst, - (select I32:$cond, EXNREF:$lhs, EXNREF:$rhs))], - "exnref.select\t$dst, $lhs, $rhs, $cond", - "exnref.select", 0x1b>; - -def : Pat<(select (i32 (setne I32:$cond, 0)), EXNREF:$lhs, EXNREF:$rhs), - (SELECT_EXNREF EXNREF:$lhs, EXNREF:$rhs, I32:$cond)>; -def : Pat<(select (i32 (seteq I32:$cond, 0)), EXNREF:$lhs, EXNREF:$rhs), - (SELECT_EXNREF EXNREF:$rhs, EXNREF:$lhs, I32:$cond)>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td deleted file mode 100644 index dd8930f079b0..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ /dev/null @@ -1,734 +0,0 @@ -// WebAssemblyInstrSIMD.td - WebAssembly SIMD codegen support -*- tablegen -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// WebAssembly SIMD operand code-gen constructs. -/// -//===----------------------------------------------------------------------===// - -// 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 -(1 << ("#SIZE#" - 1)) <= Imm && Imm < (1 << ("#SIZE#" - 1));" ->; -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, UseNamedOperandTable = 1 in - defm LOAD_#vec_t : - SIMD_I<(outs V128:$dst), (ins P2Align:$p2align, offset32_op:$off, I32:$addr), - (outs), (ins P2Align:$p2align, offset32_op:$off), [], - "v128.load\t$dst, ${off}(${addr})$p2align", - "v128.load\t$off$p2align", 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 : LoadPatOffsetOnly<vec_t, load, !cast<NI>("LOAD_"#vec_t)>; -def : LoadPatGlobalAddrOffOnly<vec_t, load, !cast<NI>("LOAD_"#vec_t)>; -} - -// Store: v128.store -multiclass SIMDStore<ValueType vec_t> { - let mayStore = 1, UseNamedOperandTable = 1 in - defm STORE_#vec_t : - SIMD_I<(outs), (ins P2Align:$p2align, offset32_op:$off, I32:$addr, V128:$vec), - (outs), (ins P2Align:$p2align, offset32_op:$off), [], - "v128.store\t${off}(${addr})$p2align, $vec", - "v128.store\t$off$p2align", 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 : StorePatOffsetOnly<vec_t, store, !cast<NI>("STORE_"#vec_t)>; -def : StorePatGlobalAddrOffOnly<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">; -let IsCanonical = 1 in -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>; - -// scalar_to_vector leaves high lanes undefined, so can be a splat -class ScalarSplatPat<ValueType vec_t, ValueType lane_t, - WebAssemblyRegClass reg_t> : - Pat<(vec_t (scalar_to_vector (lane_t reg_t:$x))), - (!cast<Instruction>("SPLAT_"#vec_t) reg_t:$x)>; - -def : ScalarSplatPat<v16i8, i32, I32>; -def : ScalarSplatPat<v8i16, i32, I32>; -def : ScalarSplatPat<v4i32, i32, I32>; -def : ScalarSplatPat<v2i64, i64, I64>; -def : ScalarSplatPat<v4f32, f32, F32>; -def : ScalarSplatPat<v2f64, f64, F64>; - -//===----------------------------------------------------------------------===// -// 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)>; - -//===----------------------------------------------------------------------===// -// 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 NE : SIMDConditionInt<"ne", SETNE, 25>; -defm NE : SIMDConditionFP<"ne", SETUNE, 65>; -} // isCommutable = 1 - -// 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 with nnan and in the -// target-independent expansion of unordered comparisons and ordered ne. -foreach nodes = [[seteq, EQ_v4f32], [setne, NE_v4f32], [setlt, LT_v4f32], - [setgt, GT_v4f32], [setle, LE_v4f32], [setge, GE_v4f32]] in -def : Pat<(v4i32 (nodes[0] (v4f32 V128:$lhs), (v4f32 V128:$rhs))), - (v4i32 (nodes[1] (v4f32 V128:$lhs), (v4f32 V128:$rhs)))>; - -foreach nodes = [[seteq, EQ_v2f64], [setne, NE_v2f64], [setlt, LT_v2f64], - [setgt, GT_v2f64], [setle, LE_v2f64], [setge, GE_v2f64]] in -def : Pat<(v2i64 (nodes[0] (v2f64 V128:$lhs), (v2f64 V128:$rhs))), - (v2i64 (nodes[1] (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>; - -// Reductions already return 0 or 1, so and 1, setne 0, and seteq 1 -// can be folded out -foreach reduction = - [["int_wasm_anytrue", "ANYTRUE"], ["int_wasm_alltrue", "ALLTRUE"]] in -foreach ty = [v16i8, v8i16, v4i32, v2i64] in { -def : Pat<(i32 (and - (i32 (!cast<Intrinsic>(reduction[0]) (ty V128:$x))), - (i32 1) - )), - (i32 (!cast<NI>(reduction[1]#"_"#ty) (ty V128:$x)))>; -def : Pat<(i32 (setne - (i32 (!cast<Intrinsic>(reduction[0]) (ty V128:$x))), - (i32 0) - )), - (i32 (!cast<NI>(reduction[1]#"_"#ty) (ty V128:$x)))>; -def : Pat<(i32 (seteq - (i32 (!cast<Intrinsic>(reduction[0]) (ty V128:$x))), - (i32 1) - )), - (i32 (!cast<NI>(reduction[1]#"_"#ty) (ty V128:$x)))>; -} - -//===----------------------------------------------------------------------===// -// 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, except if they are already 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 (sext I32:$x)))) - )), - (v2i64 (shifts[1] (v2i64 V128:$vec), (i32 I32:$x)))>; -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/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp deleted file mode 100644 index e92b34430272..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp +++ /dev/null @@ -1,387 +0,0 @@ -//=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief Does various transformations for exception handling. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyUtilities.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/WasmEHFuncInfo.h" -#include "llvm/MC/MCAsmInfo.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-late-eh-prepare" - -namespace { -class WebAssemblyLateEHPrepare final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Late Prepare Exception"; - } - - bool runOnMachineFunction(MachineFunction &MF) override; - bool addCatches(MachineFunction &MF); - bool replaceFuncletReturns(MachineFunction &MF); - bool removeUnnecessaryUnreachables(MachineFunction &MF); - bool addExceptionExtraction(MachineFunction &MF); - bool restoreStackPointer(MachineFunction &MF); - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyLateEHPrepare::ID = 0; -INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE, - "WebAssembly Late Exception Preparation", false, false) - -FunctionPass *llvm::createWebAssemblyLateEHPrepare() { - return new WebAssemblyLateEHPrepare(); -} - -// Returns the nearest EH pad that dominates this instruction. This does not use -// dominator analysis; it just does BFS on its predecessors until arriving at an -// EH pad. This assumes valid EH scopes so the first EH pad it arrives in all -// 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. -static MachineBasicBlock *getMatchingEHPad(MachineInstr *MI) { - MachineFunction *MF = MI->getParent()->getParent(); - SmallVector<MachineBasicBlock *, 2> WL; - SmallPtrSet<MachineBasicBlock *, 2> Visited; - WL.push_back(MI->getParent()); - MachineBasicBlock *EHPad = nullptr; - while (!WL.empty()) { - MachineBasicBlock *MBB = WL.pop_back_val(); - if (Visited.count(MBB)) - continue; - Visited.insert(MBB); - if (MBB->isEHPad()) { - if (EHPad && EHPad != MBB) - return nullptr; - EHPad = MBB; - continue; - } - if (MBB == &MF->front()) - return nullptr; - WL.append(MBB->pred_begin(), MBB->pred_end()); - } - return EHPad; -} - -// 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(); - 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; - if (MF.getFunction().hasPersonalityFn()) { - Changed |= addCatches(MF); - Changed |= replaceFuncletReturns(MF); - } - Changed |= removeUnnecessaryUnreachables(MF); - if (MF.getFunction().hasPersonalityFn()) { - Changed |= addExceptionExtraction(MF); - Changed |= restoreStackPointer(MF); - } - return Changed; -} - -// Add catch instruction to beginning of catchpads and cleanuppads. -bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) { - bool Changed = false; - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - MachineRegisterInfo &MRI = MF.getRegInfo(); - for (auto &MBB : MF) { - if (MBB.isEHPad()) { - Changed = true; - auto InsertPos = MBB.begin(); - if (InsertPos->isEHLabel()) // EH pad starts with an EH label - ++InsertPos; - unsigned DstReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass); - BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(), - TII.get(WebAssembly::CATCH), DstReg); - } - } - return Changed; -} - -bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) { - bool Changed = false; - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - - for (auto &MBB : MF) { - auto Pos = MBB.getFirstTerminator(); - if (Pos == MBB.end()) - continue; - MachineInstr *TI = &*Pos; - - switch (TI->getOpcode()) { - case WebAssembly::CATCHRET: { - // Replace a catchret with a branch - MachineBasicBlock *TBB = TI->getOperand(0).getMBB(); - if (!MBB.isLayoutSuccessor(TBB)) - BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR)) - .addMBB(TBB); - TI->eraseFromParent(); - Changed = true; - break; - } - case WebAssembly::CLEANUPRET: - case WebAssembly::RETHROW_IN_CATCH: { - // Replace a cleanupret/rethrow_in_catch with a rethrow - auto *EHPad = getMatchingEHPad(TI); - auto CatchPos = EHPad->begin(); - if (CatchPos->isEHLabel()) // EH pad starts with an EH label - ++CatchPos; - MachineInstr *Catch = &*CatchPos; - unsigned ExnReg = Catch->getOperand(0).getReg(); - BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) - .addReg(ExnReg); - TI->eraseFromParent(); - Changed = true; - break; - } - } - } - return Changed; -} - -bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables( - MachineFunction &MF) { - bool Changed = false; - for (auto &MBB : MF) { - for (auto &MI : MBB) { - if (MI.getOpcode() != WebAssembly::THROW && - MI.getOpcode() != WebAssembly::RETHROW) - 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(MI.getIterator()), MBB.end()); - SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(), - MBB.succ_end()); - for (auto *Succ : Succs) - if (!Succ->isEHPad()) - MBB.removeSuccessor(Succ); - eraseDeadBBsAndChildren(Succs); - } - } - - return Changed; -} - -// Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes -// exnref type object returned by 'catch', and branches to the destination if it -// matches a given tag. We currently use __cpp_exception symbol to represent the -// tag for all C++ exceptions. -// -// block $l (result i32) -// ... -// ;; exnref $e is on the stack at this point -// br_on_exn $l $e ;; branch to $l with $e's arguments -// ... -// end -// ;; Here we expect the extracted values are on top of the wasm value stack -// ... Handle exception using values ... -// -// br_on_exn takes an exnref object and branches if it matches the given tag. -// There can be multiple br_on_exn instructions if we want to match for another -// tag, but for now we only test for __cpp_exception tag, and if it does not -// match, i.e., it is a foreign exception, we rethrow it. -// -// In the destination BB that's the target of br_on_exn, extracted exception -// values (in C++'s case a single i32, which represents an exception pointer) -// are placed on top of the wasm stack. Because we can't model wasm stack in -// LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve -// it. The pseudo instruction will be deleted later. -bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) { - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - auto *EHInfo = MF.getWasmEHFuncInfo(); - SmallVector<MachineInstr *, 16> ExtractInstrs; - SmallVector<MachineInstr *, 8> ToDelete; - for (auto &MBB : MF) { - for (auto &MI : MBB) { - if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) { - if (MI.getOperand(0).isDead()) - ToDelete.push_back(&MI); - else - ExtractInstrs.push_back(&MI); - } - } - } - bool Changed = !ToDelete.empty() || !ExtractInstrs.empty(); - for (auto *MI : ToDelete) - MI->eraseFromParent(); - if (ExtractInstrs.empty()) - return Changed; - - // Find terminate pads. - SmallSet<MachineBasicBlock *, 8> TerminatePads; - for (auto &MBB : MF) { - for (auto &MI : MBB) { - if (MI.isCall()) { - const MachineOperand &CalleeOp = MI.getOperand(0); - if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() == - WebAssembly::ClangCallTerminateFn) - TerminatePads.insert(getMatchingEHPad(&MI)); - } - } - } - - for (auto *Extract : ExtractInstrs) { - MachineBasicBlock *EHPad = getMatchingEHPad(Extract); - assert(EHPad && "No matching EH pad for extract_exception"); - auto CatchPos = EHPad->begin(); - if (CatchPos->isEHLabel()) // EH pad starts with an EH label - ++CatchPos; - MachineInstr *Catch = &*CatchPos; - - if (Catch->getNextNode() != Extract) - EHPad->insert(Catch->getNextNode(), Extract->removeFromParent()); - - // - Before: - // ehpad: - // %exnref:exnref = catch - // %exn:i32 = extract_exception - // ... use exn ... - // - // - After: - // ehpad: - // %exnref:exnref = catch - // br_on_exn %thenbb, $__cpp_exception, %exnref - // br %elsebb - // elsebb: - // rethrow - // thenbb: - // %exn:i32 = extract_exception - // ... use exn ... - unsigned ExnReg = Catch->getOperand(0).getReg(); - auto *ThenMBB = MF.CreateMachineBasicBlock(); - auto *ElseMBB = MF.CreateMachineBasicBlock(); - MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB); - MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB); - ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end()); - ThenMBB->transferSuccessors(EHPad); - EHPad->addSuccessor(ThenMBB); - EHPad->addSuccessor(ElseMBB); - - DebugLoc DL = Extract->getDebugLoc(); - const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception"); - BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN)) - .addMBB(ThenMBB) - .addExternalSymbol(CPPExnSymbol) - .addReg(ExnReg); - BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB); - - // When this is a terminate pad with __clang_call_terminate() call, we don't - // rethrow it anymore and call __clang_call_terminate() with a nullptr - // argument, which will call std::terminate(). - // - // - Before: - // ehpad: - // %exnref:exnref = catch - // %exn:i32 = extract_exception - // call @__clang_call_terminate(%exn) - // unreachable - // - // - After: - // ehpad: - // %exnref:exnref = catch - // br_on_exn %thenbb, $__cpp_exception, %exnref - // br %elsebb - // elsebb: - // call @__clang_call_terminate(0) - // unreachable - // thenbb: - // %exn:i32 = extract_exception - // call @__clang_call_terminate(%exn) - // unreachable - if (TerminatePads.count(EHPad)) { - Function *ClangCallTerminateFn = - MF.getFunction().getParent()->getFunction( - WebAssembly::ClangCallTerminateFn); - assert(ClangCallTerminateFn && - "There is no __clang_call_terminate() function"); - BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID)) - .addGlobalAddress(ClangCallTerminateFn) - .addImm(0); - BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE)); - - } else { - BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg); - if (EHInfo->hasEHPadUnwindDest(EHPad)) - ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad)); - } - } - - return true; -} - -// 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. -bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) { - 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. 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 (InsertPos->isEHLabel()) // EH pad starts with an EH label - ++InsertPos; - if (InsertPos->getOpcode() == WebAssembly::CATCH) - ++InsertPos; - FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos, - MBB.begin()->getDebugLoc()); - } - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp deleted file mode 100644 index 34a8195ac4b4..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp +++ /dev/null @@ -1,210 +0,0 @@ -//===-- WebAssemblyLowerBrUnless.cpp - Lower br_unless --------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file lowers br_unless into br_if with an inverted condition. -/// -/// br_unless is not currently in the spec, but it's very convenient for LLVM -/// to use. This pass allows LLVM to use it, for now. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-lower-br_unless" - -namespace { -class WebAssemblyLowerBrUnless final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Lower br_unless"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyLowerBrUnless() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyLowerBrUnless::ID = 0; -INITIALIZE_PASS(WebAssemblyLowerBrUnless, DEBUG_TYPE, - "Lowers br_unless into inverted br_if", false, false) - -FunctionPass *llvm::createWebAssemblyLowerBrUnless() { - return new WebAssemblyLowerBrUnless(); -} - -bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** Lowering br_unless **********\n" - "********** Function: " - << MF.getName() << '\n'); - - auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - auto &MRI = MF.getRegInfo(); - - for (auto &MBB : MF) { - for (auto MII = MBB.begin(); MII != MBB.end();) { - MachineInstr *MI = &*MII++; - if (MI->getOpcode() != WebAssembly::BR_UNLESS) - continue; - - unsigned Cond = MI->getOperand(1).getReg(); - bool Inverted = false; - - // Attempt to invert the condition in place. - if (MFI.isVRegStackified(Cond)) { - assert(MRI.hasOneDef(Cond)); - 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 EQZ_I32: { - // Invert an eqz by replacing it with its operand. - Cond = Def->getOperand(1).getReg(); - Def->eraseFromParent(); - Inverted = true; - break; - } - default: - break; - } - } - - // If we weren't able to invert the condition in place. Insert an - // instruction to invert it. - if (!Inverted) { - unsigned Tmp = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::EQZ_I32), Tmp) - .addReg(Cond); - MFI.stackifyVReg(Tmp); - Cond = Tmp; - Inverted = true; - } - - // The br_unless condition has now been inverted. Insert a br_if and - // delete the br_unless. - assert(Inverted); - BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::BR_IF)) - .add(MI->getOperand(0)) - .addReg(Cond); - MBB.erase(MI); - } - } - - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp deleted file mode 100644 index 960d5134f6e9..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ /dev/null @@ -1,1110 +0,0 @@ -//=== WebAssemblyLowerEmscriptenEHSjLj.cpp - Lower exceptions for Emscripten =// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file lowers exception-related instructions and setjmp/longjmp -/// function calls in order to use Emscripten's JavaScript try and catch -/// mechanism. -/// -/// To handle exceptions and setjmp/longjmps, this scheme relies on JavaScript's -/// try and catch syntax and relevant exception-related libraries implemented -/// in JavaScript glue code that will be produced by Emscripten. This is similar -/// to the current Emscripten asm.js exception handling in fastcomp. For -/// fastcomp's EH / SjLj scheme, see these files in fastcomp LLVM branch: -/// (Location: https://github.com/kripken/emscripten-fastcomp) -/// lib/Target/JSBackend/NaCl/LowerEmExceptionsPass.cpp -/// lib/Target/JSBackend/NaCl/LowerEmSetjmp.cpp -/// lib/Target/JSBackend/JSBackend.cpp -/// lib/Target/JSBackend/CallHandlers.h -/// -/// * Exception handling -/// This pass lowers invokes and landingpads into library functions in JS glue -/// code. Invokes are lowered into function wrappers called invoke wrappers that -/// exist in JS side, which wraps the original function call with JS try-catch. -/// If an exception occurred, cxa_throw() function in JS side sets some -/// variables (see below) so we can check whether an exception occurred from -/// wasm code and handle it appropriately. -/// -/// * Setjmp-longjmp handling -/// This pass lowers setjmp to a reasonably-performant approach for emscripten. -/// The idea is that each block with a setjmp is broken up into two parts: the -/// part containing setjmp and the part right after the setjmp. The latter part -/// is either reached from the setjmp, or later from a longjmp. To handle the -/// longjmp, all calls that might longjmp are also called using invoke wrappers -/// and thus JS / try-catch. JS longjmp() function also sets some variables so -/// we can check / whether a longjmp occurred from wasm code. Each block with a -/// function call that might longjmp is also split up after the longjmp call. -/// After the longjmp call, we check whether a longjmp occurred, and if it did, -/// which setjmp it corresponds to, and jump to the right post-setjmp block. -/// We assume setjmp-longjmp handling always run after EH handling, which means -/// we don't expect any exception-related instructions when SjLj runs. -/// FIXME Currently this scheme does not support indirect call of setjmp, -/// because of the limitation of the scheme itself. fastcomp does not support it -/// either. -/// -/// In detail, this pass does following things: -/// -/// 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. -/// -/// * Exception handling -/// -/// 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. -/// -/// function setThrew(threw, value) { -/// if (__THREW__ == 0) { -/// __THREW__ = threw; -/// __threwValue = value; -/// } -/// } -// -/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code. -/// -/// 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 -/// into -/// __THREW__ = 0; -/// call @__invoke_SIG(func, arg1, arg2) -/// %__THREW__.val = __THREW__; -/// __THREW__ = 0; -/// if (%__THREW__.val == 1) -/// goto %lpad -/// else -/// goto %invoke.cont -/// SIG is a mangled string generated based on the LLVM IR-level function -/// signature. After LLVM IR types are lowered to the target wasm types, -/// the names for these wrappers will change based on wasm types as well, -/// as in invoke_vi (function takes an int and returns void). The bodies of -/// these wrappers will be generated in JS glue code, and inside those -/// wrappers we use JS try-catch to generate actual exception effects. It -/// also calls the original callee function. An example wrapper in JS code -/// would look like this: -/// function invoke_vi(index,a1) { -/// try { -/// Module["dynCall_vi"](index,a1); // This calls original callee -/// } catch(e) { -/// if (typeof e !== 'number' && e !== 'longjmp') throw e; -/// asm["setThrew"](1, 0); // setThrew is called here -/// } -/// } -/// If an exception is thrown, __THREW__ will be set to true in a wrapper, -/// so we can jump to the right BB based on this value. -/// -/// 4) Lower -/// %val = landingpad catch c1 catch c2 catch c3 ... -/// ... use %val ... -/// into -/// %fmc = call @__cxa_find_matching_catch_N(c1, c2, c3, ...) -/// %val = {%fmc, getTempRet0()} -/// ... use %val ... -/// Here N is a number calculated based on the number of clauses. -/// setTempRet0 is called from __cxa_find_matching_catch() in JS glue code. -/// -/// 5) Lower -/// resume {%a, %b} -/// into -/// call @__resumeException(%a) -/// where __resumeException() is a function in JS glue code. -/// -/// 6) Lower -/// call @llvm.eh.typeid.for(type) (intrinsic) -/// into -/// call @llvm_eh_typeid_for(type) -/// llvm_eh_typeid_for function will be generated in JS glue code. -/// -/// * Setjmp / Longjmp handling -/// -/// 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); -/// setjmpTable[0] = 0; -/// setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS -/// code. -/// -/// 3) Lower -/// setjmp(buf) -/// into -/// setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); -/// 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 -/// 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. -/// -/// -/// 4) Lower every call that might longjmp into -/// __THREW__ = 0; -/// call @__invoke_SIG(func, arg1, arg2) -/// %__THREW__.val = __THREW__; -/// __THREW__ = 0; -/// if (%__THREW__.val != 0 & __threwValue != 0) { -/// %label = testSetjmp(mem[%__THREW__.val], setjmpTable, -/// setjmpTableSize); -/// if (%label == 0) -/// emscripten_longjmp(%__THREW__.val, __threwValue); -/// setTempRet0(__threwValue); -/// } else { -/// %label = -1; -/// } -/// 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. -/// -///===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "llvm/IR/CallSite.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/SSAUpdater.h" - -using namespace llvm; - -#define DEBUG_TYPE "wasm-lower-em-ehsjlj" - -static cl::list<std::string> - EHWhitelist("emscripten-cxx-exceptions-whitelist", - cl::desc("The list of function names in which Emscripten-style " - "exception handling is enabled (see emscripten " - "EMSCRIPTEN_CATCHING_WHITELIST options)"), - cl::CommaSeparated); - -namespace { -class WebAssemblyLowerEmscriptenEHSjLj final : public ModulePass { - static const char *ResumeFName; - static const char *EHTypeIDFName; - static const char *EmLongjmpFName; - static const char *EmLongjmpJmpbufFName; - static const char *SaveSetjmpFName; - static const char *TestSetjmpFName; - static const char *FindMatchingCatchPrefix; - static const char *InvokePrefix; - - bool EnableEH; // Enable exception handling - bool EnableSjLj; // Enable setjmp/longjmp handling - - GlobalVariable *ThrewGV = nullptr; - GlobalVariable *ThrewValueGV = nullptr; - Function *GetTempRet0Func = nullptr; - Function *SetTempRet0Func = nullptr; - Function *ResumeF = nullptr; - Function *EHTypeIDF = nullptr; - Function *EmLongjmpF = nullptr; - Function *EmLongjmpJmpbufF = nullptr; - Function *SaveSetjmpF = nullptr; - Function *TestSetjmpF = nullptr; - - // __cxa_find_matching_catch_N functions. - // Indexed by the number of clauses in an original landingpad instruction. - DenseMap<int, Function *> FindMatchingCatches; - // Map of <function signature string, invoke_ wrappers> - StringMap<Function *> InvokeWrappers; - // Set of whitelisted function names for exception handling - std::set<std::string> EHWhitelistSet; - - StringRef getPassName() const override { - return "WebAssembly Lower Emscripten Exceptions"; - } - - bool runEHOnFunction(Function &F); - bool runSjLjOnFunction(Function &F); - Function *getFindMatchingCatch(Module &M, unsigned NumClauses); - - template <typename CallOrInvoke> Value *wrapInvoke(CallOrInvoke *CI); - void wrapTestSetjmp(BasicBlock *BB, Instruction *InsertPt, Value *Threw, - Value *SetjmpTable, Value *SetjmpTableSize, Value *&Label, - Value *&LongjmpResult, BasicBlock *&EndBB); - template <typename CallOrInvoke> Function *getInvokeWrapper(CallOrInvoke *CI); - - bool areAllExceptionsAllowed() const { return EHWhitelistSet.empty(); } - bool canLongjmp(Module &M, const Value *Callee) const; - - void rebuildSSA(Function &F); - -public: - static char ID; - - WebAssemblyLowerEmscriptenEHSjLj(bool EnableEH = true, bool EnableSjLj = true) - : ModulePass(ID), EnableEH(EnableEH), EnableSjLj(EnableSjLj) { - EHWhitelistSet.insert(EHWhitelist.begin(), EHWhitelist.end()); - } - bool runOnModule(Module &M) override; - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.addRequired<DominatorTreeWrapperPass>(); - } -}; -} // End anonymous namespace - -const char *WebAssemblyLowerEmscriptenEHSjLj::ResumeFName = "__resumeException"; -const char *WebAssemblyLowerEmscriptenEHSjLj::EHTypeIDFName = - "llvm_eh_typeid_for"; -const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpFName = - "emscripten_longjmp"; -const char *WebAssemblyLowerEmscriptenEHSjLj::EmLongjmpJmpbufFName = - "emscripten_longjmp_jmpbuf"; -const char *WebAssemblyLowerEmscriptenEHSjLj::SaveSetjmpFName = "saveSetjmp"; -const char *WebAssemblyLowerEmscriptenEHSjLj::TestSetjmpFName = "testSetjmp"; -const char *WebAssemblyLowerEmscriptenEHSjLj::FindMatchingCatchPrefix = - "__cxa_find_matching_catch_"; -const char *WebAssemblyLowerEmscriptenEHSjLj::InvokePrefix = "__invoke_"; - -char WebAssemblyLowerEmscriptenEHSjLj::ID = 0; -INITIALIZE_PASS(WebAssemblyLowerEmscriptenEHSjLj, DEBUG_TYPE, - "WebAssembly Lower Emscripten Exceptions / Setjmp / Longjmp", - false, false) - -ModulePass *llvm::createWebAssemblyLowerEmscriptenEHSjLj(bool EnableEH, - bool EnableSjLj) { - return new WebAssemblyLowerEmscriptenEHSjLj(EnableEH, EnableSjLj); -} - -static bool canThrow(const Value *V) { - if (const auto *F = dyn_cast<const Function>(V)) { - // Intrinsics cannot throw - if (F->isIntrinsic()) - return false; - StringRef Name = F->getName(); - // leave setjmp and longjmp (mostly) alone, we process them properly later - if (Name == "setjmp" || Name == "longjmp") - return false; - return !F->doesNotThrow(); - } - // not a function, so an indirect call - can throw, we can't tell - return true; -} - -// 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) { - - auto* GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, IRB.getInt32Ty())); - if (!GV) - report_fatal_error(Twine("unable to create global: ") + Name); - - return GV; -} - -// Simple function name mangler. -// This function simply takes LLVM's string representation of parameter types -// and concatenate them with '_'. There are non-alphanumeric characters but llc -// is ok with it, and we need to postprocess these names after the lowering -// phase anyway. -static std::string getSignature(FunctionType *FTy) { - std::string Sig; - raw_string_ostream OS(Sig); - OS << *FTy->getReturnType(); - for (Type *ParamTy : FTy->params()) - OS << "_" << *ParamTy; - if (FTy->isVarArg()) - OS << "_..."; - Sig = OS.str(); - Sig.erase(remove_if(Sig, isspace), Sig.end()); - // When s2wasm parses .s file, a comma means the end of an argument. So a - // mangled function name can contain any character but a comma. - std::replace(Sig.begin(), Sig.end(), ',', '.'); - return Sig; -} - -// Returns __cxa_find_matching_catch_N function, where N = NumClauses + 2. -// This is because a landingpad instruction contains two more arguments, a -// personality function and a cleanup bit, and __cxa_find_matching_catch_N -// functions are named after the number of arguments in the original landingpad -// instruction. -Function * -WebAssemblyLowerEmscriptenEHSjLj::getFindMatchingCatch(Module &M, - unsigned NumClauses) { - if (FindMatchingCatches.count(NumClauses)) - return FindMatchingCatches[NumClauses]; - PointerType *Int8PtrTy = Type::getInt8PtrTy(M.getContext()); - SmallVector<Type *, 16> Args(NumClauses, Int8PtrTy); - FunctionType *FTy = FunctionType::get(Int8PtrTy, Args, false); - Function *F = - Function::Create(FTy, GlobalValue::ExternalLinkage, - FindMatchingCatchPrefix + Twine(NumClauses + 2), &M); - FindMatchingCatches[NumClauses] = F; - return F; -} - -// Generate invoke wrapper seqence with preamble and postamble -// Preamble: -// __THREW__ = 0; -// Postamble: -// %__THREW__.val = __THREW__; __THREW__ = 0; -// Returns %__THREW__.val, which indicates whether an exception is thrown (or -// whether longjmp occurred), for future use. -template <typename CallOrInvoke> -Value *WebAssemblyLowerEmscriptenEHSjLj::wrapInvoke(CallOrInvoke *CI) { - LLVMContext &C = CI->getModule()->getContext(); - - // If we are calling a function that is noreturn, we must remove that - // attribute. The code we insert here does expect it to return, after we - // catch the exception. - if (CI->doesNotReturn()) { - if (auto *F = dyn_cast<Function>(CI->getCalledValue())) - F->removeFnAttr(Attribute::NoReturn); - CI->removeAttribute(AttributeList::FunctionIndex, Attribute::NoReturn); - } - - IRBuilder<> IRB(C); - IRB.SetInsertPoint(CI); - - // Pre-invoke - // __THREW__ = 0; - IRB.CreateStore(IRB.getInt32(0), ThrewGV); - - // Invoke function wrapper in JavaScript - SmallVector<Value *, 16> Args; - // Put the pointer to the callee as first argument, so it can be called - // within the invoke wrapper later - Args.push_back(CI->getCalledValue()); - Args.append(CI->arg_begin(), CI->arg_end()); - CallInst *NewCall = IRB.CreateCall(getInvokeWrapper(CI), Args); - NewCall->takeName(CI); - NewCall->setCallingConv(CI->getCallingConv()); - NewCall->setDebugLoc(CI->getDebugLoc()); - - // Because we added the pointer to the callee as first argument, all - // argument attribute indices have to be incremented by one. - SmallVector<AttributeSet, 8> ArgAttributes; - const AttributeList &InvokeAL = CI->getAttributes(); - - // No attributes for the callee pointer. - ArgAttributes.push_back(AttributeSet()); - // Copy the argument attributes from the original - for (unsigned I = 0, E = CI->getNumArgOperands(); I < E; ++I) - ArgAttributes.push_back(InvokeAL.getParamAttributes(I)); - - // Reconstruct the AttributesList based on the vector we constructed. - AttributeList NewCallAL = - AttributeList::get(C, InvokeAL.getFnAttributes(), - InvokeAL.getRetAttributes(), ArgAttributes); - NewCall->setAttributes(NewCallAL); - - CI->replaceAllUsesWith(NewCall); - - // Post-invoke - // %__THREW__.val = __THREW__; __THREW__ = 0; - Value *Threw = - IRB.CreateLoad(IRB.getInt32Ty(), ThrewGV, ThrewGV->getName() + ".val"); - IRB.CreateStore(IRB.getInt32(0), ThrewGV); - return Threw; -} - -// Get matching invoke wrapper based on callee signature -template <typename CallOrInvoke> -Function *WebAssemblyLowerEmscriptenEHSjLj::getInvokeWrapper(CallOrInvoke *CI) { - Module *M = CI->getModule(); - SmallVector<Type *, 16> ArgTys; - Value *Callee = CI->getCalledValue(); - FunctionType *CalleeFTy; - if (auto *F = dyn_cast<Function>(Callee)) - CalleeFTy = F->getFunctionType(); - else { - auto *CalleeTy = cast<PointerType>(Callee->getType())->getElementType(); - CalleeFTy = dyn_cast<FunctionType>(CalleeTy); - } - - std::string Sig = getSignature(CalleeFTy); - if (InvokeWrappers.find(Sig) != InvokeWrappers.end()) - return InvokeWrappers[Sig]; - - // Put the pointer to the callee as first argument - ArgTys.push_back(PointerType::getUnqual(CalleeFTy)); - // Add argument types - ArgTys.append(CalleeFTy->param_begin(), CalleeFTy->param_end()); - - FunctionType *FTy = FunctionType::get(CalleeFTy->getReturnType(), ArgTys, - CalleeFTy->isVarArg()); - Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, - InvokePrefix + Sig, M); - InvokeWrappers[Sig] = F; - return F; -} - -bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(Module &M, - const Value *Callee) const { - if (auto *CalleeF = dyn_cast<Function>(Callee)) - if (CalleeF->isIntrinsic()) - return false; - - // Attempting to transform inline assembly will result in something like: - // call void @__invoke_void(void ()* asm ...) - // which is invalid because inline assembly blocks do not have addresses - // and can't be passed by pointer. The result is a crash with illegal IR. - if (isa<InlineAsm>(Callee)) - return false; - - // The reason we include malloc/free here is to exclude the malloc/free - // calls generated in setjmp prep / cleanup routines. - Function *SetjmpF = M.getFunction("setjmp"); - Function *MallocF = M.getFunction("malloc"); - Function *FreeF = M.getFunction("free"); - if (Callee == SetjmpF || Callee == MallocF || Callee == FreeF) - return false; - - // There are functions in JS glue code - if (Callee == ResumeF || Callee == EHTypeIDF || Callee == SaveSetjmpF || - Callee == TestSetjmpF) - return false; - - // __cxa_find_matching_catch_N functions cannot longjmp - if (Callee->getName().startswith(FindMatchingCatchPrefix)) - return false; - - // Exception-catching related functions - Function *BeginCatchF = M.getFunction("__cxa_begin_catch"); - Function *EndCatchF = M.getFunction("__cxa_end_catch"); - Function *AllocExceptionF = M.getFunction("__cxa_allocate_exception"); - 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 == GetTempRet0Func || Callee == SetTempRet0Func) - return false; - - // Otherwise we don't know - return true; -} - -// Generate testSetjmp function call seqence with preamble and postamble. -// The code this generates is equivalent to the following JavaScript code: -// if (%__THREW__.val != 0 & threwValue != 0) { -// %label = _testSetjmp(mem[%__THREW__.val], setjmpTable, setjmpTableSize); -// if (%label == 0) -// emscripten_longjmp(%__THREW__.val, threwValue); -// setTempRet0(threwValue); -// } else { -// %label = -1; -// } -// %longjmp_result = getTempRet0(); -// -// As output parameters. returns %label, %longjmp_result, and the BB the last -// instruction (%longjmp_result = ...) is in. -void WebAssemblyLowerEmscriptenEHSjLj::wrapTestSetjmp( - BasicBlock *BB, Instruction *InsertPt, Value *Threw, Value *SetjmpTable, - Value *SetjmpTableSize, Value *&Label, Value *&LongjmpResult, - BasicBlock *&EndBB) { - Function *F = BB->getParent(); - LLVMContext &C = BB->getModule()->getContext(); - IRBuilder<> IRB(C); - IRB.SetInsertPoint(InsertPt); - - // if (%__THREW__.val != 0 & threwValue != 0) - IRB.SetInsertPoint(BB); - BasicBlock *ThenBB1 = BasicBlock::Create(C, "if.then1", F); - BasicBlock *ElseBB1 = BasicBlock::Create(C, "if.else1", F); - BasicBlock *EndBB1 = BasicBlock::Create(C, "if.end", F); - Value *ThrewCmp = IRB.CreateICmpNE(Threw, IRB.getInt32(0)); - Value *ThrewValue = IRB.CreateLoad(IRB.getInt32Ty(), ThrewValueGV, - ThrewValueGV->getName() + ".val"); - Value *ThrewValueCmp = IRB.CreateICmpNE(ThrewValue, IRB.getInt32(0)); - Value *Cmp1 = IRB.CreateAnd(ThrewCmp, ThrewValueCmp, "cmp1"); - IRB.CreateCondBr(Cmp1, ThenBB1, ElseBB1); - - // %label = _testSetjmp(mem[%__THREW__.val], _setjmpTable, _setjmpTableSize); - // if (%label == 0) - IRB.SetInsertPoint(ThenBB1); - BasicBlock *ThenBB2 = BasicBlock::Create(C, "if.then2", F); - BasicBlock *EndBB2 = BasicBlock::Create(C, "if.end2", F); - Value *ThrewInt = IRB.CreateIntToPtr(Threw, Type::getInt32PtrTy(C), - Threw->getName() + ".i32p"); - Value *LoadedThrew = IRB.CreateLoad(IRB.getInt32Ty(), ThrewInt, - ThrewInt->getName() + ".loaded"); - Value *ThenLabel = IRB.CreateCall( - TestSetjmpF, {LoadedThrew, SetjmpTable, SetjmpTableSize}, "label"); - Value *Cmp2 = IRB.CreateICmpEQ(ThenLabel, IRB.getInt32(0)); - IRB.CreateCondBr(Cmp2, ThenBB2, EndBB2); - - // emscripten_longjmp(%__THREW__.val, threwValue); - IRB.SetInsertPoint(ThenBB2); - IRB.CreateCall(EmLongjmpF, {Threw, ThrewValue}); - IRB.CreateUnreachable(); - - // setTempRet0(threwValue); - IRB.SetInsertPoint(EndBB2); - IRB.CreateCall(SetTempRet0Func, ThrewValue); - IRB.CreateBr(EndBB1); - - IRB.SetInsertPoint(ElseBB1); - IRB.CreateBr(EndBB1); - - // longjmp_result = getTempRet0(); - IRB.SetInsertPoint(EndBB1); - PHINode *LabelPHI = IRB.CreatePHI(IRB.getInt32Ty(), 2, "label"); - LabelPHI->addIncoming(ThenLabel, EndBB2); - - LabelPHI->addIncoming(IRB.getInt32(-1), ElseBB1); - - // Output parameter assignment - Label = LabelPHI; - EndBB = EndBB1; - LongjmpResult = IRB.CreateCall(GetTempRet0Func, None, "longjmp_result"); -} - -void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) { - DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); - DT.recalculate(F); // CFG has been changed - SSAUpdater SSA; - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { - for (auto UI = I.use_begin(), UE = I.use_end(); UI != UE;) { - Use &U = *UI; - ++UI; - SSA.Initialize(I.getType(), I.getName()); - SSA.AddAvailableValue(&BB, &I); - auto *User = cast<Instruction>(U.getUser()); - if (User->getParent() == &BB) - continue; - - if (auto *UserPN = dyn_cast<PHINode>(User)) - if (UserPN->getIncomingBlock(U) == &BB) - continue; - - if (DT.dominates(&I, User)) - continue; - SSA.RewriteUseAfterInsertions(U); - } - } - } -} - -bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) { - LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n"); - - LLVMContext &C = M.getContext(); - IRBuilder<> IRB(C); - - Function *SetjmpF = M.getFunction("setjmp"); - Function *LongjmpF = M.getFunction("longjmp"); - bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty(); - bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); - bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed); - - // 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; - - // Exception handling - if (EnableEH) { - // Register __resumeException function - FunctionType *ResumeFTy = - FunctionType::get(IRB.getVoidTy(), IRB.getInt8PtrTy(), false); - ResumeF = Function::Create(ResumeFTy, GlobalValue::ExternalLinkage, - ResumeFName, &M); - - // Register llvm_eh_typeid_for function - FunctionType *EHTypeIDTy = - FunctionType::get(IRB.getInt32Ty(), IRB.getInt8PtrTy(), false); - EHTypeIDF = Function::Create(EHTypeIDTy, GlobalValue::ExternalLinkage, - EHTypeIDFName, &M); - - for (Function &F : M) { - if (F.isDeclaration()) - continue; - Changed |= runEHOnFunction(F); - } - } - - // Setjmp/longjmp handling - if (DoSjLj) { - Changed = true; // We have setjmp or longjmp somewhere - - if (LongjmpF) { - // Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is - // defined in JS code - EmLongjmpJmpbufF = Function::Create(LongjmpF->getFunctionType(), - GlobalValue::ExternalLinkage, - EmLongjmpJmpbufFName, &M); - - LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF); - } - - 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); - } - } - - if (!Changed) { - // Delete unused global variables and functions - if (ResumeF) - ResumeF->eraseFromParent(); - if (EHTypeIDF) - EHTypeIDF->eraseFromParent(); - if (EmLongjmpF) - EmLongjmpF->eraseFromParent(); - if (SaveSetjmpF) - SaveSetjmpF->eraseFromParent(); - if (TestSetjmpF) - TestSetjmpF->eraseFromParent(); - return false; - } - - return true; -} - -bool WebAssemblyLowerEmscriptenEHSjLj::runEHOnFunction(Function &F) { - Module &M = *F.getParent(); - LLVMContext &C = F.getContext(); - IRBuilder<> IRB(C); - bool Changed = false; - SmallVector<Instruction *, 64> ToErase; - SmallPtrSet<LandingPadInst *, 32> LandingPads; - bool AllowExceptions = - areAllExceptionsAllowed() || EHWhitelistSet.count(F.getName()); - - for (BasicBlock &BB : F) { - auto *II = dyn_cast<InvokeInst>(BB.getTerminator()); - if (!II) - continue; - Changed = true; - LandingPads.insert(II->getLandingPadInst()); - IRB.SetInsertPoint(II); - - bool NeedInvoke = AllowExceptions && canThrow(II->getCalledValue()); - if (NeedInvoke) { - // Wrap invoke with invoke wrapper and generate preamble/postamble - Value *Threw = wrapInvoke(II); - ToErase.push_back(II); - - // Insert a branch based on __THREW__ variable - Value *Cmp = IRB.CreateICmpEQ(Threw, IRB.getInt32(1), "cmp"); - IRB.CreateCondBr(Cmp, II->getUnwindDest(), II->getNormalDest()); - - } else { - // This can't throw, and we don't need this invoke, just replace it with a - // call+branch - SmallVector<Value *, 16> Args(II->arg_begin(), II->arg_end()); - CallInst *NewCall = - IRB.CreateCall(II->getFunctionType(), II->getCalledValue(), Args); - NewCall->takeName(II); - NewCall->setCallingConv(II->getCallingConv()); - NewCall->setDebugLoc(II->getDebugLoc()); - NewCall->setAttributes(II->getAttributes()); - II->replaceAllUsesWith(NewCall); - ToErase.push_back(II); - - IRB.CreateBr(II->getNormalDest()); - - // Remove any PHI node entries from the exception destination - II->getUnwindDest()->removePredecessor(&BB); - } - } - - // Process resume instructions - for (BasicBlock &BB : F) { - // Scan the body of the basic block for resumes - for (Instruction &I : BB) { - auto *RI = dyn_cast<ResumeInst>(&I); - if (!RI) - continue; - - // Split the input into legal values - Value *Input = RI->getValue(); - IRB.SetInsertPoint(RI); - Value *Low = IRB.CreateExtractValue(Input, 0, "low"); - // Create a call to __resumeException function - IRB.CreateCall(ResumeF, {Low}); - // Add a terminator to the block - IRB.CreateUnreachable(); - ToErase.push_back(RI); - } - } - - // Process llvm.eh.typeid.for intrinsics - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { - auto *CI = dyn_cast<CallInst>(&I); - if (!CI) - continue; - const Function *Callee = CI->getCalledFunction(); - if (!Callee) - continue; - if (Callee->getIntrinsicID() != Intrinsic::eh_typeid_for) - continue; - - IRB.SetInsertPoint(CI); - CallInst *NewCI = - IRB.CreateCall(EHTypeIDF, CI->getArgOperand(0), "typeid"); - CI->replaceAllUsesWith(NewCI); - ToErase.push_back(CI); - } - } - - // Look for orphan landingpads, can occur in blocks with no predecessors - for (BasicBlock &BB : F) { - Instruction *I = BB.getFirstNonPHI(); - if (auto *LPI = dyn_cast<LandingPadInst>(I)) - LandingPads.insert(LPI); - } - - // Handle all the landingpad for this function together, as multiple invokes - // may share a single lp - for (LandingPadInst *LPI : LandingPads) { - IRB.SetInsertPoint(LPI); - SmallVector<Value *, 16> FMCArgs; - for (unsigned I = 0, E = LPI->getNumClauses(); I < E; ++I) { - Constant *Clause = LPI->getClause(I); - // As a temporary workaround for the lack of aggregate varargs support - // in the interface between JS and wasm, break out filter operands into - // their component elements. - if (LPI->isFilter(I)) { - auto *ATy = cast<ArrayType>(Clause->getType()); - for (unsigned J = 0, E = ATy->getNumElements(); J < E; ++J) { - Value *EV = IRB.CreateExtractValue(Clause, makeArrayRef(J), "filter"); - FMCArgs.push_back(EV); - } - } else - FMCArgs.push_back(Clause); - } - - // Create a call to __cxa_find_matching_catch_N function - Function *FMCF = getFindMatchingCatch(M, FMCArgs.size()); - CallInst *FMCI = IRB.CreateCall(FMCF, FMCArgs, "fmc"); - Value *Undef = UndefValue::get(LPI->getType()); - Value *Pair0 = IRB.CreateInsertValue(Undef, FMCI, 0, "pair0"); - Value *TempRet0 = IRB.CreateCall(GetTempRet0Func, None, "tempret0"); - Value *Pair1 = IRB.CreateInsertValue(Pair0, TempRet0, 1, "pair1"); - - LPI->replaceAllUsesWith(Pair1); - ToErase.push_back(LPI); - } - - // Erase everything we no longer need in this function - for (Instruction *I : ToErase) - I->eraseFromParent(); - - return Changed; -} - -bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) { - Module &M = *F.getParent(); - LLVMContext &C = F.getContext(); - IRBuilder<> IRB(C); - SmallVector<Instruction *, 64> ToErase; - // Vector of %setjmpTable values - std::vector<Instruction *> SetjmpTableInsts; - // Vector of %setjmpTableSize values - std::vector<Instruction *> SetjmpTableSizeInsts; - - // Setjmp preparation - - // This instruction effectively means %setjmpTableSize = 4. - // We create this as an instruction intentionally, and we don't want to fold - // this instruction to a constant 4, because this value will be used in - // SSAUpdater.AddAvailableValue(...) later. - BasicBlock &EntryBB = F.getEntryBlock(); - BinaryOperator *SetjmpTableSize = BinaryOperator::Create( - Instruction::Add, IRB.getInt32(4), IRB.getInt32(0), "setjmpTableSize", - &*EntryBB.getFirstInsertionPt()); - // setjmpTable = (int *) malloc(40); - Instruction *SetjmpTable = CallInst::CreateMalloc( - SetjmpTableSize, IRB.getInt32Ty(), IRB.getInt32Ty(), IRB.getInt32(40), - nullptr, nullptr, "setjmpTable"); - // setjmpTable[0] = 0; - IRB.SetInsertPoint(SetjmpTableSize); - IRB.CreateStore(IRB.getInt32(0), SetjmpTable); - SetjmpTableInsts.push_back(SetjmpTable); - SetjmpTableSizeInsts.push_back(SetjmpTableSize); - - // Setjmp transformation - std::vector<PHINode *> SetjmpRetPHIs; - Function *SetjmpF = M.getFunction("setjmp"); - for (User *U : SetjmpF->users()) { - auto *CI = dyn_cast<CallInst>(U); - if (!CI) - report_fatal_error("Does not support indirect calls to setjmp"); - - BasicBlock *BB = CI->getParent(); - if (BB->getParent() != &F) // in other function - continue; - - // The tail is everything right after the call, and will be reached once - // when setjmp is called, and later when longjmp returns to the setjmp - BasicBlock *Tail = SplitBlock(BB, CI->getNextNode()); - // Add a phi to the tail, which will be the output of setjmp, which - // indicates if this is the first call or a longjmp back. The phi directly - // uses the right value based on where we arrive from - IRB.SetInsertPoint(Tail->getFirstNonPHI()); - PHINode *SetjmpRet = IRB.CreatePHI(IRB.getInt32Ty(), 2, "setjmp.ret"); - - // setjmp initial call returns 0 - SetjmpRet->addIncoming(IRB.getInt32(0), BB); - // The proper output is now this, not the setjmp call itself - CI->replaceAllUsesWith(SetjmpRet); - // longjmp returns to the setjmp will add themselves to this phi - SetjmpRetPHIs.push_back(SetjmpRet); - - // Fix call target - // Our index in the function is our place in the array + 1 to avoid index - // 0, because index 0 means the longjmp is not ours to handle. - IRB.SetInsertPoint(CI); - Value *Args[] = {CI->getArgOperand(0), IRB.getInt32(SetjmpRetPHIs.size()), - SetjmpTable, SetjmpTableSize}; - Instruction *NewSetjmpTable = - IRB.CreateCall(SaveSetjmpF, Args, "setjmpTable"); - Instruction *NewSetjmpTableSize = - IRB.CreateCall(GetTempRet0Func, None, "setjmpTableSize"); - SetjmpTableInsts.push_back(NewSetjmpTable); - SetjmpTableSizeInsts.push_back(NewSetjmpTableSize); - ToErase.push_back(CI); - } - - // Update each call that can longjmp so it can return to a setjmp where - // relevant. - - // Because we are creating new BBs while processing and don't want to make - // all these newly created BBs candidates again for longjmp processing, we - // first make the vector of candidate BBs. - std::vector<BasicBlock *> BBs; - for (BasicBlock &BB : F) - BBs.push_back(&BB); - - // BBs.size() will change within the loop, so we query it every time - for (unsigned I = 0; I < BBs.size(); I++) { - BasicBlock *BB = BBs[I]; - for (Instruction &I : *BB) { - assert(!isa<InvokeInst>(&I)); - auto *CI = dyn_cast<CallInst>(&I); - if (!CI) - continue; - - const Value *Callee = CI->getCalledValue(); - if (!canLongjmp(M, Callee)) - continue; - - Value *Threw = nullptr; - BasicBlock *Tail; - if (Callee->getName().startswith(InvokePrefix)) { - // If invoke wrapper has already been generated for this call in - // previous EH phase, search for the load instruction - // %__THREW__.val = __THREW__; - // in postamble after the invoke wrapper call - LoadInst *ThrewLI = nullptr; - StoreInst *ThrewResetSI = nullptr; - for (auto I = std::next(BasicBlock::iterator(CI)), IE = BB->end(); - I != IE; ++I) { - if (auto *LI = dyn_cast<LoadInst>(I)) - if (auto *GV = dyn_cast<GlobalVariable>(LI->getPointerOperand())) - if (GV == ThrewGV) { - Threw = ThrewLI = LI; - break; - } - } - // Search for the store instruction after the load above - // __THREW__ = 0; - for (auto I = std::next(BasicBlock::iterator(ThrewLI)), IE = BB->end(); - I != IE; ++I) { - if (auto *SI = dyn_cast<StoreInst>(I)) - if (auto *GV = dyn_cast<GlobalVariable>(SI->getPointerOperand())) - if (GV == ThrewGV && SI->getValueOperand() == IRB.getInt32(0)) { - ThrewResetSI = SI; - break; - } - } - assert(Threw && ThrewLI && "Cannot find __THREW__ load after invoke"); - assert(ThrewResetSI && "Cannot find __THREW__ store after invoke"); - Tail = SplitBlock(BB, ThrewResetSI->getNextNode()); - - } else { - // Wrap call with invoke wrapper and generate preamble/postamble - Threw = wrapInvoke(CI); - ToErase.push_back(CI); - Tail = SplitBlock(BB, CI->getNextNode()); - } - - // We need to replace the terminator in Tail - SplitBlock makes BB go - // straight to Tail, we need to check if a longjmp occurred, and go to the - // right setjmp-tail if so - ToErase.push_back(BB->getTerminator()); - - // Generate a function call to testSetjmp function and preamble/postamble - // code to figure out (1) whether longjmp occurred (2) if longjmp - // occurred, which setjmp it corresponds to - Value *Label = nullptr; - Value *LongjmpResult = nullptr; - BasicBlock *EndBB = nullptr; - wrapTestSetjmp(BB, CI, Threw, SetjmpTable, SetjmpTableSize, Label, - LongjmpResult, EndBB); - assert(Label && LongjmpResult && EndBB); - - // Create switch instruction - IRB.SetInsertPoint(EndBB); - SwitchInst *SI = IRB.CreateSwitch(Label, Tail, SetjmpRetPHIs.size()); - // -1 means no longjmp happened, continue normally (will hit the default - // switch case). 0 means a longjmp that is not ours to handle, needs a - // rethrow. Otherwise the index is the same as the index in P+1 (to avoid - // 0). - for (unsigned I = 0; I < SetjmpRetPHIs.size(); I++) { - SI->addCase(IRB.getInt32(I + 1), SetjmpRetPHIs[I]->getParent()); - SetjmpRetPHIs[I]->addIncoming(LongjmpResult, EndBB); - } - - // We are splitting the block here, and must continue to find other calls - // in the block - which is now split. so continue to traverse in the Tail - BBs.push_back(Tail); - } - } - - // Erase everything we no longer need in this function - for (Instruction *I : ToErase) - I->eraseFromParent(); - - // Free setjmpTable buffer before each return instruction - for (BasicBlock &BB : F) { - Instruction *TI = BB.getTerminator(); - if (isa<ReturnInst>(TI)) - CallInst::CreateFree(SetjmpTable, TI); - } - - // Every call to saveSetjmp can change setjmpTable and setjmpTableSize - // (when buffer reallocation occurs) - // entry: - // setjmpTableSize = 4; - // setjmpTable = (int *) malloc(40); - // setjmpTable[0] = 0; - // ... - // somebb: - // setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize); - // 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; - SSAUpdater SetjmpTableSizeSSA; - SetjmpTableSSA.Initialize(Type::getInt32PtrTy(C), "setjmpTable"); - SetjmpTableSizeSSA.Initialize(Type::getInt32Ty(C), "setjmpTableSize"); - for (Instruction *I : SetjmpTableInsts) - SetjmpTableSSA.AddAvailableValue(I->getParent(), I); - for (Instruction *I : SetjmpTableSizeInsts) - SetjmpTableSizeSSA.AddAvailableValue(I->getParent(), I); - - for (auto UI = SetjmpTable->use_begin(), UE = SetjmpTable->use_end(); - UI != UE;) { - // Grab the use before incrementing the iterator. - Use &U = *UI; - // Increment the iterator before removing the use from the list. - ++UI; - if (auto *I = dyn_cast<Instruction>(U.getUser())) - if (I->getParent() != &EntryBB) - SetjmpTableSSA.RewriteUse(U); - } - for (auto UI = SetjmpTableSize->use_begin(), UE = SetjmpTableSize->use_end(); - UI != UE;) { - Use &U = *UI; - ++UI; - if (auto *I = dyn_cast<Instruction>(U.getUser())) - if (I->getParent() != &EntryBB) - SetjmpTableSizeSSA.RewriteUse(U); - } - - // Finally, our modifications to the cfg can break dominance of SSA variables. - // For example, in this code, - // if (x()) { .. setjmp() .. } - // if (y()) { .. longjmp() .. } - // We must split the longjmp block, and it can jump into the block splitted - // from setjmp one. But that means that when we split the setjmp block, it's - // first part no longer dominates its second part - there is a theoretically - // possible control flow path where x() is false, then y() is true and we - // reach the second part of the setjmp block, without ever reaching the first - // part. So, we rebuild SSA form here. - rebuildSSA(F); - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp deleted file mode 100644 index 494d3fadbc8c..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp +++ /dev/null @@ -1,190 +0,0 @@ -//===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Lower @llvm.global_dtors. -/// -/// WebAssembly doesn't have a builtin way to invoke static destructors. -/// Implement @llvm.global_dtors by creating wrapper functions that are -/// registered in @llvm.global_ctors and which contain a call to -/// `__cxa_atexit` to register their destructor functions. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Utils/ModuleUtils.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-lower-global-dtors" - -namespace { -class LowerGlobalDtors final : public ModulePass { - StringRef getPassName() const override { - return "WebAssembly Lower @llvm.global_dtors"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - ModulePass::getAnalysisUsage(AU); - } - - bool runOnModule(Module &M) override; - -public: - static char ID; - LowerGlobalDtors() : ModulePass(ID) {} -}; -} // End anonymous namespace - -char LowerGlobalDtors::ID = 0; -INITIALIZE_PASS(LowerGlobalDtors, DEBUG_TYPE, - "Lower @llvm.global_dtors for WebAssembly", false, false) - -ModulePass *llvm::createWebAssemblyLowerGlobalDtors() { - return new LowerGlobalDtors(); -} - -bool LowerGlobalDtors::runOnModule(Module &M) { - LLVM_DEBUG(dbgs() << "********** Lower Global Destructors **********\n"); - - GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors"); - if (!GV || !GV->hasInitializer()) - return false; - - const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); - if (!InitList) - return false; - - // Sanity-check @llvm.global_dtor's type. - auto *ETy = dyn_cast<StructType>(InitList->getType()->getElementType()); - if (!ETy || ETy->getNumElements() != 3 || - !ETy->getTypeAtIndex(0U)->isIntegerTy() || - !ETy->getTypeAtIndex(1U)->isPointerTy() || - !ETy->getTypeAtIndex(2U)->isPointerTy()) - return false; // Not (int, ptr, ptr). - - // Collect the contents of @llvm.global_dtors, collated by priority and - // associated symbol. - std::map<uint16_t, MapVector<Constant *, std::vector<Constant *>>> DtorFuncs; - for (Value *O : InitList->operands()) { - auto *CS = dyn_cast<ConstantStruct>(O); - if (!CS) - continue; // Malformed. - - auto *Priority = dyn_cast<ConstantInt>(CS->getOperand(0)); - if (!Priority) - continue; // Malformed. - uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX); - - Constant *DtorFunc = CS->getOperand(1); - if (DtorFunc->isNullValue()) - break; // Found a null terminator, skip the rest. - - Constant *Associated = CS->getOperand(2); - Associated = cast<Constant>(Associated->stripPointerCastsNoFollowAliases()); - - DtorFuncs[PriorityValue][Associated].push_back(DtorFunc); - } - if (DtorFuncs.empty()) - return false; - - // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); - LLVMContext &C = M.getContext(); - PointerType *VoidStar = Type::getInt8PtrTy(C); - Type *AtExitFuncArgs[] = {VoidStar}; - FunctionType *AtExitFuncTy = - FunctionType::get(Type::getVoidTy(C), AtExitFuncArgs, - /*isVarArg=*/false); - - FunctionCallee AtExit = M.getOrInsertFunction( - "__cxa_atexit", - FunctionType::get(Type::getInt32Ty(C), - {PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar}, - /*isVarArg=*/false)); - - // Declare __dso_local. - Constant *DsoHandle = M.getNamedValue("__dso_handle"); - if (!DsoHandle) { - Type *DsoHandleTy = Type::getInt8Ty(C); - GlobalVariable *Handle = new GlobalVariable( - M, DsoHandleTy, /*isConstant=*/true, - GlobalVariable::ExternalWeakLinkage, nullptr, "__dso_handle"); - Handle->setVisibility(GlobalVariable::HiddenVisibility); - DsoHandle = Handle; - } - - // For each unique priority level and associated symbol, generate a function - // to call all the destructors at that level, and a function to register the - // first function with __cxa_atexit. - for (auto &PriorityAndMore : DtorFuncs) { - uint16_t Priority = PriorityAndMore.first; - for (auto &AssociatedAndMore : PriorityAndMore.second) { - 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); - BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors); - FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C), - /*isVarArg=*/false); - - for (auto Dtor : AssociatedAndMore.second) - CallInst::Create(VoidVoid, Dtor, "", BB); - ReturnInst::Create(C, BB); - - Function *RegisterCallDtors = Function::Create( - VoidVoid, Function::PrivateLinkage, - "register_call_dtors" + - (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority)) - : Twine()) + - (!Associated->isNullValue() ? (Twine(".") + Associated->getName()) - : Twine()), - &M); - BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors); - BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors); - BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors); - - Value *Null = ConstantPointerNull::get(VoidStar); - Value *Args[] = {CallDtors, Null, DsoHandle}; - Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB); - Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res, - Constant::getNullValue(Res->getType())); - BranchInst::Create(FailBB, RetBB, Cmp, EntryBB); - - // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave. - // This should be very rare, because if the process is running out of - // memory before main has even started, something is wrong. - CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), "", - FailBB); - new UnreachableInst(C, FailBB); - - ReturnInst::Create(C, RetBB); - - // Now register the registration function with @llvm.global_ctors. - appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated); - } - } - - // Now that we've lowered everything, remove @llvm.global_dtors. - GV->eraseFromParent(); - - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp deleted file mode 100644 index 288b991ae2c5..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ /dev/null @@ -1,312 +0,0 @@ -// WebAssemblyMCInstLower.cpp - Convert WebAssembly MachineInstr to an MCInst // -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains code to lower WebAssembly MachineInstrs to their -/// corresponding MCInst records. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyMCInstLower.h" -#include "WebAssemblyAsmPrinter.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblyRuntimeLibcallSignatures.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/IR/Constants.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCExpr.h" -#include "llvm/MC/MCInst.h" -#include "llvm/MC/MCSymbolWasm.h" -#include "llvm/Support/ErrorHandling.h" -#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. -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(); - auto *WasmSym = cast<MCSymbolWasm>(Printer.getSymbol(Global)); - - if (const auto *FuncTy = dyn_cast<FunctionType>(Global->getValueType())) { - const MachineFunction &MF = *MO.getParent()->getParent()->getParent(); - const TargetMachine &TM = MF.getTarget(); - const Function &CurrentFunc = MF.getFunction(); - - SmallVector<MVT, 1> ResultMVTs; - SmallVector<MVT, 4> ParamMVTs; - computeSignatureVTs(FuncTy, CurrentFunc, TM, ParamMVTs, ResultMVTs); - - auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs); - WasmSym->setSignature(Signature.get()); - Printer.addSignature(std::move(Signature)); - WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); - } - - return WasmSym; -} - -MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol( - const MachineOperand &MO) const { - const char *Name = MO.getSymbolName(); - auto *WasmSym = cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name)); - const WebAssemblySubtarget &Subtarget = Printer.getSubtarget(); - - // Except for certain known symbols, all 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 || strcmp(Name, "__tls_base") == 0 || - strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 || - strcmp(Name, "__tls_size") == 0) { - bool Mutable = - strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0; - WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); - WasmSym->setGlobalType(wasm::WasmGlobalType{ - uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64 - : wasm::WASM_TYPE_I32), - Mutable}); - return WasmSym; - } - - SmallVector<wasm::ValType, 4> Returns; - SmallVector<wasm::ValType, 4> 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); - - // 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(const MachineOperand &MO, - MCSymbol *Sym) const { - MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; - unsigned TargetFlags = MO.getTargetFlags(); - - switch (TargetFlags) { - case WebAssemblyII::MO_NO_FLAG: - break; - case WebAssemblyII::MO_GOT: - Kind = MCSymbolRefExpr::VK_GOT; - break; - case WebAssemblyII::MO_MEMORY_BASE_REL: - Kind = MCSymbolRefExpr::VK_WASM_MBREL; - break; - case WebAssemblyII::MO_TABLE_BASE_REL: - Kind = MCSymbolRefExpr::VK_WASM_TBREL; - break; - default: - llvm_unreachable("Unknown target flag on GV operand"); - } - - const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx); - - if (MO.getOffset() != 0) { - const auto *WasmSym = cast<MCSymbolWasm>(Sym); - if (TargetFlags == WebAssemblyII::MO_GOT) - report_fatal_error("GOT symbol references do not support offsets"); - if (WasmSym->isFunction()) - report_fatal_error("Function addresses with offsets not supported"); - if (WasmSym->isGlobal()) - report_fatal_error("Global indexes with offsets not supported"); - if (WasmSym->isEvent()) - report_fatal_error("Event indexes with offsets not supported"); - - Expr = MCBinaryExpr::createAdd( - Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); - } - - return MCOperand::createExpr(Expr); -} - -// Return the WebAssembly type associated with the given register class. -static wasm::ValType getType(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return wasm::ValType::I32; - if (RC == &WebAssembly::I64RegClass) - return wasm::ValType::I64; - if (RC == &WebAssembly::F32RegClass) - 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"); -} - -void WebAssemblyMCInstLower::lower(const MachineInstr *MI, - MCInst &OutMI) const { - OutMI.setOpcode(MI->getOpcode()); - - const MCInstrDesc &Desc = MI->getDesc(); - for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) { - const MachineOperand &MO = MI->getOperand(I); - - MCOperand MCOp; - switch (MO.getType()) { - default: - MI->print(errs()); - llvm_unreachable("unknown operand type"); - case MachineOperand::MO_MachineBasicBlock: - MI->print(errs()); - llvm_unreachable("MachineBasicBlock operand should have been rewritten"); - case MachineOperand::MO_Register: { - // Ignore all implicit register operands. - if (MO.isImplicit()) - continue; - const WebAssemblyFunctionInfo &MFI = - *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>(); - unsigned WAReg = MFI.getWAReg(MO.getReg()); - MCOp = MCOperand::createReg(WAReg); - break; - } - case MachineOperand::MO_Immediate: - if (I < Desc.NumOperands) { - const MCOperandInfo &Info = Desc.OpInfo[I]; - if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) { - MCSymbol *Sym = Printer.createTempSymbol("typeindex"); - - SmallVector<wasm::ValType, 4> Returns; - SmallVector<wasm::ValType, 4> Params; - - const MachineRegisterInfo &MRI = - MI->getParent()->getParent()->getRegInfo(); - for (const MachineOperand &MO : MI->defs()) - Returns.push_back(getType(MRI.getRegClass(MO.getReg()))); - for (const MachineOperand &MO : MI->explicit_uses()) - if (MO.isReg()) - Params.push_back(getType(MRI.getRegClass(MO.getReg()))); - - // call_indirect instructions have a callee operand at the end which - // doesn't count as a param. - if (WebAssembly::isCallIndirect(MI->getOpcode())) - Params.pop_back(); - - auto *WasmSym = cast<MCSymbolWasm>(Sym); - 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( - WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx); - MCOp = MCOperand::createExpr(Expr); - break; - } - } - MCOp = MCOperand::createImm(MO.getImm()); - break; - case MachineOperand::MO_FPImmediate: { - // TODO: MC converts all floating point immediate operands to double. - // This is fine for numeric values, but may cause NaNs to change bits. - const ConstantFP *Imm = MO.getFPImm(); - if (Imm->getType()->isFloatTy()) - MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToFloat()); - else if (Imm->getType()->isDoubleTy()) - MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble()); - else - llvm_unreachable("unknown floating point immediate type"); - break; - } - case MachineOperand::MO_GlobalAddress: - MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); - break; - case MachineOperand::MO_ExternalSymbol: - // The target flag indicates whether this is a symbol for a - // variable or a function. - assert(MO.getTargetFlags() == 0 && - "WebAssembly uses only symbol flags on ExternalSymbols"); - MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); - 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, MO.getMCSymbol()); - 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/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h deleted file mode 100644 index 2c375a01a7f5..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h +++ /dev/null @@ -1,44 +0,0 @@ -//===-- WebAssemblyMCInstLower.h - Lower MachineInstr to MCInst -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file declares the class to lower WebAssembly MachineInstrs to -/// their corresponding MCInst records. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYMCINSTLOWER_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYMCINSTLOWER_H - -#include "llvm/MC/MCInst.h" -#include "llvm/Support/Compiler.h" - -namespace llvm { -class WebAssemblyAsmPrinter; -class MCContext; -class MCSymbol; -class MachineInstr; -class MachineOperand; - -/// This class is used to lower an MachineInstr into an MCInst. -class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower { - MCContext &Ctx; - WebAssemblyAsmPrinter &Printer; - - MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; - MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; - MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; - -public: - WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer) - : Ctx(ctx), Printer(printer) {} - void lower(const MachineInstr *MI, MCInst &OutMI) const; -}; -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp deleted file mode 100644 index d31c1226bfdb..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.cpp +++ /dev/null @@ -1,92 +0,0 @@ -//=- WebAssemblyMachineFunctionInfo.cpp - WebAssembly Machine Function Info -=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements WebAssembly-specific per-machine-function -/// information. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblyISelLowering.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/Analysis.h" -using namespace llvm; - -WebAssemblyFunctionInfo::~WebAssemblyFunctionInfo() = default; // anchor. - -void WebAssemblyFunctionInfo::initWARegs() { - assert(WARegs.empty()); - unsigned Reg = UnusedReg; - WARegs.resize(MF.getRegInfo().getNumVirtRegs(), Reg); -} - -void llvm::computeLegalValueVTs(const Function &F, const TargetMachine &TM, - Type *Ty, SmallVectorImpl<MVT> &ValueVTs) { - const DataLayout &DL(F.getParent()->getDataLayout()); - const WebAssemblyTargetLowering &TLI = - *TM.getSubtarget<WebAssemblySubtarget>(F).getTargetLowering(); - SmallVector<EVT, 4> VTs; - ComputeValueVTs(TLI, DL, Ty, VTs); - - for (EVT VT : VTs) { - unsigned NumRegs = TLI.getNumRegisters(F.getContext(), VT); - MVT RegisterVT = TLI.getRegisterType(F.getContext(), VT); - for (unsigned I = 0; I != NumRegs; ++I) - ValueVTs.push_back(RegisterVT); - } -} - -void llvm::computeSignatureVTs(const FunctionType *Ty, const Function &F, - const TargetMachine &TM, - SmallVectorImpl<MVT> &Params, - SmallVectorImpl<MVT> &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(PtrVT); - } - - 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; -} - -yaml::WebAssemblyFunctionInfo::WebAssemblyFunctionInfo( - const llvm::WebAssemblyFunctionInfo &MFI) - : CFGStackified(MFI.isCFGStackified()) {} - -void yaml::WebAssemblyFunctionInfo::mappingImpl(yaml::IO &YamlIO) { - MappingTraits<WebAssemblyFunctionInfo>::mapping(YamlIO, *this); -} - -void WebAssemblyFunctionInfo::initializeBaseYamlFields( - const yaml::WebAssemblyFunctionInfo &YamlMFI) { - CFGStackified = YamlMFI.CFGStackified; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h deleted file mode 100644 index 4b9ba491dee6..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ /dev/null @@ -1,172 +0,0 @@ -// WebAssemblyMachineFunctionInfo.h-WebAssembly machine function info-*- C++ -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file declares WebAssembly-specific per-machine-function -/// information. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYMACHINEFUNCTIONINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYMACHINEFUNCTIONINFO_H - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/BinaryFormat/Wasm.h" -#include "llvm/CodeGen/MIRYamlMapping.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/MC/MCSymbolWasm.h" - -namespace llvm { - -namespace yaml { -struct WebAssemblyFunctionInfo; -} - -/// This class is derived from MachineFunctionInfo and contains private -/// WebAssembly-specific information for each MachineFunction. -class WebAssemblyFunctionInfo final : public MachineFunctionInfo { - MachineFunction &MF; - - std::vector<MVT> Params; - std::vector<MVT> Results; - std::vector<MVT> Locals; - - /// A mapping from CodeGen vreg index to WebAssembly register number. - std::vector<unsigned> WARegs; - - /// A mapping from CodeGen vreg index to a boolean value indicating whether - /// the given register is considered to be "stackified", meaning it has been - /// determined or made to meet the stack requirements: - /// - single use (per path) - /// - single def (per path) - /// - defined and used in LIFO order with other stack registers - BitVector VRegStackified; - - // A virtual register holding the pointer to the vararg buffer for vararg - // functions. It is created and set in TLI::LowerFormalArguments and read by - // TLI::LowerVASTART - unsigned VarargVreg = -1U; - - // A virtual register holding the base pointer for functions that have - // overaligned values on the user stack. - unsigned BasePtrVreg = -1U; - - // Function properties. - bool CFGStackified = false; - -public: - explicit WebAssemblyFunctionInfo(MachineFunction &MF) : MF(MF) {} - ~WebAssemblyFunctionInfo() override; - void initializeBaseYamlFields(const yaml::WebAssemblyFunctionInfo &YamlMFI); - - void addParam(MVT VT) { Params.push_back(VT); } - const std::vector<MVT> &getParams() const { return Params; } - - void addResult(MVT VT) { Results.push_back(VT); } - const std::vector<MVT> &getResults() const { return Results; } - - 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; } - void addLocal(MVT VT) { Locals.push_back(VT); } - const std::vector<MVT> &getLocals() const { return Locals; } - - unsigned getVarargBufferVreg() const { - assert(VarargVreg != -1U && "Vararg vreg hasn't been set"); - return VarargVreg; - } - void setVarargBufferVreg(unsigned Reg) { VarargVreg = Reg; } - - unsigned getBasePointerVreg() const { - assert(BasePtrVreg != -1U && "Base ptr vreg hasn't been set"); - return BasePtrVreg; - } - void setBasePointerVreg(unsigned Reg) { BasePtrVreg = Reg; } - - static const unsigned UnusedReg = -1u; - - void stackifyVReg(unsigned VReg) { - assert(MF.getRegInfo().getUniqueVRegDef(VReg)); - auto I = TargetRegisterInfo::virtReg2Index(VReg); - if (I >= VRegStackified.size()) - VRegStackified.resize(I + 1); - VRegStackified.set(I); - } - bool isVRegStackified(unsigned VReg) const { - auto I = TargetRegisterInfo::virtReg2Index(VReg); - if (I >= VRegStackified.size()) - return false; - return VRegStackified.test(I); - } - - void initWARegs(); - void setWAReg(unsigned VReg, unsigned WAReg) { - assert(WAReg != UnusedReg); - auto I = TargetRegisterInfo::virtReg2Index(VReg); - assert(I < WARegs.size()); - WARegs[I] = WAReg; - } - unsigned getWAReg(unsigned VReg) const { - auto I = TargetRegisterInfo::virtReg2Index(VReg); - assert(I < WARegs.size()); - return WARegs[I]; - } - - // For a given stackified WAReg, return the id number to print with push/pop. - static unsigned getWARegStackId(unsigned Reg) { - assert(Reg & INT32_MIN); - return Reg & INT32_MAX; - } - - bool isCFGStackified() const { return CFGStackified; } - void setCFGStackified(bool Value = true) { CFGStackified = Value; } -}; - -void computeLegalValueVTs(const Function &F, const TargetMachine &TM, Type *Ty, - SmallVectorImpl<MVT> &ValueVTs); - -// 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); - -namespace yaml { - -struct WebAssemblyFunctionInfo final : public yaml::MachineFunctionInfo { - bool CFGStackified = false; - - WebAssemblyFunctionInfo() = default; - WebAssemblyFunctionInfo(const llvm::WebAssemblyFunctionInfo &MFI); - - void mappingImpl(yaml::IO &YamlIO) override; - ~WebAssemblyFunctionInfo() = default; -}; - -template <> struct MappingTraits<WebAssemblyFunctionInfo> { - static void mapping(IO &YamlIO, WebAssemblyFunctionInfo &MFI) { - YamlIO.mapOptional("isCFGStackified", MFI.CFGStackified, false); - } -}; - -} // end namespace yaml - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp deleted file mode 100644 index 7ac0511c28b0..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp +++ /dev/null @@ -1,211 +0,0 @@ -//== WebAssemblyMemIntrinsicResults.cpp - Optimize memory intrinsic results ==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements an optimization pass using memory intrinsic results. -/// -/// 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 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. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/CodeGen/LiveIntervals.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-mem-intrinsic-results" - -namespace { -class WebAssemblyMemIntrinsicResults final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyMemIntrinsicResults() : MachineFunctionPass(ID) {} - - StringRef getPassName() const override { - return "WebAssembly Memory Intrinsic Results"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<MachineBlockFrequencyInfo>(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - AU.addRequired<MachineDominatorTree>(); - AU.addPreserved<MachineDominatorTree>(); - AU.addRequired<LiveIntervals>(); - AU.addPreserved<SlotIndexes>(); - AU.addPreserved<LiveIntervals>(); - AU.addRequired<TargetLibraryInfoWrapperPass>(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -private: -}; -} // end anonymous namespace - -char WebAssemblyMemIntrinsicResults::ID = 0; -INITIALIZE_PASS(WebAssemblyMemIntrinsicResults, DEBUG_TYPE, - "Optimize memory intrinsic result values for WebAssembly", - false, false) - -FunctionPass *llvm::createWebAssemblyMemIntrinsicResults() { - return new WebAssemblyMemIntrinsicResults(); -} - -// Replace uses of FromReg with ToReg if they are dominated by MI. -static bool replaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI, - unsigned FromReg, unsigned ToReg, - const MachineRegisterInfo &MRI, - MachineDominatorTree &MDT, - LiveIntervals &LIS) { - bool Changed = false; - - LiveInterval *FromLI = &LIS.getInterval(FromReg); - LiveInterval *ToLI = &LIS.getInterval(ToReg); - - SlotIndex FromIdx = LIS.getInstructionIndex(MI).getRegSlot(); - VNInfo *FromVNI = FromLI->getVNInfoAt(FromIdx); - - SmallVector<SlotIndex, 4> Indices; - - for (auto I = MRI.use_nodbg_begin(FromReg), E = MRI.use_nodbg_end(); - I != E;) { - MachineOperand &O = *I++; - MachineInstr *Where = O.getParent(); - - // Check that MI dominates the instruction in the normal way. - if (&MI == Where || !MDT.dominates(&MI, Where)) - continue; - - // If this use gets a different value, skip it. - SlotIndex WhereIdx = LIS.getInstructionIndex(*Where); - VNInfo *WhereVNI = FromLI->getVNInfoAt(WhereIdx); - if (WhereVNI && WhereVNI != FromVNI) - continue; - - // Make sure ToReg isn't clobbered before it gets there. - VNInfo *ToVNI = ToLI->getVNInfoAt(WhereIdx); - if (ToVNI && ToVNI != FromVNI) - continue; - - Changed = true; - LLVM_DEBUG(dbgs() << "Setting operand " << O << " in " << *Where << " from " - << MI << "\n"); - O.setReg(ToReg); - - // If the store's def was previously dead, it is no longer. - if (!O.isUndef()) { - MI.getOperand(0).setIsDead(false); - - Indices.push_back(WhereIdx.getRegSlot()); - } - } - - if (Changed) { - // Extend ToReg's liveness. - LIS.extendToIndices(*ToLI, Indices); - - // Shrink FromReg's liveness. - LIS.shrinkToUses(FromLI); - - // 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()); - } - - return Changed; -} - -static bool optimizeCall(MachineBasicBlock &MBB, MachineInstr &MI, - const MachineRegisterInfo &MRI, - MachineDominatorTree &MDT, LiveIntervals &LIS, - const WebAssemblyTargetLowering &TLI, - const TargetLibraryInfo &LibInfo) { - MachineOperand &Op1 = MI.getOperand(1); - if (!Op1.isSymbol()) - return false; - - StringRef Name(Op1.getSymbolName()); - bool CallReturnsInput = Name == TLI.getLibcallName(RTLIB::MEMCPY) || - Name == TLI.getLibcallName(RTLIB::MEMMOVE) || - Name == TLI.getLibcallName(RTLIB::MEMSET); - if (!CallReturnsInput) - return false; - - LibFunc Func; - if (!LibInfo.getLibFunc(Name, Func)) - return false; - - unsigned FromReg = MI.getOperand(2).getReg(); - unsigned ToReg = MI.getOperand(0).getReg(); - if (MRI.getRegClass(FromReg) != MRI.getRegClass(ToReg)) - 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 WebAssemblyMemIntrinsicResults::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Memory Intrinsic Results **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - MachineRegisterInfo &MRI = MF.getRegInfo(); - auto &MDT = getAnalysis<MachineDominatorTree>(); - const WebAssemblyTargetLowering &TLI = - *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering(); - const auto &LibInfo = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); - auto &LIS = getAnalysis<LiveIntervals>(); - bool Changed = false; - - // We don't preserve SSA form. - MRI.leaveSSA(); - - assert(MRI.tracksLiveness() && - "MemIntrinsicResults expects liveness tracking"); - - for (auto &MBB : MF) { - LLVM_DEBUG(dbgs() << "Basic Block: " << MBB.getName() << '\n'); - for (auto &MI : MBB) - switch (MI.getOpcode()) { - default: - break; - case WebAssembly::CALL_i32: - case WebAssembly::CALL_i64: - Changed |= optimizeCall(MBB, MI, MRI, MDT, LIS, TLI, LibInfo); - break; - } - } - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp deleted file mode 100644 index 8c7c3305c201..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeLiveIntervals.cpp +++ /dev/null @@ -1,107 +0,0 @@ -//===--- WebAssemblyOptimizeLiveIntervals.cpp - LiveInterval processing ---===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Optimize LiveIntervals for use in a post-RA context. -// -/// LiveIntervals normally runs before register allocation when the code is -/// only recently lowered out of SSA form, so it's uncommon for registers to -/// have multiple defs, and when they do, the defs are usually closely related. -/// Later, after coalescing, tail duplication, and other optimizations, it's -/// more common to see registers with multiple unrelated defs. This pass -/// updates LiveIntervals to distribute the value numbers across separate -/// LiveIntervals. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/LiveIntervals.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-optimize-live-intervals" - -namespace { -class WebAssemblyOptimizeLiveIntervals final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Optimize Live Intervals"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<LiveIntervals>(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - AU.addPreserved<SlotIndexes>(); - AU.addPreserved<LiveIntervals>(); - AU.addPreservedID(LiveVariablesID); - AU.addPreservedID(MachineDominatorsID); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyOptimizeLiveIntervals() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyOptimizeLiveIntervals::ID = 0; -INITIALIZE_PASS(WebAssemblyOptimizeLiveIntervals, DEBUG_TYPE, - "Optimize LiveIntervals for WebAssembly", false, false) - -FunctionPass *llvm::createWebAssemblyOptimizeLiveIntervals() { - return new WebAssemblyOptimizeLiveIntervals(); -} - -bool WebAssemblyOptimizeLiveIntervals::runOnMachineFunction( - MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** Optimize LiveIntervals **********\n" - "********** Function: " - << MF.getName() << '\n'); - - MachineRegisterInfo &MRI = MF.getRegInfo(); - auto &LIS = getAnalysis<LiveIntervals>(); - - // We don't preserve SSA form. - MRI.leaveSSA(); - - assert(MRI.tracksLiveness() && "OptimizeLiveIntervals expects liveness"); - - // Split multiple-VN LiveIntervals into multiple LiveIntervals. - 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)) - continue; - - LIS.splitSeparateComponents(LIS.getInterval(Reg), SplitLIs); - SplitLIs.clear(); - } - - // In PrepareForLiveIntervals, we conservatively inserted IMPLICIT_DEF - // instructions to satisfy LiveIntervals' requirement that all uses be - // dominated by defs. Now that LiveIntervals has computed which of these - // defs are actually needed and which are dead, remove the dead ones. - 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()); - LIS.removeVRegDefAt(LI, LIS.getInstructionIndex(*MI).getRegSlot()); - LIS.RemoveMachineInstrFromMaps(*MI); - MI->eraseFromParent(); - } - } - - return false; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp deleted file mode 100644 index d20352259e07..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyOptimizeReturned.cpp +++ /dev/null @@ -1,83 +0,0 @@ -//===-- WebAssemblyOptimizeReturned.cpp - Optimize "returned" attributes --===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Optimize calls with "returned" attributes for WebAssembly. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/InstVisitor.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-optimize-returned" - -namespace { -class OptimizeReturned final : public FunctionPass, - public InstVisitor<OptimizeReturned> { - StringRef getPassName() const override { - return "WebAssembly Optimize Returned"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<DominatorTreeWrapperPass>(); - AU.addPreserved<DominatorTreeWrapperPass>(); - FunctionPass::getAnalysisUsage(AU); - } - - bool runOnFunction(Function &F) override; - - DominatorTree *DT = nullptr; - -public: - static char ID; - OptimizeReturned() : FunctionPass(ID) {} - - void visitCallSite(CallSite CS); -}; -} // End anonymous namespace - -char OptimizeReturned::ID = 0; -INITIALIZE_PASS(OptimizeReturned, DEBUG_TYPE, - "Optimize calls with \"returned\" attributes for WebAssembly", - false, false) - -FunctionPass *llvm::createWebAssemblyOptimizeReturned() { - return new OptimizeReturned(); -} - -void OptimizeReturned::visitCallSite(CallSite CS) { - for (unsigned I = 0, E = CS.getNumArgOperands(); I < E; ++I) - if (CS.paramHasAttr(I, Attribute::Returned)) { - Instruction *Inst = CS.getInstruction(); - Value *Arg = CS.getArgOperand(I); - // Ignore constants, globals, undef, etc. - if (isa<Constant>(Arg)) - continue; - // Like replaceDominatedUsesWith but using Instruction/Use dominance. - for (auto UI = Arg->use_begin(), UE = Arg->use_end(); UI != UE;) { - Use &U = *UI++; - if (DT->dominates(Inst, U)) - U.set(Inst); - } - } -} - -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/contrib/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp deleted file mode 100644 index e11cdeaa0e79..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp +++ /dev/null @@ -1,217 +0,0 @@ -//===-- WebAssemblyPeephole.cpp - WebAssembly Peephole Optimiztions -------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Late peephole optimizations for WebAssembly. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-peephole" - -static cl::opt<bool> DisableWebAssemblyFallthroughReturnOpt( - "disable-wasm-fallthrough-return-opt", cl::Hidden, - cl::desc("WebAssembly: Disable fallthrough-return optimizations."), - cl::init(false)); - -namespace { -class WebAssemblyPeephole final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly late peephole optimizer"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<TargetLibraryInfoWrapperPass>(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; - WebAssemblyPeephole() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyPeephole::ID = 0; -INITIALIZE_PASS(WebAssemblyPeephole, DEBUG_TYPE, - "WebAssembly peephole optimizations", false, false) - -FunctionPass *llvm::createWebAssemblyPeephole() { - return new WebAssemblyPeephole(); -} - -/// If desirable, rewrite NewReg to a drop register. -static bool maybeRewriteToDrop(unsigned OldReg, unsigned NewReg, - MachineOperand &MO, WebAssemblyFunctionInfo &MFI, - MachineRegisterInfo &MRI) { - bool Changed = false; - if (OldReg == NewReg) { - Changed = true; - unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg)); - MO.setReg(NewReg); - MO.setIsDead(); - MFI.stackifyVReg(NewReg); - } - return Changed; -} - -static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB, - const MachineFunction &MF, - WebAssemblyFunctionInfo &MFI, - MachineRegisterInfo &MRI, - const WebAssemblyInstrInfo &TII, - unsigned FallthroughOpc, - unsigned CopyLocalOpc) { - if (DisableWebAssemblyFallthroughReturnOpt) - return false; - if (&MBB != &MF.back()) - return false; - - MachineBasicBlock::iterator End = MBB.end(); - --End; - assert(End->getOpcode() == WebAssembly::END_FUNCTION); - --End; - if (&MI != &*End) - return false; - - if (FallthroughOpc != WebAssembly::FALLTHROUGH_RETURN_VOID) { - // If the operand isn't stackified, insert a COPY to read the operand and - // stackify it. - MachineOperand &MO = MI.getOperand(0); - unsigned Reg = MO.getReg(); - if (!MFI.isVRegStackified(Reg)) { - unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); - BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(CopyLocalOpc), NewReg) - .addReg(Reg); - MO.setReg(NewReg); - MFI.stackifyVReg(NewReg); - } - } - - // Rewrite the return. - MI.setDesc(TII.get(FallthroughOpc)); - return true; -} - -bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Peephole **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - MachineRegisterInfo &MRI = MF.getRegInfo(); - WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - const WebAssemblyTargetLowering &TLI = - *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering(); - auto &LibInfo = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); - bool Changed = false; - - for (auto &MBB : MF) - for (auto &MI : MBB) - switch (MI.getOpcode()) { - default: - break; - case WebAssembly::CALL_i32: - case WebAssembly::CALL_i64: { - MachineOperand &Op1 = MI.getOperand(1); - if (Op1.isSymbol()) { - StringRef Name(Op1.getSymbolName()); - if (Name == TLI.getLibcallName(RTLIB::MEMCPY) || - Name == TLI.getLibcallName(RTLIB::MEMMOVE) || - Name == TLI.getLibcallName(RTLIB::MEMSET)) { - LibFunc Func; - if (LibInfo.getLibFunc(Name, Func)) { - const auto &Op2 = MI.getOperand(2); - if (!Op2.isReg()) - report_fatal_error("Peephole: call to builtin function with " - "wrong signature, not consuming reg"); - MachineOperand &MO = MI.getOperand(0); - unsigned OldReg = MO.getReg(); - unsigned NewReg = Op2.getReg(); - - if (MRI.getRegClass(NewReg) != MRI.getRegClass(OldReg)) - report_fatal_error("Peephole: call to builtin function with " - "wrong signature, from/to mismatch"); - Changed |= maybeRewriteToDrop(OldReg, NewReg, MO, MFI, MRI); - } - } - } - break; - } - // Optimize away an explicit void return at the end of the function. - case WebAssembly::RETURN_I32: - Changed |= maybeRewriteToFallthrough( - MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32, - WebAssembly::COPY_I32); - break; - case WebAssembly::RETURN_I64: - Changed |= maybeRewriteToFallthrough( - MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64, - WebAssembly::COPY_I64); - break; - case WebAssembly::RETURN_F32: - Changed |= maybeRewriteToFallthrough( - MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32, - WebAssembly::COPY_F32); - break; - case WebAssembly::RETURN_F64: - Changed |= maybeRewriteToFallthrough( - MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64, - WebAssembly::COPY_F64); - break; - case WebAssembly::RETURN_v16i8: - Changed |= maybeRewriteToFallthrough( - MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v16i8, - WebAssembly::COPY_V128); - break; - case WebAssembly::RETURN_v8i16: - Changed |= maybeRewriteToFallthrough( - MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v8i16, - WebAssembly::COPY_V128); - break; - case WebAssembly::RETURN_v4i32: - Changed |= maybeRewriteToFallthrough( - 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, - WebAssembly::INSTRUCTION_LIST_END); - break; - } - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp deleted file mode 100644 index 3bfbf607344d..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp +++ /dev/null @@ -1,127 +0,0 @@ -//===- WebAssemblyPrepareForLiveIntervals.cpp - Prepare for LiveIntervals -===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Fix up code to meet LiveInterval's requirements. -/// -/// Some CodeGen passes don't preserve LiveInterval's requirements, because -/// they run after register allocation and it isn't important. However, -/// WebAssembly runs LiveIntervals in a late pass. This pass transforms code -/// to meet LiveIntervals' requirements; primarily, it ensures that all -/// virtual register uses have definitions (IMPLICIT_DEF definitions if -/// nothing else). -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyUtilities.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-prepare-for-live-intervals" - -namespace { -class WebAssemblyPrepareForLiveIntervals final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyPrepareForLiveIntervals() : MachineFunctionPass(ID) {} - -private: - StringRef getPassName() const override { - return "WebAssembly Prepare For LiveIntervals"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; -}; -} // end anonymous namespace - -char WebAssemblyPrepareForLiveIntervals::ID = 0; -INITIALIZE_PASS(WebAssemblyPrepareForLiveIntervals, DEBUG_TYPE, - "Fix up code for LiveIntervals", false, false) - -FunctionPass *llvm::createWebAssemblyPrepareForLiveIntervals() { - return new WebAssemblyPrepareForLiveIntervals(); -} - -// Test whether the given register has an ARGUMENT def. -static bool hasArgumentDef(unsigned Reg, const MachineRegisterInfo &MRI) { - for (const auto &Def : MRI.def_instructions(Reg)) - if (WebAssembly::isArgument(Def.getOpcode())) - return true; - return false; -} - -bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction( - MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Prepare For LiveIntervals **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - bool Changed = false; - MachineRegisterInfo &MRI = MF.getRegInfo(); - const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - MachineBasicBlock &Entry = *MF.begin(); - - assert(!mustPreserveAnalysisID(LiveIntervalsID) && - "LiveIntervals shouldn't be active yet!"); - - // We don't preserve SSA form. - MRI.leaveSSA(); - - // BranchFolding and perhaps other passes don't preserve IMPLICIT_DEF - // instructions. LiveIntervals requires that all paths to virtual register - // uses provide a definition. Insert IMPLICIT_DEFs in the entry block to - // conservatively satisfy this. - // - // TODO: This is fairly heavy-handed; find a better approach. - // - for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) { - unsigned Reg = TargetRegisterInfo::index2VirtReg(I); - - // Skip unused registers. - if (MRI.use_nodbg_empty(Reg)) - continue; - - // Skip registers that have an ARGUMENT definition. - if (hasArgumentDef(Reg, MRI)) - continue; - - BuildMI(Entry, Entry.begin(), DebugLoc(), - TII.get(WebAssembly::IMPLICIT_DEF), Reg); - Changed = true; - } - - // Move ARGUMENT_* instructions to the top of the entry block, so that their - // liveness reflects the fact that these really are live-in values. - for (auto MII = Entry.begin(), MIE = Entry.end(); MII != MIE;) { - MachineInstr &MI = *MII++; - if (WebAssembly::isArgument(MI.getOpcode())) { - MI.removeFromParent(); - Entry.insert(Entry.begin(), &MI); - } - } - - // Ok, we're now ready to run the LiveIntervals analysis again. - MF.getProperties().set(MachineFunctionProperties::Property::TracksLiveness); - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp deleted file mode 100644 index 6f09c45b6642..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//===-- WebAssemblyRegColoring.cpp - Register coloring --------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements a virtual register coloring pass. -/// -/// WebAssembly doesn't have a fixed number of registers, but it is still -/// desirable to minimize the total number of registers used in each function. -/// -/// This code is modeled after lib/CodeGen/StackSlotColoring.cpp. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "llvm/CodeGen/LiveIntervals.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-reg-coloring" - -namespace { -class WebAssemblyRegColoring final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyRegColoring() : MachineFunctionPass(ID) {} - - StringRef getPassName() const override { - return "WebAssembly Register Coloring"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<LiveIntervals>(); - AU.addRequired<MachineBlockFrequencyInfo>(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - AU.addPreservedID(MachineDominatorsID); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -private: -}; -} // end anonymous namespace - -char WebAssemblyRegColoring::ID = 0; -INITIALIZE_PASS(WebAssemblyRegColoring, DEBUG_TYPE, - "Minimize number of registers used", false, false) - -FunctionPass *llvm::createWebAssemblyRegColoring() { - return new WebAssemblyRegColoring(); -} - -// Compute the total spill weight for VReg. -static float computeWeight(const MachineRegisterInfo *MRI, - const MachineBlockFrequencyInfo *MBFI, - unsigned VReg) { - float Weight = 0.0f; - for (MachineOperand &MO : MRI->reg_nodbg_operands(VReg)) - Weight += LiveIntervals::getSpillWeight(MO.isDef(), MO.isUse(), MBFI, - *MO.getParent()); - return Weight; -} - -bool WebAssemblyRegColoring::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Register Coloring **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - // If there are calls to setjmp or sigsetjmp, don't perform coloring. Virtual - // registers could be modified before the longjmp is executed, resulting in - // the wrong value being used afterwards. (See <rdar://problem/8007500>.) - // TODO: Does WebAssembly need to care about setjmp for register coloring? - if (MF.exposesReturnsTwice()) - return false; - - MachineRegisterInfo *MRI = &MF.getRegInfo(); - LiveIntervals *Liveness = &getAnalysis<LiveIntervals>(); - const MachineBlockFrequencyInfo *MBFI = - &getAnalysis<MachineBlockFrequencyInfo>(); - WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - - // Gather all register intervals into a list and sort them. - unsigned NumVRegs = MRI->getNumVirtRegs(); - SmallVector<LiveInterval *, 0> SortedIntervals; - SortedIntervals.reserve(NumVRegs); - - LLVM_DEBUG(dbgs() << "Interesting register intervals:\n"); - for (unsigned I = 0; I < NumVRegs; ++I) { - unsigned VReg = TargetRegisterInfo::index2VirtReg(I); - if (MFI.isVRegStackified(VReg)) - continue; - // Skip unused registers, which can use $drop. - if (MRI->use_empty(VReg)) - continue; - - LiveInterval *LI = &Liveness->getInterval(VReg); - assert(LI->weight == 0.0f); - LI->weight = computeWeight(MRI, MBFI, VReg); - LLVM_DEBUG(LI->dump()); - SortedIntervals.push_back(LI); - } - LLVM_DEBUG(dbgs() << '\n'); - - // Sort them to put arguments first (since we don't want to rename live-in - // 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, [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); - SmallVector<SmallVector<LiveInterval *, 4>, 16> Assignments( - SortedIntervals.size()); - BitVector UsedColors(SortedIntervals.size()); - bool Changed = false; - for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { - LiveInterval *LI = SortedIntervals[I]; - unsigned Old = LI->reg; - size_t Color = I; - const TargetRegisterClass *RC = MRI->getRegClass(Old); - - // Check if it's possible to reuse any of the used colors. - if (!MRI->isLiveIn(Old)) - for (unsigned C : UsedColors.set_bits()) { - if (MRI->getRegClass(SortedIntervals[C]->reg) != RC) - continue; - for (LiveInterval *OtherLI : Assignments[C]) - if (!OtherLI->empty() && OtherLI->overlaps(*LI)) - goto continue_outer; - Color = C; - break; - continue_outer:; - } - - unsigned New = SortedIntervals[Color]->reg; - SlotMapping[I] = New; - Changed |= Old != New; - UsedColors.set(Color); - Assignments[Color].push_back(LI); - LLVM_DEBUG( - dbgs() << "Assigning vreg" << TargetRegisterInfo::virtReg2Index(LI->reg) - << " to vreg" << TargetRegisterInfo::virtReg2Index(New) << "\n"); - } - if (!Changed) - return false; - - // Rewrite register operands. - for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { - unsigned Old = SortedIntervals[I]->reg; - unsigned New = SlotMapping[I]; - if (Old != New) - MRI->replaceRegWith(Old, New); - } - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp deleted file mode 100644 index cdca23f55b29..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp +++ /dev/null @@ -1,110 +0,0 @@ -//===-- WebAssemblyRegNumbering.cpp - Register Numbering ------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements a pass which assigns WebAssembly register -/// numbers for CodeGen virtual registers. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "WebAssemblyUtilities.h" -#include "llvm/ADT/SCCIterator.h" -#include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineLoopInfo.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-reg-numbering" - -namespace { -class WebAssemblyRegNumbering final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Register Numbering"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyRegNumbering() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyRegNumbering::ID = 0; -INITIALIZE_PASS(WebAssemblyRegNumbering, DEBUG_TYPE, - "Assigns WebAssembly register numbers for virtual registers", - false, false) - -FunctionPass *llvm::createWebAssemblyRegNumbering() { - return new WebAssemblyRegNumbering(); -} - -bool WebAssemblyRegNumbering::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** Register Numbering **********\n" - "********** Function: " - << MF.getName() << '\n'); - - WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - MachineRegisterInfo &MRI = MF.getRegInfo(); - - MFI.initWARegs(); - - // WebAssembly argument registers are in the same index space as local - // variables. Assign the numbers for them first. - MachineBasicBlock &EntryMBB = MF.front(); - for (MachineInstr &MI : EntryMBB) { - if (!WebAssembly::isArgument(MI.getOpcode())) - break; - - int64_t Imm = MI.getOperand(1).getImm(); - LLVM_DEBUG(dbgs() << "Arg VReg " << MI.getOperand(0).getReg() - << " -> WAReg " << Imm << "\n"); - MFI.setWAReg(MI.getOperand(0).getReg(), Imm); - } - - // Then assign regular WebAssembly registers for all remaining used - // virtual registers. TODO: Consider sorting the registers by frequency of - // use, to maximize usage of small immediate fields. - unsigned NumVRegs = MF.getRegInfo().getNumVirtRegs(); - unsigned NumStackRegs = 0; - // Start the numbering for locals after the arg regs - unsigned CurReg = MFI.getParams().size(); - for (unsigned VRegIdx = 0; VRegIdx < NumVRegs; ++VRegIdx) { - unsigned VReg = TargetRegisterInfo::index2VirtReg(VRegIdx); - // Skip unused registers. - if (MRI.use_empty(VReg)) - continue; - // Handle stackified registers. - if (MFI.isVRegStackified(VReg)) { - LLVM_DEBUG(dbgs() << "VReg " << VReg << " -> WAReg " - << (INT32_MIN | NumStackRegs) << "\n"); - MFI.setWAReg(VReg, INT32_MIN | NumStackRegs++); - continue; - } - if (MFI.getWAReg(VReg) == WebAssemblyFunctionInfo::UnusedReg) { - LLVM_DEBUG(dbgs() << "VReg " << VReg << " -> WAReg " << CurReg << "\n"); - MFI.setWAReg(VReg, CurReg++); - } - } - - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp deleted file mode 100644 index a120a6471014..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ /dev/null @@ -1,937 +0,0 @@ -//===-- WebAssemblyRegStackify.cpp - Register Stackification --------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements a register stacking pass. -/// -/// This pass reorders instructions to put register uses and defs in an order -/// such that they form single-use expression trees. Registers fitting this form -/// are then marked as "stackified", meaning references to them are replaced by -/// "push" and "pop" from the value stack. -/// -/// This is primarily a code size optimization, since temporary values on the -/// value stack don't need to be named. -/// -//===----------------------------------------------------------------------===// - -#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" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineModuleInfoImpls.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-reg-stackify" - -namespace { -class WebAssemblyRegStackify final : public MachineFunctionPass { - StringRef getPassName() const override { - return "WebAssembly Register Stackify"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired<AAResultsWrapperPass>(); - AU.addRequired<MachineDominatorTree>(); - AU.addRequired<LiveIntervals>(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - AU.addPreserved<SlotIndexes>(); - AU.addPreserved<LiveIntervals>(); - AU.addPreservedID(LiveVariablesID); - AU.addPreserved<MachineDominatorTree>(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyRegStackify() : MachineFunctionPass(ID) {} -}; -} // end anonymous namespace - -char WebAssemblyRegStackify::ID = 0; -INITIALIZE_PASS(WebAssemblyRegStackify, DEBUG_TYPE, - "Reorder instructions to use the WebAssembly value stack", - false, false) - -FunctionPass *llvm::createWebAssemblyRegStackify() { - return new WebAssemblyRegStackify(); -} - -// Decorate the given instruction with implicit operands that enforce the -// expression stack ordering constraints for an instruction which is on -// the expression stack. -static void imposeStackOrdering(MachineInstr *MI) { - // Write the opaque VALUE_STACK register. - if (!MI->definesRegister(WebAssembly::VALUE_STACK)) - MI->addOperand(MachineOperand::CreateReg(WebAssembly::VALUE_STACK, - /*isDef=*/true, - /*isImp=*/true)); - - // Also read the opaque VALUE_STACK register. - if (!MI->readsRegister(WebAssembly::VALUE_STACK)) - MI->addOperand(MachineOperand::CreateReg(WebAssembly::VALUE_STACK, - /*isDef=*/false, - /*isImp=*/true)); -} - -// Convert an IMPLICIT_DEF instruction into an instruction which defines -// a constant zero value. -static void convertImplicitDefToConstZero(MachineInstr *MI, - MachineRegisterInfo &MRI, - const TargetInstrInfo *TII, - MachineFunction &MF, - LiveIntervals &LIS) { - assert(MI->getOpcode() == TargetOpcode::IMPLICIT_DEF); - - 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)); - } else if (RegClass == &WebAssembly::I64RegClass) { - MI->setDesc(TII->get(WebAssembly::CONST_I64)); - MI->addOperand(MachineOperand::CreateImm(0)); - } else if (RegClass == &WebAssembly::F32RegClass) { - MI->setDesc(TII->get(WebAssembly::CONST_F32)); - auto *Val = cast<ConstantFP>(Constant::getNullValue( - Type::getFloatTy(MF.getFunction().getContext()))); - MI->addOperand(MachineOperand::CreateFPImm(Val)); - } else if (RegClass == &WebAssembly::F64RegClass) { - MI->setDesc(TII->get(WebAssembly::CONST_F64)); - auto *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"); - } -} - -// Determine whether a call to the callee referenced by -// MI->getOperand(CalleeOpNo) reads memory, writes memory, and/or has side -// effects. -static void queryCallee(const MachineInstr &MI, unsigned CalleeOpNo, bool &Read, - bool &Write, bool &Effects, bool &StackPointer) { - // All calls can use the stack pointer. - StackPointer = true; - - const MachineOperand &MO = MI.getOperand(CalleeOpNo); - if (MO.isGlobal()) { - const Constant *GV = MO.getGlobal(); - if (const auto *GA = dyn_cast<GlobalAlias>(GV)) - if (!GA->isInterposable()) - GV = GA->getAliasee(); - - if (const auto *F = dyn_cast<Function>(GV)) { - if (!F->doesNotThrow()) - Effects = true; - if (F->doesNotAccessMemory()) - return; - if (F->onlyReadsMemory()) { - Read = true; - return; - } - } - } - - // Assume the worst. - Write = true; - Read = true; - Effects = true; -} - -// Determine whether MI reads memory, writes memory, has side effects, -// and/or uses the stack pointer value. -static void query(const MachineInstr &MI, AliasAnalysis &AA, bool &Read, - bool &Write, bool &Effects, bool &StackPointer) { - assert(!MI.isTerminator()); - - if (MI.isDebugInstr() || MI.isPosition()) - return; - - // Check for loads. - if (MI.mayLoad() && !MI.isDereferenceableInvariantLoad(&AA)) - Read = true; - - // Check for stores. - if (MI.mayStore()) { - Write = 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: - // 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 - // of memoperands as having a potential unknown memory reference. - break; - default: - // Record volatile accesses, unless it's a call, as calls are handled - // specially below. - if (!MI.isCall()) { - Write = true; - Effects = true; - } - break; - } - } - - // 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: - // 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 - // to move them because overflow and invalid are Undefined Behavior. - break; - default: - Effects = true; - break; - } - } - - // 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()) { - unsigned CalleeOpNo = WebAssembly::getCalleeOpNo(MI.getOpcode()); - queryCallee(MI, CalleeOpNo, Read, Write, Effects, StackPointer); - } -} - -// Test whether Def is safe and profitable to rematerialize. -static bool shouldRematerialize(const MachineInstr &Def, AliasAnalysis &AA, - const WebAssemblyInstrInfo *TII) { - return Def.isAsCheapAsAMove() && TII->isTriviallyReMaterializable(Def, &AA); -} - -// Identify the definition for this register at this point. This is a -// generalization of MachineRegisterInfo::getUniqueVRegDef that uses -// LiveIntervals to handle complex cases. -static MachineInstr *getVRegDef(unsigned Reg, const MachineInstr *Insert, - const MachineRegisterInfo &MRI, - 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; - - // MRI doesn't know what the Def is. Try asking LIS. - if (const VNInfo *ValNo = LIS.getInterval(Reg).getVNInfoBefore( - LIS.getInstructionIndex(*Insert))) - return LIS.getInstructionFromIndex(ValNo->def); - - return nullptr; -} - -// 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) { - // 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()); - assert(DefVNI); - for (auto &I : MRI.use_nodbg_operands(Reg)) { - const auto &Result = LI.Query(LIS.getInstructionIndex(*I.getParent())); - if (Result.valueIn() == DefVNI) { - if (!Result.isKill()) - return false; - if (HasOne) - return false; - HasOne = true; - } - } - return HasOne; -} - -// Test whether it's safe to move Def to just before Insert. -// TODO: Compute memory dependencies in a way that doesn't require always -// walking the block. -// TODO: Compute memory dependencies in a way that uses AliasAnalysis to be -// more precise. -static bool isSafeToMove(const MachineInstr *Def, const MachineInstr *Insert, - AliasAnalysis &AA, const MachineRegisterInfo &MRI) { - assert(Def->getParent() == Insert->getParent()); - - // 'catch' and 'extract_exception' should be the first instruction of a BB and - // cannot move. - if (Def->getOpcode() == WebAssembly::CATCH || - Def->getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) { - const MachineBasicBlock *MBB = Def->getParent(); - auto NextI = std::next(MachineBasicBlock::const_iterator(Def)); - for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI) - ; - if (NextI != Insert) - return false; - } - - // Check for register dependencies. - SmallVector<unsigned, 4> MutableRegisters; - for (const MachineOperand &MO : Def->operands()) { - if (!MO.isReg() || MO.isUndef()) - continue; - unsigned Reg = MO.getReg(); - - // If the register is dead here and at Insert, ignore it. - if (MO.isDead() && Insert->definesRegister(Reg) && - !Insert->readsRegister(Reg)) - continue; - - if (TargetRegisterInfo::isPhysicalRegister(Reg)) { - // Ignore ARGUMENTS; it's just used to keep the ARGUMENT_* instructions - // from moving down, and we've already checked for that. - if (Reg == WebAssembly::ARGUMENTS) - continue; - // If the physical register is never modified, ignore it. - if (!MRI.isPhysRegModified(Reg)) - continue; - // Otherwise, it's a physical register with unknown liveness. - return false; - } - - // If one of the operands isn't in SSA form, it has different values at - // different times, and we need to make sure we don't move our use across - // a different def. - if (!MO.isDef() && !MRI.hasOneDef(Reg)) - MutableRegisters.push_back(Reg); - } - - bool Read = false, Write = false, Effects = false, StackPointer = false; - query(*Def, AA, Read, Write, Effects, StackPointer); - - // If the instruction does not access memory and has no side effects, it has - // no additional dependencies. - bool HasMutableRegisters = !MutableRegisters.empty(); - if (!Read && !Write && !Effects && !StackPointer && !HasMutableRegisters) - return true; - - // Scan through the intervening instructions between Def and Insert. - MachineBasicBlock::const_iterator D(Def), I(Insert); - for (--I; I != D; --I) { - bool InterveningRead = false; - bool InterveningWrite = false; - bool InterveningEffects = false; - bool InterveningStackPointer = false; - query(*I, AA, InterveningRead, InterveningWrite, InterveningEffects, - InterveningStackPointer); - if (Effects && InterveningEffects) - return false; - if (Read && InterveningWrite) - return false; - if (Write && (InterveningRead || InterveningWrite)) - return false; - if (StackPointer && InterveningStackPointer) - return false; - - for (unsigned Reg : MutableRegisters) - for (const MachineOperand &MO : I->operands()) - if (MO.isReg() && MO.isDef() && MO.getReg() == Reg) - return false; - } - - return true; -} - -/// Test whether OneUse, a use of Reg, dominates all of Reg's other uses. -static bool oneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse, - const MachineBasicBlock &MBB, - const MachineRegisterInfo &MRI, - const MachineDominatorTree &MDT, - LiveIntervals &LIS, - WebAssemblyFunctionInfo &MFI) { - const LiveInterval &LI = LIS.getInterval(Reg); - - const MachineInstr *OneUseInst = OneUse.getParent(); - VNInfo *OneUseVNI = LI.getVNInfoBefore(LIS.getInstructionIndex(*OneUseInst)); - - for (const MachineOperand &Use : MRI.use_nodbg_operands(Reg)) { - if (&Use == &OneUse) - continue; - - const MachineInstr *UseInst = Use.getParent(); - VNInfo *UseVNI = LI.getVNInfoBefore(LIS.getInstructionIndex(*UseInst)); - - if (UseVNI != OneUseVNI) - continue; - - if (UseInst == OneUseInst) { - // Another use in the same instruction. We need to ensure that the one - // selected use happens "before" it. - if (&OneUse > &Use) - return false; - } else { - // Test that the use is dominated by the one selected use. - while (!MDT.dominates(OneUseInst, UseInst)) { - // 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 local.gets for - // uses and implicit local.sets for defs. - if (UseInst->getDesc().getNumDefs() == 0) - return false; - const MachineOperand &MO = UseInst->getOperand(0); - if (!MO.isReg()) - return false; - unsigned DefReg = MO.getReg(); - if (!TargetRegisterInfo::isVirtualRegister(DefReg) || - !MFI.isVRegStackified(DefReg)) - return false; - assert(MRI.hasOneNonDBGUse(DefReg)); - const MachineOperand &NewUse = *MRI.use_nodbg_begin(DefReg); - const MachineInstr *NewUseInst = NewUse.getParent(); - if (NewUseInst == OneUseInst) { - if (&OneUse > &NewUse) - return false; - break; - } - UseInst = NewUseInst; - } - } - } - return true; -} - -/// Get the appropriate tee opcode for the given register class. -static unsigned getTeeOpcode(const TargetRegisterClass *RC) { - if (RC == &WebAssembly::I32RegClass) - return WebAssembly::TEE_I32; - if (RC == &WebAssembly::I64RegClass) - return WebAssembly::TEE_I64; - if (RC == &WebAssembly::F32RegClass) - return WebAssembly::TEE_F32; - if (RC == &WebAssembly::F64RegClass) - return WebAssembly::TEE_F64; - if (RC == &WebAssembly::V128RegClass) - return WebAssembly::TEE_V128; - llvm_unreachable("Unexpected register class"); -} - -// Shrink LI to its uses, cleaning up LI. -static void shrinkToUses(LiveInterval &LI, LiveIntervals &LIS) { - if (LIS.shrinkToUses(&LI)) { - 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, - 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)) { - // No one else is using this register for anything so we can just stackify - // it in place. - MFI.stackifyVReg(Reg); - } else { - // The register may have unrelated uses or defs; create a new register for - // just our one def and use so that we can stackify it. - unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); - Def->getOperand(0).setReg(NewReg); - Op.setReg(NewReg); - - // Tell LiveIntervals about the new register. - LIS.createAndComputeVirtRegInterval(NewReg); - - // Tell LiveIntervals about the changes to the old register. - LiveInterval &LI = LIS.getInterval(Reg); - LI.removeSegment(LIS.getInstructionIndex(*Def).getRegSlot(), - LIS.getInstructionIndex(*Op.getParent()).getRegSlot(), - /*RemoveDeadValNo=*/true); - - MFI.stackifyVReg(NewReg); - - DefDIs.updateReg(NewReg); - - LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); - } - - imposeStackOrdering(Def); - return Def; -} - -/// A trivially cloneable instruction; clone it and nest the new copy with the -/// current instruction. -static MachineInstr *rematerializeCheapDef( - unsigned Reg, MachineOperand &Op, MachineInstr &Def, MachineBasicBlock &MBB, - MachineBasicBlock::instr_iterator Insert, LiveIntervals &LIS, - WebAssemblyFunctionInfo &MFI, MachineRegisterInfo &MRI, - const WebAssemblyInstrInfo *TII, const WebAssemblyRegisterInfo *TRI) { - 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); - MachineInstr *Clone = &*std::prev(Insert); - LIS.InsertMachineInstrInMaps(*Clone); - LIS.createAndComputeVirtRegInterval(NewReg); - MFI.stackifyVReg(NewReg); - imposeStackOrdering(Clone); - - LLVM_DEBUG(dbgs() << " - Cloned to "; Clone->dump()); - - // Shrink the interval. - bool IsDead = MRI.use_empty(Reg); - if (!IsDead) { - LiveInterval &LI = LIS.getInterval(Reg); - shrinkToUses(LI, LIS); - IsDead = !LI.liveAt(LIS.getInstructionIndex(Def).getDeadSlot()); - } - - // If that was the last use of the original, delete the original. - // Move or clone corresponding DBG_VALUEs to the 'Insert' location. - if (IsDead) { - LLVM_DEBUG(dbgs() << " - Deleting original\n"); - SlotIndex Idx = LIS.getInstructionIndex(Def).getRegSlot(); - LIS.removePhysRegDefAt(WebAssembly::ARGUMENTS, Idx); - LIS.removeInterval(Reg); - LIS.RemoveMachineInstrFromMaps(Def); - Def.eraseFromParent(); - - DefDIs.move(&*Insert); - DefDIs.updateReg(NewReg); - } else { - DefDIs.clone(&*Insert, NewReg); - } - - return Clone; -} - -/// A multiple-use def in the same block with no intervening memory or register -/// dependencies; move the def down, nest it with the current instruction, and -/// insert a tee to satisfy the rest of the uses. As an illustration, rewrite -/// this: -/// -/// Reg = INST ... // Def -/// INST ..., Reg, ... // Insert -/// INST ..., Reg, ... -/// INST ..., Reg, ... -/// -/// to this: -/// -/// DefReg = INST ... // Def (to become the new Insert) -/// TeeReg, Reg = TEE_... DefReg -/// INST ..., TeeReg, ... // Insert -/// INST ..., Reg, ... -/// INST ..., Reg, ... -/// -/// 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, - MachineInstr *Insert, LiveIntervals &LIS, WebAssemblyFunctionInfo &MFI, - 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); - - // Create the Tee and attach the registers. - const auto *RegClass = MRI.getRegClass(Reg); - unsigned TeeReg = MRI.createVirtualRegister(RegClass); - unsigned DefReg = MRI.createVirtualRegister(RegClass); - MachineOperand &DefMO = Def->getOperand(0); - MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(), - TII->get(getTeeOpcode(RegClass)), TeeReg) - .addReg(Reg, RegState::Define) - .addReg(DefReg, getUndefRegState(DefMO.isDead())); - Op.setReg(TeeReg); - DefMO.setReg(DefReg); - 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); - VNInfo *ValNo = LI.getVNInfoAt(DefIdx); - I->start = TeeIdx; - ValNo->def = TeeIdx; - shrinkToUses(LI, LIS); - - // Finish stackifying the new regs. - LIS.createAndComputeVirtRegInterval(TeeReg); - LIS.createAndComputeVirtRegInterval(DefReg); - MFI.stackifyVReg(DefReg); - MFI.stackifyVReg(TeeReg); - 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; -} - -namespace { -/// A stack for walking the tree of instructions being built, visiting the -/// MachineOperands in DFS order. -class TreeWalkerState { - using mop_iterator = MachineInstr::mop_iterator; - using mop_reverse_iterator = std::reverse_iterator<mop_iterator>; - using RangeTy = iterator_range<mop_reverse_iterator>; - SmallVector<RangeTy, 4> Worklist; - -public: - explicit TreeWalkerState(MachineInstr *Insert) { - const iterator_range<mop_iterator> &Range = Insert->explicit_uses(); - if (Range.begin() != Range.end()) - Worklist.push_back(reverse(Range)); - } - - bool done() const { return Worklist.empty(); } - - MachineOperand &pop() { - RangeTy &Range = Worklist.back(); - MachineOperand &Op = *Range.begin(); - Range = drop_begin(Range, 1); - if (Range.begin() == Range.end()) - Worklist.pop_back(); - assert((Worklist.empty() || - Worklist.back().begin() != Worklist.back().end()) && - "Empty ranges shouldn't remain in the worklist"); - return Op; - } - - /// Push Instr's operands onto the stack to be visited. - void pushOperands(MachineInstr *Instr) { - const iterator_range<mop_iterator> &Range(Instr->explicit_uses()); - if (Range.begin() != Range.end()) - Worklist.push_back(reverse(Range)); - } - - /// Some of Instr's operands are on the top of the stack; remove them and - /// re-insert them starting from the beginning (because we've commuted them). - void resetTopOperands(MachineInstr *Instr) { - assert(hasRemainingOperands(Instr) && - "Reseting operands should only be done when the instruction has " - "an operand still on the stack"); - Worklist.back() = reverse(Instr->explicit_uses()); - } - - /// Test whether Instr has operands remaining to be visited at the top of - /// the stack. - bool hasRemainingOperands(const MachineInstr *Instr) const { - if (Worklist.empty()) - return false; - const RangeTy &Range = Worklist.back(); - return Range.begin() != Range.end() && Range.begin()->getParent() == Instr; - } - - /// Test whether the given register is present on the stack, indicating an - /// 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 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) - if (MO.isReg() && MO.getReg() == Reg) - return true; - return false; - } -}; - -/// State to keep track of whether commuting is in flight or whether it's been -/// 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 tentative - /// state where we've commuted the operands of the current instruction and are - /// 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 = false; - bool Declined = false; - - /// During the tentative state, these hold the operand indices of the commuted - /// operands. - unsigned Operand0, Operand1; - -public: - /// Stackification for an operand was not successful due to ordering - /// constraints. If possible, and if we haven't already tried it and declined - /// it, commute Insert's operands and prepare to revisit it. - void maybeCommute(MachineInstr *Insert, TreeWalkerState &TreeWalker, - const WebAssemblyInstrInfo *TII) { - if (TentativelyCommuting) { - assert(!Declined && - "Don't decline commuting until you've finished trying it"); - // Commuting didn't help. Revert it. - TII->commuteInstruction(*Insert, /*NewMI=*/false, Operand0, Operand1); - TentativelyCommuting = false; - Declined = true; - } else if (!Declined && TreeWalker.hasRemainingOperands(Insert)) { - Operand0 = TargetInstrInfo::CommuteAnyOperandIndex; - Operand1 = TargetInstrInfo::CommuteAnyOperandIndex; - if (TII->findCommutedOpIndices(*Insert, Operand0, Operand1)) { - // Tentatively commute the operands and try again. - TII->commuteInstruction(*Insert, /*NewMI=*/false, Operand0, Operand1); - TreeWalker.resetTopOperands(Insert); - TentativelyCommuting = true; - Declined = false; - } - } - } - - /// Stackification for some operand was successful. Reset to the default - /// state. - void reset() { - TentativelyCommuting = false; - Declined = false; - } -}; -} // end anonymous namespace - -bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "********** Register Stackifying **********\n" - "********** Function: " - << MF.getName() << '\n'); - - bool Changed = false; - MachineRegisterInfo &MRI = MF.getRegInfo(); - WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); - const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - const auto *TRI = MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); - AliasAnalysis &AA = getAnalysis<AAResultsWrapperPass>().getAAResults(); - auto &MDT = getAnalysis<MachineDominatorTree>(); - auto &LIS = getAnalysis<LiveIntervals>(); - - // Walk the instructions from the bottom up. Currently we don't look past - // block boundaries, and the blocks aren't ordered so the block visitation - // order isn't significant, but we may want to change this in the future. - for (MachineBasicBlock &MBB : MF) { - // Don't use a range-based for loop, because we modify the list as we're - // iterating over it and the end iterator may change. - for (auto MII = MBB.rbegin(); MII != MBB.rend(); ++MII) { - MachineInstr *Insert = &*MII; - // Don't nest anything inside an inline asm, because we don't have - // constraints for $push inputs. - if (Insert->isInlineAsm()) - continue; - - // Ignore debugging intrinsics. - if (Insert->isDebugValue()) - continue; - - // Iterate through the inputs in reverse order, since we'll be pulling - // operands off the stack in LIFO order. - CommutingState Commuting; - TreeWalkerState TreeWalker(Insert); - while (!TreeWalker.done()) { - MachineOperand &Op = TreeWalker.pop(); - - // We're only interested in explicit virtual register operands. - if (!Op.isReg()) - continue; - - unsigned Reg = Op.getReg(); - assert(Op.isUse() && "explicit_uses() should only iterate over uses"); - assert(!Op.isImplicit() && - "explicit_uses() should only iterate over explicit operands"); - if (TargetRegisterInfo::isPhysicalRegister(Reg)) - continue; - - // Identify the definition for this register at this point. - MachineInstr *Def = getVRegDef(Reg, Insert, MRI, LIS); - if (!Def) - continue; - - // Don't nest an INLINE_ASM def into anything, because we don't have - // constraints for $pop outputs. - if (Def->isInlineAsm()) - continue; - - // Argument instructions represent live-in registers and not real - // instructions. - if (WebAssembly::isArgument(Def->getOpcode())) - continue; - - // Currently catch's return value register cannot be stackified, because - // the wasm LLVM backend currently does not support live-in values - // entering blocks, which is a part of multi-value proposal. - // - // Once we support live-in values of wasm blocks, this can be: - // catch ; push exnref value onto stack - // block exnref -> i32 - // br_on_exn $__cpp_exception ; pop the exnref value - // end_block - // - // But because we don't support it yet, the catch instruction's dst - // register should be assigned to a local to be propagated across - // 'block' boundary now. - // - // TODO Fix this once we support the multi-value proposal. - if (Def->getOpcode() == WebAssembly::CATCH) - continue; - - // Decide which strategy to take. Prefer to move a single-use value - // over cloning it, and prefer cloning over introducing a tee. - // For moving, we require the def to be in the same block as the use; - // this makes things simpler (LiveIntervals' handleMove function only - // supports intra-block moves) and it's MachineSink's job to catch all - // the sinking opportunities anyway. - bool SameBlock = Def->getParent() == &MBB; - bool CanMove = SameBlock && isSafeToMove(Def, Insert, AA, MRI) && - !TreeWalker.isOnStack(Reg); - if (CanMove && hasOneUse(Reg, Def, MRI, MDT, LIS)) { - Insert = moveForSingleUse(Reg, Op, Def, MBB, Insert, LIS, MFI, MRI); - } else if (shouldRematerialize(*Def, AA, TII)) { - Insert = - rematerializeCheapDef(Reg, Op, *Def, MBB, Insert->getIterator(), - LIS, MFI, MRI, TII, TRI); - } else if (CanMove && - oneUseDominatesOtherUses(Reg, Op, MBB, MRI, MDT, LIS, MFI)) { - Insert = moveAndTeeForMultiUse(Reg, Op, Def, MBB, Insert, LIS, MFI, - MRI, TII); - } else { - // We failed to stackify the operand. If the problem was ordering - // constraints, Commuting may be able to help. - if (!CanMove && SameBlock) - Commuting.maybeCommute(Insert, TreeWalker, TII); - // Proceed to the next operand. - continue; - } - - // If the instruction we just stackified is an IMPLICIT_DEF, convert it - // 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, LIS); - - // We stackified an operand. Add the defining instruction's operands to - // the worklist stack now to continue to build an ever deeper tree. - Commuting.reset(); - TreeWalker.pushOperands(Insert); - } - - // If we stackified any operands, skip over the tree to start looking for - // the next instruction we can build a tree on. - if (Insert != &*MII) { - imposeStackOrdering(&*MII); - MII = MachineBasicBlock::iterator(Insert).getReverse(); - Changed = true; - } - } - } - - // If we used VALUE_STACK anywhere, add it to the live-in sets everywhere so - // that it never looks like a use-before-def. - if (Changed) { - MF.getRegInfo().addLiveIn(WebAssembly::VALUE_STACK); - for (MachineBasicBlock &MBB : MF) - MBB.addLiveIn(WebAssembly::VALUE_STACK); - } - -#ifndef NDEBUG - // Verify that pushes and pops are performed in LIFO order. - SmallVector<unsigned, 0> Stack; - for (MachineBasicBlock &MBB : MF) { - for (MachineInstr &MI : MBB) { - if (MI.isDebugInstr()) - continue; - for (MachineOperand &MO : reverse(MI.explicit_operands())) { - if (!MO.isReg()) - continue; - unsigned Reg = MO.getReg(); - - if (MFI.isVRegStackified(Reg)) { - if (MO.isDef()) - Stack.push_back(Reg); - else - assert(Stack.pop_back_val() == Reg && - "Register stack pop should be paired with a push"); - } - } - } - // TODO: Generalize this code to support keeping values on the stack across - // basic block boundaries. - assert(Stack.empty() && - "Register stack pushes and pops should be balanced"); - } -#endif - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp deleted file mode 100644 index ea9cfc00adfd..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp +++ /dev/null @@ -1,150 +0,0 @@ -//===-- WebAssemblyRegisterInfo.cpp - WebAssembly Register Information ----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the WebAssembly implementation of the -/// TargetRegisterInfo class. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyRegisterInfo.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssemblyFrameLowering.h" -#include "WebAssemblyInstrInfo.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#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/Target/TargetOptions.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-reg-info" - -#define GET_REGINFO_TARGET_DESC -#include "WebAssemblyGenRegisterInfo.inc" - -WebAssemblyRegisterInfo::WebAssemblyRegisterInfo(const Triple &TT) - : WebAssemblyGenRegisterInfo(0), TT(TT) {} - -const MCPhysReg * -WebAssemblyRegisterInfo::getCalleeSavedRegs(const MachineFunction *) const { - static const MCPhysReg CalleeSavedRegs[] = {0}; - return CalleeSavedRegs; -} - -BitVector -WebAssemblyRegisterInfo::getReservedRegs(const MachineFunction & /*MF*/) const { - BitVector Reserved(getNumRegs()); - for (auto Reg : {WebAssembly::SP32, WebAssembly::SP64, WebAssembly::FP32, - WebAssembly::FP64}) - Reserved.set(Reg); - return Reserved; -} - -void WebAssemblyRegisterInfo::eliminateFrameIndex( - MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum, - RegScavenger * /*RS*/) const { - assert(SPAdj == 0); - MachineInstr &MI = *II; - - MachineBasicBlock &MBB = *MI.getParent(); - MachineFunction &MF = *MBB.getParent(); - MachineRegisterInfo &MRI = MF.getRegInfo(); - int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); - const MachineFrameInfo &MFI = MF.getFrameInfo(); - int64_t FrameOffset = MFI.getStackSize() + MFI.getObjectOffset(FrameIndex); - - assert(MFI.getObjectSize(FrameIndex) != 0 && - "We assume that variable-sized objects have already been lowered, " - "and don't use FrameIndex operands."); - Register FrameRegister = getFrameRegister(MF); - - // If this is the address operand of a load or store, make it relative to SP - // and fold the frame offset directly in. - unsigned AddrOperandNum = WebAssembly::getNamedOperandIdx( - MI.getOpcode(), WebAssembly::OpName::addr); - if (AddrOperandNum == FIOperandNum) { - unsigned OffsetOperandNum = WebAssembly::getNamedOperandIdx( - MI.getOpcode(), WebAssembly::OpName::off); - assert(FrameOffset >= 0 && MI.getOperand(OffsetOperandNum).getImm() >= 0); - int64_t Offset = MI.getOperand(OffsetOperandNum).getImm() + FrameOffset; - - if (static_cast<uint64_t>(Offset) <= std::numeric_limits<uint32_t>::max()) { - MI.getOperand(OffsetOperandNum).setImm(Offset); - MI.getOperand(FIOperandNum) - .ChangeToRegister(FrameRegister, /*isDef=*/false); - return; - } - } - - // If this is an address being added to a constant, fold the frame offset - // into the constant. - if (MI.getOpcode() == WebAssembly::ADD_I32) { - MachineOperand &OtherMO = MI.getOperand(3 - FIOperandNum); - if (OtherMO.isReg()) { - unsigned OtherMOReg = OtherMO.getReg(); - if (TargetRegisterInfo::isVirtualRegister(OtherMOReg)) { - MachineInstr *Def = MF.getRegInfo().getUniqueVRegDef(OtherMOReg); - // TODO: For now we just opportunistically do this in the case where - // the CONST_I32 happens to have exactly one def and one use. We - // should generalize this to optimize in more cases. - if (Def && Def->getOpcode() == WebAssembly::CONST_I32 && - MRI.hasOneNonDBGUse(Def->getOperand(0).getReg())) { - MachineOperand &ImmMO = Def->getOperand(1); - ImmMO.setImm(ImmMO.getImm() + uint32_t(FrameOffset)); - MI.getOperand(FIOperandNum) - .ChangeToRegister(FrameRegister, /*isDef=*/false); - return; - } - } - } - } - - // Otherwise create an i32.add SP, offset and make it the operand. - const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); - - unsigned FIRegOperand = FrameRegister; - if (FrameOffset) { - // Create i32.add SP, offset and make it the operand. - const TargetRegisterClass *PtrRC = - MRI.getTargetRegisterInfo()->getPointerRegClass(MF); - unsigned OffsetOp = MRI.createVirtualRegister(PtrRC); - BuildMI(MBB, *II, II->getDebugLoc(), TII->get(WebAssembly::CONST_I32), - OffsetOp) - .addImm(FrameOffset); - FIRegOperand = MRI.createVirtualRegister(PtrRC); - BuildMI(MBB, *II, II->getDebugLoc(), TII->get(WebAssembly::ADD_I32), - FIRegOperand) - .addReg(FrameRegister) - .addReg(OffsetOp); - } - MI.getOperand(FIOperandNum).ChangeToRegister(FIRegOperand, /*isDef=*/false); -} - -Register -WebAssemblyRegisterInfo::getFrameRegister(const MachineFunction &MF) const { - static const unsigned Regs[2][2] = { - /* !isArch64Bit isArch64Bit */ - /* !hasFP */ {WebAssembly::SP32, WebAssembly::SP64}, - /* hasFP */ {WebAssembly::FP32, WebAssembly::FP64}}; - const WebAssemblyFrameLowering *TFI = getFrameLowering(MF); - return Regs[TFI->hasFP(MF)][TT.isArch64Bit()]; -} - -const TargetRegisterClass * -WebAssemblyRegisterInfo::getPointerRegClass(const MachineFunction &MF, - unsigned Kind) const { - assert(Kind == 0 && "Only one kind of pointer on WebAssembly"); - if (MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()) - return &WebAssembly::I64RegClass; - return &WebAssembly::I32RegClass; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h deleted file mode 100644 index 7880eb217dbf..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h +++ /dev/null @@ -1,53 +0,0 @@ -// WebAssemblyRegisterInfo.h - WebAssembly Register Information Impl -*- C++ -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the WebAssembly implementation of the -/// WebAssemblyRegisterInfo class. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYREGISTERINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYREGISTERINFO_H - -#define GET_REGINFO_HEADER -#include "WebAssemblyGenRegisterInfo.inc" - -namespace llvm { - -class MachineFunction; -class RegScavenger; -class TargetRegisterClass; -class Triple; - -class WebAssemblyRegisterInfo final : public WebAssemblyGenRegisterInfo { - const Triple &TT; - -public: - explicit WebAssemblyRegisterInfo(const Triple &TT); - - // Code Generation virtual methods. - const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; - BitVector getReservedRegs(const MachineFunction &MF) const override; - void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, - unsigned FIOperandNum, - RegScavenger *RS = nullptr) const override; - - // Debug information queries. - Register getFrameRegister(const MachineFunction &MF) const override; - - const TargetRegisterClass * - getPointerRegClass(const MachineFunction &MF, - unsigned Kind = 0) const override; - // This does not apply to wasm. - const uint32_t *getNoPreservedMask() const override { return nullptr; } -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td deleted file mode 100644 index 6d3d6c723277..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td +++ /dev/null @@ -1,67 +0,0 @@ -//WebAssemblyRegisterInfo.td-Describe the WebAssembly Registers -*- tablegen -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file describes the WebAssembly register classes and some nominal -/// physical registers. -/// -//===----------------------------------------------------------------------===// - -class WebAssemblyReg<string n> : Register<n> { - let Namespace = "WebAssembly"; -} - -class WebAssemblyRegClass<list<ValueType> regTypes, int alignment, dag regList> - : RegisterClass<"WebAssembly", regTypes, alignment, regList>; - -//===----------------------------------------------------------------------===// -// Registers -//===----------------------------------------------------------------------===// - -// Special registers used as the frame and stack pointer. -// -// WebAssembly may someday supports mixed 32-bit and 64-bit heaps in the same -// application, which requires separate width FP and SP. -def FP32 : WebAssemblyReg<"%FP32">; -def FP64 : WebAssemblyReg<"%FP64">; -def SP32 : WebAssemblyReg<"%SP32">; -def SP64 : WebAssemblyReg<"%SP64">; - -// The register allocation framework requires register classes have at least -// one register, so we define a few for the integer / floating point register -// classes since we otherwise don't need a physical register in those classes. -// These are also used a "types" in the generated assembly matcher. -def I32_0 : WebAssemblyReg<"%i32.0">; -def I64_0 : WebAssemblyReg<"%i64.0">; -def F32_0 : WebAssemblyReg<"%f32.0">; -def F64_0 : WebAssemblyReg<"%f64.0">; - -def V128_0: WebAssemblyReg<"%v128">; - -def EXNREF_0 : WebAssemblyReg<"%exnref.0">; - -// The value stack "register". This is an opaque entity which serves to order -// uses and defs that must remain in LIFO order. -def VALUE_STACK : WebAssemblyReg<"STACK">; - -// The incoming arguments "register". This is an opaque entity which serves to -// order the ARGUMENT instructions that are emulating live-in registers and -// must not be scheduled below other instructions. -def ARGUMENTS : WebAssemblyReg<"ARGUMENTS">; - -//===----------------------------------------------------------------------===// -// Register classes -//===----------------------------------------------------------------------===// - -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, v2f64, v2i64, v4i32, v16i8, v8i16], 128, - (add V128_0)>; -def EXNREF : WebAssemblyRegClass<[exnref], 0, (add EXNREF_0)>; diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp deleted file mode 100644 index 5eafd6c54e78..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp +++ /dev/null @@ -1,102 +0,0 @@ -//===-- WebAssemblyReplacePhysRegs.cpp - Replace phys regs with virt regs -===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements a pass that replaces physical registers with -/// virtual registers. -/// -/// LLVM expects certain physical registers, such as a stack pointer. However, -/// WebAssembly doesn't actually have such physical registers. This pass is run -/// once LLVM no longer needs these registers, and replaces them with virtual -/// registers, so they can participate in register stackifying and coloring in -/// the normal way. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-replace-phys-regs" - -namespace { -class WebAssemblyReplacePhysRegs final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblyReplacePhysRegs() : MachineFunctionPass(ID) {} - -private: - StringRef getPassName() const override { - return "WebAssembly Replace Physical Registers"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; -}; -} // end anonymous namespace - -char WebAssemblyReplacePhysRegs::ID = 0; -INITIALIZE_PASS(WebAssemblyReplacePhysRegs, DEBUG_TYPE, - "Replace physical registers with virtual registers", false, - false) - -FunctionPass *llvm::createWebAssemblyReplacePhysRegs() { - return new WebAssemblyReplacePhysRegs(); -} - -bool WebAssemblyReplacePhysRegs::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Replace Physical Registers **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - MachineRegisterInfo &MRI = MF.getRegInfo(); - const auto &TRI = *MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); - bool Changed = false; - - assert(!mustPreserveAnalysisID(LiveIntervalsID) && - "LiveIntervals shouldn't be active yet!"); - // We don't preserve SSA or liveness. - MRI.leaveSSA(); - MRI.invalidateLiveness(); - - for (unsigned PReg = WebAssembly::NoRegister + 1; - PReg < WebAssembly::NUM_TARGET_REGS; ++PReg) { - // Skip fake registers that are never used explicitly. - if (PReg == WebAssembly::VALUE_STACK || PReg == WebAssembly::ARGUMENTS) - continue; - - // 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;) { - MachineOperand &MO = *I++; - if (!MO.isImplicit()) { - if (VReg == WebAssembly::NoRegister) - VReg = MRI.createVirtualRegister(RC); - MO.setReg(VReg); - if (MO.getParent()->isDebugValue()) - MO.setIsDebug(); - Changed = true; - } - } - } - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp deleted file mode 100644 index 7b9ae90326f0..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.cpp +++ /dev/null @@ -1,900 +0,0 @@ -// CodeGen/RuntimeLibcallSignatures.cpp - R.T. Lib. Call Signatures -*- C++ -*-- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains signature information for runtime libcalls. -/// -/// CodeGen uses external symbols, which it refers to by name. The WebAssembly -/// target needs type information for all functions. This file contains a big -/// table providing type signatures for all runtime library functions that LLVM -/// uses. -/// -/// This is currently a fairly heavy-handed solution. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyRuntimeLibcallSignatures.h" -#include "WebAssemblySubtarget.h" -#include "llvm/CodeGen/RuntimeLibcalls.h" -#include "llvm/Support/ManagedStatic.h" - -using namespace llvm; - -namespace { - -enum RuntimeLibcallSignature { - func, - f32_func_f32, - f32_func_f64, - f32_func_i32, - f32_func_i64, - f32_func_i16, - f64_func_f32, - f64_func_f64, - f64_func_i32, - f64_func_i64, - i32_func_f32, - i32_func_f64, - i32_func_i32, - i64_func_f32, - i64_func_f64, - i64_func_i64, - f32_func_f32_f32, - f32_func_f32_i32, - f32_func_i64_i64, - f64_func_f64_f64, - f64_func_f64_i32, - f64_func_i64_i64, - i16_func_f32, - i16_func_f64, - i16_func_i64_i64, - i8_func_i8_i8, - func_f32_iPTR_iPTR, - func_f64_iPTR_iPTR, - i16_func_i16_i16, - i32_func_f32_f32, - i32_func_f64_f64, - i32_func_i32_i32, - i32_func_i32_i32_iPTR, - i64_func_i64_i64, - i64_func_i64_i64_iPTR, - i64_i64_func_f32, - i64_i64_func_f64, - i16_i16_func_i16_i16, - i32_i32_func_i32_i32, - i64_i64_func_i64_i64, - i64_i64_func_i64_i64_i64_i64, - i64_i64_func_i64_i64_i64_i64_iPTR, - i64_i64_i64_i64_func_i64_i64_i64_i64, - i64_i64_func_i64_i64_i32, - iPTR_func_iPTR_i32_iPTR, - iPTR_func_iPTR_iPTR_iPTR, - f32_func_f32_f32_f32, - f64_func_f64_f64_f64, - func_i64_i64_iPTR_iPTR, - func_iPTR_f32, - func_iPTR_f64, - func_iPTR_i32, - func_iPTR_i64, - func_iPTR_i64_i64, - func_iPTR_i64_i64_i64_i64, - func_iPTR_i64_i64_i64_i64_i64_i64, - i32_func_i64_i64, - i32_func_i64_i64_i64_i64, - iPTR_func_f32, - iPTR_func_f64, - iPTR_func_i64_i64, - unsupported -}; - -struct RuntimeLibcallSignatureTable { - std::vector<RuntimeLibcallSignature> Table; - - // Any newly-added libcalls will be unsupported by default. - RuntimeLibcallSignatureTable() : Table(RTLIB::UNKNOWN_LIBCALL, unsupported) { - // Integer - Table[RTLIB::SHL_I16] = i16_func_i16_i16; - Table[RTLIB::SHL_I32] = i32_func_i32_i32; - Table[RTLIB::SHL_I64] = i64_func_i64_i64; - Table[RTLIB::SHL_I128] = i64_i64_func_i64_i64_i32; - Table[RTLIB::SRL_I16] = i16_func_i16_i16; - Table[RTLIB::SRL_I32] = i32_func_i32_i32; - Table[RTLIB::SRL_I64] = i64_func_i64_i64; - Table[RTLIB::SRL_I128] = i64_i64_func_i64_i64_i32; - Table[RTLIB::SRA_I16] = i16_func_i16_i16; - Table[RTLIB::SRA_I32] = i32_func_i32_i32; - Table[RTLIB::SRA_I64] = i64_func_i64_i64; - Table[RTLIB::SRA_I128] = i64_i64_func_i64_i64_i32; - Table[RTLIB::MUL_I8] = i8_func_i8_i8; - Table[RTLIB::MUL_I16] = i16_func_i16_i16; - Table[RTLIB::MUL_I32] = i32_func_i32_i32; - Table[RTLIB::MUL_I64] = i64_func_i64_i64; - Table[RTLIB::MUL_I128] = i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::MULO_I32] = i32_func_i32_i32_iPTR; - Table[RTLIB::MULO_I64] = i64_func_i64_i64_iPTR; - Table[RTLIB::MULO_I128] = i64_i64_func_i64_i64_i64_i64_iPTR; - Table[RTLIB::SDIV_I8] = i8_func_i8_i8; - Table[RTLIB::SDIV_I16] = i16_func_i16_i16; - Table[RTLIB::SDIV_I32] = i32_func_i32_i32; - Table[RTLIB::SDIV_I64] = i64_func_i64_i64; - Table[RTLIB::SDIV_I128] = i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::UDIV_I8] = i8_func_i8_i8; - Table[RTLIB::UDIV_I16] = i16_func_i16_i16; - Table[RTLIB::UDIV_I32] = i32_func_i32_i32; - Table[RTLIB::UDIV_I64] = i64_func_i64_i64; - Table[RTLIB::UDIV_I128] = i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::SREM_I8] = i8_func_i8_i8; - Table[RTLIB::SREM_I16] = i16_func_i16_i16; - Table[RTLIB::SREM_I32] = i32_func_i32_i32; - Table[RTLIB::SREM_I64] = i64_func_i64_i64; - Table[RTLIB::SREM_I128] = i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::UREM_I8] = i8_func_i8_i8; - Table[RTLIB::UREM_I16] = i16_func_i16_i16; - Table[RTLIB::UREM_I32] = i32_func_i32_i32; - Table[RTLIB::UREM_I64] = i64_func_i64_i64; - Table[RTLIB::UREM_I128] = i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::SDIVREM_I8] = i8_func_i8_i8; - Table[RTLIB::SDIVREM_I16] = i16_i16_func_i16_i16; - Table[RTLIB::SDIVREM_I32] = i32_i32_func_i32_i32; - Table[RTLIB::SDIVREM_I64] = i64_func_i64_i64; - Table[RTLIB::SDIVREM_I128] = i64_i64_i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::UDIVREM_I8] = i8_func_i8_i8; - Table[RTLIB::UDIVREM_I16] = i16_i16_func_i16_i16; - Table[RTLIB::UDIVREM_I32] = i32_i32_func_i32_i32; - Table[RTLIB::UDIVREM_I64] = i64_i64_func_i64_i64; - Table[RTLIB::UDIVREM_I128] = i64_i64_i64_i64_func_i64_i64_i64_i64; - Table[RTLIB::NEG_I32] = i32_func_i32; - Table[RTLIB::NEG_I64] = i64_func_i64; - - // Floating-point. - // All F80 and PPCF128 routines are unsupported. - Table[RTLIB::ADD_F32] = f32_func_f32_f32; - Table[RTLIB::ADD_F64] = f64_func_f64_f64; - Table[RTLIB::ADD_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::SUB_F32] = f32_func_f32_f32; - Table[RTLIB::SUB_F64] = f64_func_f64_f64; - Table[RTLIB::SUB_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::MUL_F32] = f32_func_f32_f32; - Table[RTLIB::MUL_F64] = f64_func_f64_f64; - Table[RTLIB::MUL_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::DIV_F32] = f32_func_f32_f32; - Table[RTLIB::DIV_F64] = f64_func_f64_f64; - Table[RTLIB::DIV_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::REM_F32] = f32_func_f32_f32; - Table[RTLIB::REM_F64] = f64_func_f64_f64; - Table[RTLIB::REM_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::FMA_F32] = f32_func_f32_f32_f32; - Table[RTLIB::FMA_F64] = f64_func_f64_f64_f64; - Table[RTLIB::FMA_F128] = func_iPTR_i64_i64_i64_i64_i64_i64; - Table[RTLIB::POWI_F32] = f32_func_f32_i32; - Table[RTLIB::POWI_F64] = f64_func_f64_i32; - Table[RTLIB::POWI_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::SQRT_F32] = f32_func_f32; - Table[RTLIB::SQRT_F64] = f64_func_f64; - Table[RTLIB::SQRT_F128] = func_iPTR_i64_i64; - Table[RTLIB::LOG_F32] = f32_func_f32; - Table[RTLIB::LOG_F64] = f64_func_f64; - Table[RTLIB::LOG_F128] = func_iPTR_i64_i64; - Table[RTLIB::LOG2_F32] = f32_func_f32; - Table[RTLIB::LOG2_F64] = f64_func_f64; - Table[RTLIB::LOG2_F128] = func_iPTR_i64_i64; - Table[RTLIB::LOG10_F32] = f32_func_f32; - Table[RTLIB::LOG10_F64] = f64_func_f64; - Table[RTLIB::LOG10_F128] = func_iPTR_i64_i64; - Table[RTLIB::EXP_F32] = f32_func_f32; - Table[RTLIB::EXP_F64] = f64_func_f64; - Table[RTLIB::EXP_F128] = func_iPTR_i64_i64; - Table[RTLIB::EXP2_F32] = f32_func_f32; - Table[RTLIB::EXP2_F64] = f64_func_f64; - Table[RTLIB::EXP2_F128] = func_iPTR_i64_i64; - Table[RTLIB::SIN_F32] = f32_func_f32; - Table[RTLIB::SIN_F64] = f64_func_f64; - Table[RTLIB::SIN_F128] = func_iPTR_i64_i64; - Table[RTLIB::COS_F32] = f32_func_f32; - Table[RTLIB::COS_F64] = f64_func_f64; - Table[RTLIB::COS_F128] = func_iPTR_i64_i64; - Table[RTLIB::SINCOS_F32] = func_f32_iPTR_iPTR; - Table[RTLIB::SINCOS_F64] = func_f64_iPTR_iPTR; - Table[RTLIB::SINCOS_F128] = func_i64_i64_iPTR_iPTR; - Table[RTLIB::POW_F32] = f32_func_f32_f32; - Table[RTLIB::POW_F64] = f64_func_f64_f64; - Table[RTLIB::POW_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::CEIL_F32] = f32_func_f32; - Table[RTLIB::CEIL_F64] = f64_func_f64; - Table[RTLIB::CEIL_F128] = func_iPTR_i64_i64; - Table[RTLIB::TRUNC_F32] = f32_func_f32; - Table[RTLIB::TRUNC_F64] = f64_func_f64; - Table[RTLIB::TRUNC_F128] = func_iPTR_i64_i64; - Table[RTLIB::RINT_F32] = f32_func_f32; - Table[RTLIB::RINT_F64] = f64_func_f64; - Table[RTLIB::RINT_F128] = func_iPTR_i64_i64; - Table[RTLIB::NEARBYINT_F32] = f32_func_f32; - Table[RTLIB::NEARBYINT_F64] = f64_func_f64; - Table[RTLIB::NEARBYINT_F128] = func_iPTR_i64_i64; - Table[RTLIB::ROUND_F32] = f32_func_f32; - Table[RTLIB::ROUND_F64] = f64_func_f64; - Table[RTLIB::ROUND_F128] = func_iPTR_i64_i64; - Table[RTLIB::LROUND_F32] = iPTR_func_f32; - Table[RTLIB::LROUND_F64] = iPTR_func_f64; - Table[RTLIB::LROUND_F128] = iPTR_func_i64_i64; - Table[RTLIB::LLROUND_F32] = i64_func_f32; - Table[RTLIB::LLROUND_F64] = i64_func_f64; - Table[RTLIB::LLROUND_F128] = i64_func_i64_i64; - Table[RTLIB::LRINT_F32] = iPTR_func_f32; - Table[RTLIB::LRINT_F64] = iPTR_func_f64; - Table[RTLIB::LRINT_F128] = iPTR_func_i64_i64; - Table[RTLIB::LLRINT_F32] = i64_func_f32; - Table[RTLIB::LLRINT_F64] = i64_func_f64; - Table[RTLIB::LLRINT_F128] = i64_func_i64_i64; - Table[RTLIB::FLOOR_F32] = f32_func_f32; - Table[RTLIB::FLOOR_F64] = f64_func_f64; - Table[RTLIB::FLOOR_F128] = func_iPTR_i64_i64; - Table[RTLIB::COPYSIGN_F32] = f32_func_f32_f32; - Table[RTLIB::COPYSIGN_F64] = f64_func_f64_f64; - Table[RTLIB::COPYSIGN_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::FMIN_F32] = f32_func_f32_f32; - Table[RTLIB::FMIN_F64] = f64_func_f64_f64; - Table[RTLIB::FMIN_F128] = func_iPTR_i64_i64_i64_i64; - Table[RTLIB::FMAX_F32] = f32_func_f32_f32; - Table[RTLIB::FMAX_F64] = f64_func_f64_f64; - Table[RTLIB::FMAX_F128] = func_iPTR_i64_i64_i64_i64; - - // Conversion - // All F80 and PPCF128 routines are unsupported. - Table[RTLIB::FPEXT_F64_F128] = func_iPTR_f64; - Table[RTLIB::FPEXT_F32_F128] = func_iPTR_f32; - Table[RTLIB::FPEXT_F32_F64] = f64_func_f32; - Table[RTLIB::FPEXT_F16_F32] = f32_func_i16; - Table[RTLIB::FPROUND_F32_F16] = i16_func_f32; - Table[RTLIB::FPROUND_F64_F16] = i16_func_f64; - Table[RTLIB::FPROUND_F64_F32] = f32_func_f64; - Table[RTLIB::FPROUND_F128_F16] = i16_func_i64_i64; - Table[RTLIB::FPROUND_F128_F32] = f32_func_i64_i64; - Table[RTLIB::FPROUND_F128_F64] = f64_func_i64_i64; - Table[RTLIB::FPTOSINT_F32_I32] = i32_func_f32; - Table[RTLIB::FPTOSINT_F32_I64] = i64_func_f32; - Table[RTLIB::FPTOSINT_F32_I128] = i64_i64_func_f32; - Table[RTLIB::FPTOSINT_F64_I32] = i32_func_f64; - Table[RTLIB::FPTOSINT_F64_I64] = i64_func_f64; - Table[RTLIB::FPTOSINT_F64_I128] = i64_i64_func_f64; - Table[RTLIB::FPTOSINT_F128_I32] = i32_func_i64_i64; - Table[RTLIB::FPTOSINT_F128_I64] = i64_func_i64_i64; - Table[RTLIB::FPTOSINT_F128_I128] = i64_i64_func_i64_i64; - Table[RTLIB::FPTOUINT_F32_I32] = i32_func_f32; - Table[RTLIB::FPTOUINT_F32_I64] = i64_func_f32; - Table[RTLIB::FPTOUINT_F32_I128] = i64_i64_func_f32; - Table[RTLIB::FPTOUINT_F64_I32] = i32_func_f64; - Table[RTLIB::FPTOUINT_F64_I64] = i64_func_f64; - Table[RTLIB::FPTOUINT_F64_I128] = i64_i64_func_f64; - Table[RTLIB::FPTOUINT_F128_I32] = i32_func_i64_i64; - Table[RTLIB::FPTOUINT_F128_I64] = i64_func_i64_i64; - Table[RTLIB::FPTOUINT_F128_I128] = i64_i64_func_i64_i64; - Table[RTLIB::SINTTOFP_I32_F32] = f32_func_i32; - Table[RTLIB::SINTTOFP_I32_F64] = f64_func_i32; - Table[RTLIB::SINTTOFP_I32_F128] = func_iPTR_i32; - Table[RTLIB::SINTTOFP_I64_F32] = f32_func_i64; - Table[RTLIB::SINTTOFP_I64_F64] = f64_func_i64; - Table[RTLIB::SINTTOFP_I64_F128] = func_iPTR_i64; - Table[RTLIB::SINTTOFP_I128_F32] = f32_func_i64_i64; - Table[RTLIB::SINTTOFP_I128_F64] = f64_func_i64_i64; - Table[RTLIB::SINTTOFP_I128_F128] = func_iPTR_i64_i64; - Table[RTLIB::UINTTOFP_I32_F32] = f32_func_i32; - Table[RTLIB::UINTTOFP_I32_F64] = f64_func_i64; - Table[RTLIB::UINTTOFP_I32_F128] = func_iPTR_i32; - Table[RTLIB::UINTTOFP_I64_F32] = f32_func_i64; - Table[RTLIB::UINTTOFP_I64_F64] = f64_func_i64; - Table[RTLIB::UINTTOFP_I64_F128] = func_iPTR_i64; - Table[RTLIB::UINTTOFP_I128_F32] = f32_func_i64_i64; - Table[RTLIB::UINTTOFP_I128_F64] = f64_func_i64_i64; - Table[RTLIB::UINTTOFP_I128_F128] = func_iPTR_i64_i64; - - // Comparison - // ALl F80 and PPCF128 routines are unsupported. - Table[RTLIB::OEQ_F32] = i32_func_f32_f32; - Table[RTLIB::OEQ_F64] = i32_func_f64_f64; - Table[RTLIB::OEQ_F128] = i32_func_i64_i64_i64_i64; - Table[RTLIB::UNE_F32] = i32_func_f32_f32; - Table[RTLIB::UNE_F64] = i32_func_f64_f64; - Table[RTLIB::UNE_F128] = i32_func_i64_i64_i64_i64; - Table[RTLIB::OGE_F32] = i32_func_f32_f32; - Table[RTLIB::OGE_F64] = i32_func_f64_f64; - Table[RTLIB::OGE_F128] = i32_func_i64_i64_i64_i64; - Table[RTLIB::OLT_F32] = i32_func_f32_f32; - Table[RTLIB::OLT_F64] = i32_func_f64_f64; - Table[RTLIB::OLT_F128] = i32_func_i64_i64_i64_i64; - Table[RTLIB::OLE_F32] = i32_func_f32_f32; - Table[RTLIB::OLE_F64] = i32_func_f64_f64; - Table[RTLIB::OLE_F128] = i32_func_i64_i64_i64_i64; - Table[RTLIB::OGT_F32] = i32_func_f32_f32; - Table[RTLIB::OGT_F64] = i32_func_f64_f64; - Table[RTLIB::OGT_F128] = i32_func_i64_i64_i64_i64; - Table[RTLIB::UO_F32] = i32_func_f32_f32; - Table[RTLIB::UO_F64] = i32_func_f64_f64; - Table[RTLIB::UO_F128] = i32_func_i64_i64_i64_i64; - // O_FXX has the weird property that it uses the same libcall name as UO_FXX - // This breaks our name-based lookup. Fortunately only the UO family of - // libcalls appears to be actually used. - Table[RTLIB::O_F32] = unsupported; - Table[RTLIB::O_F64] = unsupported; - Table[RTLIB::O_F128] = unsupported; - - // Memory - Table[RTLIB::MEMCPY] = iPTR_func_iPTR_iPTR_iPTR; - Table[RTLIB::MEMSET] = iPTR_func_iPTR_i32_iPTR; - Table[RTLIB::MEMMOVE] = iPTR_func_iPTR_iPTR_iPTR; - - // __stack_chk_fail - Table[RTLIB::STACKPROTECTOR_CHECK_FAIL] = func; - - // Return address handling - Table[RTLIB::RETURN_ADDRESS] = i32_func_i32; - - // Element-wise Atomic memory - // TODO: Fix these when we implement atomic support - Table[RTLIB::MEMCPY_ELEMENT_UNORDERED_ATOMIC_1] = unsupported; - Table[RTLIB::MEMCPY_ELEMENT_UNORDERED_ATOMIC_2] = unsupported; - Table[RTLIB::MEMCPY_ELEMENT_UNORDERED_ATOMIC_4] = unsupported; - Table[RTLIB::MEMCPY_ELEMENT_UNORDERED_ATOMIC_8] = unsupported; - Table[RTLIB::MEMCPY_ELEMENT_UNORDERED_ATOMIC_16] = unsupported; - Table[RTLIB::MEMMOVE_ELEMENT_UNORDERED_ATOMIC_1] = unsupported; - Table[RTLIB::MEMMOVE_ELEMENT_UNORDERED_ATOMIC_2] = unsupported; - Table[RTLIB::MEMMOVE_ELEMENT_UNORDERED_ATOMIC_4] = unsupported; - Table[RTLIB::MEMMOVE_ELEMENT_UNORDERED_ATOMIC_8] = unsupported; - Table[RTLIB::MEMMOVE_ELEMENT_UNORDERED_ATOMIC_16] = unsupported; - - Table[RTLIB::MEMSET_ELEMENT_UNORDERED_ATOMIC_1] = unsupported; - Table[RTLIB::MEMSET_ELEMENT_UNORDERED_ATOMIC_2] = unsupported; - Table[RTLIB::MEMSET_ELEMENT_UNORDERED_ATOMIC_4] = unsupported; - Table[RTLIB::MEMSET_ELEMENT_UNORDERED_ATOMIC_8] = unsupported; - Table[RTLIB::MEMSET_ELEMENT_UNORDERED_ATOMIC_16] = unsupported; - - // Atomic '__sync_*' libcalls. - // TODO: Fix these when we implement atomic support - Table[RTLIB::SYNC_VAL_COMPARE_AND_SWAP_1] = unsupported; - Table[RTLIB::SYNC_VAL_COMPARE_AND_SWAP_2] = unsupported; - Table[RTLIB::SYNC_VAL_COMPARE_AND_SWAP_4] = unsupported; - Table[RTLIB::SYNC_VAL_COMPARE_AND_SWAP_8] = unsupported; - Table[RTLIB::SYNC_VAL_COMPARE_AND_SWAP_16] = unsupported; - Table[RTLIB::SYNC_LOCK_TEST_AND_SET_1] = unsupported; - Table[RTLIB::SYNC_LOCK_TEST_AND_SET_2] = unsupported; - Table[RTLIB::SYNC_LOCK_TEST_AND_SET_4] = unsupported; - Table[RTLIB::SYNC_LOCK_TEST_AND_SET_8] = unsupported; - Table[RTLIB::SYNC_LOCK_TEST_AND_SET_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_ADD_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_ADD_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_ADD_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_ADD_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_ADD_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_SUB_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_SUB_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_SUB_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_SUB_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_SUB_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_AND_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_AND_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_AND_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_AND_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_AND_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_OR_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_OR_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_OR_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_OR_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_OR_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_XOR_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_XOR_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_XOR_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_XOR_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_XOR_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_NAND_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_NAND_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_NAND_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_NAND_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_NAND_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MAX_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MAX_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MAX_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MAX_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MAX_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMAX_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMAX_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMAX_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMAX_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMAX_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MIN_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MIN_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MIN_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MIN_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_MIN_16] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMIN_1] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMIN_2] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMIN_4] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMIN_8] = unsupported; - Table[RTLIB::SYNC_FETCH_AND_UMIN_16] = unsupported; - - // Atomic '__atomic_*' libcalls. - // TODO: Fix these when we implement atomic support - Table[RTLIB::ATOMIC_LOAD] = unsupported; - Table[RTLIB::ATOMIC_LOAD_1] = unsupported; - Table[RTLIB::ATOMIC_LOAD_2] = unsupported; - Table[RTLIB::ATOMIC_LOAD_4] = unsupported; - Table[RTLIB::ATOMIC_LOAD_8] = unsupported; - Table[RTLIB::ATOMIC_LOAD_16] = unsupported; - - Table[RTLIB::ATOMIC_STORE] = unsupported; - Table[RTLIB::ATOMIC_STORE_1] = unsupported; - Table[RTLIB::ATOMIC_STORE_2] = unsupported; - Table[RTLIB::ATOMIC_STORE_4] = unsupported; - Table[RTLIB::ATOMIC_STORE_8] = unsupported; - Table[RTLIB::ATOMIC_STORE_16] = unsupported; - - Table[RTLIB::ATOMIC_EXCHANGE] = unsupported; - Table[RTLIB::ATOMIC_EXCHANGE_1] = unsupported; - Table[RTLIB::ATOMIC_EXCHANGE_2] = unsupported; - Table[RTLIB::ATOMIC_EXCHANGE_4] = unsupported; - Table[RTLIB::ATOMIC_EXCHANGE_8] = unsupported; - Table[RTLIB::ATOMIC_EXCHANGE_16] = unsupported; - - Table[RTLIB::ATOMIC_COMPARE_EXCHANGE] = unsupported; - Table[RTLIB::ATOMIC_COMPARE_EXCHANGE_1] = unsupported; - Table[RTLIB::ATOMIC_COMPARE_EXCHANGE_2] = unsupported; - Table[RTLIB::ATOMIC_COMPARE_EXCHANGE_4] = unsupported; - Table[RTLIB::ATOMIC_COMPARE_EXCHANGE_8] = unsupported; - Table[RTLIB::ATOMIC_COMPARE_EXCHANGE_16] = unsupported; - - Table[RTLIB::ATOMIC_FETCH_ADD_1] = unsupported; - Table[RTLIB::ATOMIC_FETCH_ADD_2] = unsupported; - Table[RTLIB::ATOMIC_FETCH_ADD_4] = unsupported; - Table[RTLIB::ATOMIC_FETCH_ADD_8] = unsupported; - Table[RTLIB::ATOMIC_FETCH_ADD_16] = unsupported; - - Table[RTLIB::ATOMIC_FETCH_SUB_1] = unsupported; - Table[RTLIB::ATOMIC_FETCH_SUB_2] = unsupported; - Table[RTLIB::ATOMIC_FETCH_SUB_4] = unsupported; - Table[RTLIB::ATOMIC_FETCH_SUB_8] = unsupported; - Table[RTLIB::ATOMIC_FETCH_SUB_16] = unsupported; - - Table[RTLIB::ATOMIC_FETCH_AND_1] = unsupported; - Table[RTLIB::ATOMIC_FETCH_AND_2] = unsupported; - Table[RTLIB::ATOMIC_FETCH_AND_4] = unsupported; - Table[RTLIB::ATOMIC_FETCH_AND_8] = unsupported; - Table[RTLIB::ATOMIC_FETCH_AND_16] = unsupported; - - Table[RTLIB::ATOMIC_FETCH_OR_1] = unsupported; - Table[RTLIB::ATOMIC_FETCH_OR_2] = unsupported; - Table[RTLIB::ATOMIC_FETCH_OR_4] = unsupported; - Table[RTLIB::ATOMIC_FETCH_OR_8] = unsupported; - Table[RTLIB::ATOMIC_FETCH_OR_16] = unsupported; - - Table[RTLIB::ATOMIC_FETCH_XOR_1] = unsupported; - Table[RTLIB::ATOMIC_FETCH_XOR_2] = unsupported; - Table[RTLIB::ATOMIC_FETCH_XOR_4] = unsupported; - Table[RTLIB::ATOMIC_FETCH_XOR_8] = unsupported; - Table[RTLIB::ATOMIC_FETCH_XOR_16] = unsupported; - - Table[RTLIB::ATOMIC_FETCH_NAND_1] = unsupported; - Table[RTLIB::ATOMIC_FETCH_NAND_2] = unsupported; - Table[RTLIB::ATOMIC_FETCH_NAND_4] = unsupported; - Table[RTLIB::ATOMIC_FETCH_NAND_8] = unsupported; - Table[RTLIB::ATOMIC_FETCH_NAND_16] = unsupported; - } -}; - -ManagedStatic<RuntimeLibcallSignatureTable> RuntimeLibcallSignatures; - -// Maps libcall names to their RTLIB::Libcall number. Builds the map in a -// constructor for use with ManagedStatic -struct StaticLibcallNameMap { - StringMap<RTLIB::Libcall> Map; - StaticLibcallNameMap() { - static const std::pair<const char *, RTLIB::Libcall> NameLibcalls[] = { -#define HANDLE_LIBCALL(code, name) {(const char *)name, RTLIB::code}, -#include "llvm/IR/RuntimeLibcalls.def" -#undef HANDLE_LIBCALL - }; - for (const auto &NameLibcall : NameLibcalls) { - if (NameLibcall.first != nullptr && - RuntimeLibcallSignatures->Table[NameLibcall.second] != unsupported) { - assert(Map.find(NameLibcall.first) == Map.end() && - "duplicate libcall names in name map"); - Map[NameLibcall.first] = NameLibcall.second; - } - } - // Override the __gnu_f2h_ieee/__gnu_h2f_ieee names so that the f32 name is - // consistent with the f64 and f128 names. - Map["__extendhfsf2"] = RTLIB::FPEXT_F16_F32; - Map["__truncsfhf2"] = RTLIB::FPROUND_F32_F16; - - Map["emscripten_return_address"] = RTLIB::RETURN_ADDRESS; - } -}; - -} // end anonymous namespace - -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 PtrTy = - Subtarget.hasAddr64() ? wasm::ValType::I64 : wasm::ValType::I32; - - auto &Table = RuntimeLibcallSignatures->Table; - switch (Table[LC]) { - case func: - break; - case f32_func_f32: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - break; - case f32_func_f64: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F64); - break; - case f32_func_i32: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::I32); - break; - case f32_func_i64: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::I64); - break; - case f32_func_i16: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::I32); - break; - case f64_func_f32: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F32); - break; - case f64_func_f64: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - break; - case f64_func_i32: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::I32); - break; - case f64_func_i64: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::I64); - break; - case i32_func_f32: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::F32); - break; - case i32_func_f64: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::F64); - break; - case i32_func_i32: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - break; - case i64_func_f32: - Rets.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::F32); - break; - case i64_func_f64: - Rets.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::F64); - break; - case i64_func_i64: - Rets.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case f32_func_f32_f32: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - break; - case f32_func_f32_i32: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::I32); - break; - case f32_func_i64_i64: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case f64_func_f64_f64: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - break; - case f64_func_f64_i32: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::I32); - break; - case f64_func_i64_i64: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i16_func_f32: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::F32); - break; - case i16_func_f64: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::F64); - break; - case i16_func_i64_i64: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i8_func_i8_i8: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - break; - case func_f32_iPTR_iPTR: - Params.push_back(wasm::ValType::F32); - Params.push_back(PtrTy); - Params.push_back(PtrTy); - break; - case func_f64_iPTR_iPTR: - Params.push_back(wasm::ValType::F64); - Params.push_back(PtrTy); - Params.push_back(PtrTy); - break; - case i16_func_i16_i16: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - break; - case i32_func_f32_f32: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - break; - case i32_func_f64_f64: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - break; - case i32_func_i32_i32: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - break; - case i32_func_i32_i32_iPTR: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - Params.push_back(PtrTy); - break; - case i64_func_i64_i64: - Rets.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i64_func_i64_i64_iPTR: - Rets.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(PtrTy); - break; - case i64_i64_func_f32: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::F32); - break; - case i64_i64_func_f64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::F64); - break; - case i16_i16_func_i16_i16: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I32); - Rets.push_back(wasm::ValType::I32); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - break; - case i32_i32_func_i32_i32: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I32); - Rets.push_back(wasm::ValType::I32); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I32); - break; - case i64_i64_func_i64_i64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i64_i64_func_i64_i64_i64_i64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i64_i64_func_i64_i64_i64_i64_iPTR: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(PtrTy); - break; - case i64_i64_i64_i64_func_i64_i64_i64_i64: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i64_i64_func_i64_i64_i32: -#if 0 // TODO: Enable this when wasm gets multiple-return-value support. - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); - Rets.push_back(wasm::ValType::I64); -#else - Params.push_back(PtrTy); -#endif - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I32); - break; - case iPTR_func_iPTR_i32_iPTR: - Rets.push_back(PtrTy); - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I32); - Params.push_back(PtrTy); - break; - case iPTR_func_iPTR_iPTR_iPTR: - Rets.push_back(PtrTy); - Params.push_back(PtrTy); - Params.push_back(PtrTy); - Params.push_back(PtrTy); - break; - case f32_func_f32_f32_f32: - Rets.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - Params.push_back(wasm::ValType::F32); - break; - case f64_func_f64_f64_f64: - Rets.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - Params.push_back(wasm::ValType::F64); - break; - case func_i64_i64_iPTR_iPTR: - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(PtrTy); - Params.push_back(PtrTy); - break; - case func_iPTR_f32: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::F32); - break; - case func_iPTR_f64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::F64); - break; - case func_iPTR_i32: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I32); - break; - case func_iPTR_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - break; - case func_iPTR_i64_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case func_iPTR_i64_i64_i64_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case func_iPTR_i64_i64_i64_i64_i64_i64: - Params.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i32_func_i64_i64: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case i32_func_i64_i64_i64_i64: - Rets.push_back(wasm::ValType::I32); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case iPTR_func_f32: - Rets.push_back(PtrTy); - Params.push_back(wasm::ValType::F32); - break; - case iPTR_func_f64: - Rets.push_back(PtrTy); - Params.push_back(wasm::ValType::F64); - break; - case iPTR_func_i64_i64: - Rets.push_back(PtrTy); - Params.push_back(wasm::ValType::I64); - Params.push_back(wasm::ValType::I64); - break; - case unsupported: - llvm_unreachable("unsupported runtime library signature"); - } -} - -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::getLibcallSignature(const WebAssemblySubtarget &Subtarget, - const char *Name, - SmallVectorImpl<wasm::ValType> &Rets, - SmallVectorImpl<wasm::ValType> &Params) { - auto &Map = LibcallNameMap->Map; - auto Val = Map.find(Name); -#ifndef NDEBUG - if (Val == Map.end()) { - auto message = std::string("unexpected runtime library name: ") + Name; - llvm_unreachable(message.c_str()); - } -#endif - return getLibcallSignature(Subtarget, Val->second, Rets, Params); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h deleted file mode 100644 index 6ae8aaaba59c..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyRuntimeLibcallSignatures.h +++ /dev/null @@ -1,37 +0,0 @@ -// CodeGen/RuntimeLibcallSignatures.h - R.T. Lib. Call Signatures -*- C++ -*--// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file provides signature information for runtime libcalls. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_RUNTIME_LIBCALL_SIGNATURES_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_RUNTIME_LIBCALL_SIGNATURES_H - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/CodeGen/RuntimeLibcalls.h" - -namespace llvm { - -class WebAssemblySubtarget; - -extern void getLibcallSignature(const WebAssemblySubtarget &Subtarget, - RTLIB::Libcall LC, - 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 - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp deleted file mode 100644 index 890e4b8e4e2a..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//===-- WebAssemblySelectionDAGInfo.cpp - WebAssembly SelectionDAG Info ---===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements the WebAssemblySelectionDAGInfo class. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyTargetMachine.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-selectiondag-info" - -WebAssemblySelectionDAGInfo::~WebAssemblySelectionDAGInfo() = default; // anchor - -SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemcpy( - SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Dst, SDValue Src, - SDValue Size, unsigned Align, bool IsVolatile, bool AlwaysInline, - MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const { - if (!DAG.getMachineFunction() - .getSubtarget<WebAssemblySubtarget>() - .hasBulkMemory()) - return SDValue(); - - SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32); - return DAG.getNode(WebAssemblyISD::MEMORY_COPY, DL, MVT::Other, - {Chain, MemIdx, MemIdx, Dst, Src, - DAG.getZExtOrTrunc(Size, DL, MVT::i32)}); -} - -SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemmove( - SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Op1, SDValue Op2, - SDValue Op3, unsigned Align, bool IsVolatile, - MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const { - return EmitTargetCodeForMemcpy(DAG, DL, Chain, Op1, Op2, Op3, Align, - IsVolatile, false, DstPtrInfo, - SrcPtrInfo); -} - -SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemset( - SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Dst, SDValue Val, - SDValue Size, unsigned Align, bool IsVolatile, - MachinePointerInfo DstPtrInfo) const { - if (!DAG.getMachineFunction() - .getSubtarget<WebAssemblySubtarget>() - .hasBulkMemory()) - return SDValue(); - - SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32); - // Only low byte matters for val argument, so anyext the i8 - return DAG.getNode(WebAssemblyISD::MEMORY_FILL, DL, MVT::Other, Chain, MemIdx, - Dst, DAG.getAnyExtOrTrunc(Val, DL, MVT::i32), - DAG.getZExtOrTrunc(Size, DL, MVT::i32)); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h deleted file mode 100644 index 0b90ece27dff..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.h +++ /dev/null @@ -1,44 +0,0 @@ -//=- WebAssemblySelectionDAGInfo.h - WebAssembly SelectionDAG Info -*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines the WebAssembly subclass for -/// SelectionDAGTargetInfo. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYSELECTIONDAGINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYSELECTIONDAGINFO_H - -#include "llvm/CodeGen/SelectionDAGTargetInfo.h" - -namespace llvm { - -class WebAssemblySelectionDAGInfo final : public SelectionDAGTargetInfo { -public: - ~WebAssemblySelectionDAGInfo() override; - SDValue EmitTargetCodeForMemcpy(SelectionDAG &DAG, const SDLoc &dl, - SDValue Chain, SDValue Op1, SDValue Op2, - SDValue Op3, unsigned Align, bool isVolatile, - bool AlwaysInline, - MachinePointerInfo DstPtrInfo, - MachinePointerInfo SrcPtrInfo) const override; - SDValue EmitTargetCodeForMemmove(SelectionDAG &DAG, const SDLoc &dl, - SDValue Chain, SDValue Op1, SDValue Op2, - SDValue Op3, unsigned Align, bool isVolatile, - MachinePointerInfo DstPtrInfo, - MachinePointerInfo SrcPtrInfo) const override; - SDValue EmitTargetCodeForMemset(SelectionDAG &DAG, const SDLoc &DL, - SDValue Chain, SDValue Op1, SDValue Op2, - SDValue Op3, unsigned Align, bool IsVolatile, - MachinePointerInfo DstPtrInfo) const override; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp deleted file mode 100644 index a249ccf17638..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//=- WebAssemblySetP2AlignOperands.cpp - Set alignments on loads and stores -=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file sets the p2align operands on load and store instructions. -/// -//===----------------------------------------------------------------------===// - -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssembly.h" -#include "WebAssemblyInstrInfo.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineMemOperand.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-set-p2align-operands" - -namespace { -class WebAssemblySetP2AlignOperands final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - WebAssemblySetP2AlignOperands() : MachineFunctionPass(ID) {} - - StringRef getPassName() const override { - return "WebAssembly Set p2align Operands"; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addPreserved<MachineBlockFrequencyInfo>(); - AU.addPreservedID(MachineDominatorsID); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; -}; -} // end anonymous namespace - -char WebAssemblySetP2AlignOperands::ID = 0; -INITIALIZE_PASS(WebAssemblySetP2AlignOperands, DEBUG_TYPE, - "Set the p2align operands for WebAssembly loads and stores", - false, false) - -FunctionPass *llvm::createWebAssemblySetP2AlignOperands() { - return new WebAssemblySetP2AlignOperands(); -} - -static void rewriteP2Align(MachineInstr &MI, unsigned OperandNo) { - assert(MI.getOperand(OperandNo).getImm() == 0 && - "ISel should set p2align operands to 0"); - assert(MI.hasOneMemOperand() && - "Load and store instructions have exactly one mem operand"); - assert((*MI.memoperands_begin())->getSize() == - (UINT64_C(1) << WebAssembly::GetDefaultP2Align(MI.getOpcode())) && - "Default p2align value should be natural"); - assert(MI.getDesc().OpInfo[OperandNo].OperandType == - WebAssembly::OPERAND_P2ALIGN && - "Load and store instructions should have a p2align operand"); - 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()))); - - MI.getOperand(OperandNo).setImm(P2Align); -} - -bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Set p2align Operands **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - bool Changed = false; - - for (auto &MBB : MF) { - for (auto &MI : MBB) { - int16_t P2AlignOpNum = WebAssembly::getNamedOperandIdx( - MI.getOpcode(), WebAssembly::OpName::p2align); - if (P2AlignOpNum != -1) { - rewriteP2Align(MI, P2AlignOpNum); - Changed = true; - } - } - } - - return Changed; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp deleted file mode 100644 index 196a74565285..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//===-- WebAssemblySubtarget.cpp - WebAssembly Subtarget Information ------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements the WebAssembly-specific subclass of -/// TargetSubtarget. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblySubtarget.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "WebAssemblyInstrInfo.h" -#include "llvm/Support/TargetRegistry.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm-subtarget" - -#define GET_SUBTARGETINFO_CTOR -#define GET_SUBTARGETINFO_TARGET_DESC -#include "WebAssemblyGenSubtargetInfo.inc" - -WebAssemblySubtarget & -WebAssemblySubtarget::initializeSubtargetDependencies(StringRef FS) { - // Determine default and user-specified characteristics - - if (CPUString.empty()) - CPUString = "generic"; - - ParseSubtargetFeatures(CPUString, FS); - return *this; -} - -WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT, - const std::string &CPU, - const std::string &FS, - const TargetMachine &TM) - : WebAssemblyGenSubtargetInfo(TT, CPU, FS), CPUString(CPU), - TargetTriple(TT), FrameLowering(), - InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(), - TLInfo(TM, *this) {} - -bool WebAssemblySubtarget::enableAtomicExpand() const { - // If atomics are disabled, atomic ops are lowered instead of expanded - return hasAtomics(); -} - -bool WebAssemblySubtarget::enableMachineScheduler() const { - // Disable the MachineScheduler for now. Even with ShouldTrackPressure set and - // enableMachineSchedDefaultSched overridden, it appears to have an overall - // negative effect for the kinds of register optimizations we're doing. - return false; -} - -bool WebAssemblySubtarget::useAA() const { return true; } diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h deleted file mode 100644 index 8db2120f9834..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h +++ /dev/null @@ -1,115 +0,0 @@ -//=- WebAssemblySubtarget.h - Define Subtarget for the WebAssembly -*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file declares the WebAssembly-specific subclass of -/// TargetSubtarget. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYSUBTARGET_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYSUBTARGET_H - -#include "WebAssemblyFrameLowering.h" -#include "WebAssemblyISelLowering.h" -#include "WebAssemblyInstrInfo.h" -#include "WebAssemblySelectionDAGInfo.h" -#include "llvm/CodeGen/TargetSubtargetInfo.h" -#include <string> - -#define GET_SUBTARGETINFO_ENUM -#define GET_SUBTARGETINFO_HEADER -#include "WebAssemblyGenSubtargetInfo.inc" - -namespace llvm { - -// Defined in WebAssemblyGenSubtargetInfo.inc. -extern const SubtargetFeatureKV - WebAssemblyFeatureKV[WebAssembly::NumSubtargetFeatures]; - -class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo { - enum SIMDEnum { - NoSIMD, - SIMD128, - UnimplementedSIMD128, - } SIMDLevel = NoSIMD; - - bool HasAtomics = false; - bool HasNontrappingFPToInt = false; - bool HasSignExt = false; - bool HasExceptionHandling = false; - bool HasBulkMemory = false; - bool HasMultivalue = false; - bool HasMutableGlobals = false; - bool HasTailCall = false; - - /// String name of used CPU. - std::string CPUString; - - /// What processor and OS we're targeting. - Triple TargetTriple; - - WebAssemblyFrameLowering FrameLowering; - WebAssemblyInstrInfo InstrInfo; - WebAssemblySelectionDAGInfo TSInfo; - WebAssemblyTargetLowering TLInfo; - - /// Initializes using CPUString and the passed in feature string so that we - /// can use initializer lists for subtarget initialization. - WebAssemblySubtarget &initializeSubtargetDependencies(StringRef FS); - -public: - /// This constructor initializes the data members to match that - /// of the specified triple. - WebAssemblySubtarget(const Triple &TT, const std::string &CPU, - const std::string &FS, const TargetMachine &TM); - - const WebAssemblySelectionDAGInfo *getSelectionDAGInfo() const override { - return &TSInfo; - } - const WebAssemblyFrameLowering *getFrameLowering() const override { - return &FrameLowering; - } - const WebAssemblyTargetLowering *getTargetLowering() const override { - return &TLInfo; - } - const WebAssemblyInstrInfo *getInstrInfo() const override { - return &InstrInfo; - } - const WebAssemblyRegisterInfo *getRegisterInfo() const override { - return &getInstrInfo()->getRegisterInfo(); - } - const Triple &getTargetTriple() const { return TargetTriple; } - bool enableAtomicExpand() const override; - bool enableIndirectBrExpand() const override { return true; } - bool enableMachineScheduler() const override; - bool useAA() const override; - - // Predicates used by WebAssemblyInstrInfo.td. - bool hasAddr64() const { return TargetTriple.isArch64Bit(); } - 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; } - bool hasExceptionHandling() const { return HasExceptionHandling; } - bool hasBulkMemory() const { return HasBulkMemory; } - bool hasMultivalue() const { return HasMultivalue; } - bool hasMutableGlobals() const { return HasMutableGlobals; } - bool hasTailCall() const { return HasTailCall; } - - /// Parses features string setting specified subtarget options. Definition of - /// function is auto generated by tblgen. - void ParseSubtargetFeatures(StringRef CPU, StringRef FS); -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp deleted file mode 100644 index 7e65368e671a..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ /dev/null @@ -1,507 +0,0 @@ -//===- WebAssemblyTargetMachine.cpp - Define TargetMachine for WebAssembly -==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines the WebAssembly-specific subclass of TargetMachine. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyTargetMachine.h" -#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" -#include "TargetInfo/WebAssemblyTargetInfo.h" -#include "WebAssembly.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "WebAssemblyTargetObjectFile.h" -#include "WebAssemblyTargetTransformInfo.h" -#include "llvm/CodeGen/MIRParser/MIParser.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/CodeGen/RegAllocRegistry.h" -#include "llvm/CodeGen/TargetPassConfig.h" -#include "llvm/IR/Function.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Target/TargetOptions.h" -#include "llvm/Transforms/Scalar.h" -#include "llvm/Transforms/Scalar/LowerAtomic.h" -#include "llvm/Transforms/Utils.h" -using namespace llvm; - -#define DEBUG_TYPE "wasm" - -// Emscripten's asm.js-style exception handling -static cl::opt<bool> EnableEmException( - "enable-emscripten-cxx-exceptions", - cl::desc("WebAssembly Emscripten-style exception handling"), - cl::init(false)); - -// Emscripten's asm.js-style setjmp/longjmp handling -static cl::opt<bool> EnableEmSjLj( - "enable-emscripten-sjlj", - cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), - cl::init(false)); - -extern "C" void LLVMInitializeWebAssemblyTarget() { - // Register the target. - RegisterTargetMachine<WebAssemblyTargetMachine> X( - getTheWebAssemblyTarget32()); - RegisterTargetMachine<WebAssemblyTargetMachine> Y( - getTheWebAssemblyTarget64()); - - // Register backend passes - auto &PR = *PassRegistry::getPassRegistry(); - initializeWebAssemblyAddMissingPrototypesPass(PR); - initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR); - initializeLowerGlobalDtorsPass(PR); - initializeFixFunctionBitcastsPass(PR); - initializeOptimizeReturnedPass(PR); - initializeWebAssemblyArgumentMovePass(PR); - initializeWebAssemblySetP2AlignOperandsPass(PR); - initializeWebAssemblyReplacePhysRegsPass(PR); - initializeWebAssemblyPrepareForLiveIntervalsPass(PR); - initializeWebAssemblyOptimizeLiveIntervalsPass(PR); - initializeWebAssemblyMemIntrinsicResultsPass(PR); - initializeWebAssemblyRegStackifyPass(PR); - initializeWebAssemblyRegColoringPass(PR); - initializeWebAssemblyFixIrreducibleControlFlowPass(PR); - initializeWebAssemblyLateEHPreparePass(PR); - initializeWebAssemblyExceptionInfoPass(PR); - initializeWebAssemblyCFGSortPass(PR); - initializeWebAssemblyCFGStackifyPass(PR); - initializeWebAssemblyExplicitLocalsPass(PR); - initializeWebAssemblyLowerBrUnlessPass(PR); - initializeWebAssemblyRegNumberingPass(PR); - initializeWebAssemblyPeepholePass(PR); - initializeWebAssemblyCallIndirectFixupPass(PR); -} - -//===----------------------------------------------------------------------===// -// WebAssembly Lowering public interface. -//===----------------------------------------------------------------------===// - -static Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM, - const Triple &TT) { - 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; - } - - if (!TT.isOSEmscripten()) { - // Relocation modes other than static are currently implemented in a way - // that only works for Emscripten, so disable them if we aren't targeting - // Emscripten. - return Reloc::Static; - } - - return *RM; -} - -/// Create an WebAssembly architecture model. -/// -WebAssemblyTargetMachine::WebAssemblyTargetMachine( - const Target &T, const Triple &TT, StringRef CPU, StringRef FS, - const TargetOptions &Options, Optional<Reloc::Model> RM, - Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT) - : LLVMTargetMachine(T, - 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, TT), - 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 - // LLVM 'unreachable' to ISD::TRAP and then lower that to WebAssembly's - // 'unreachable' instructions which is meant for that case. - this->Options.TrapUnreachable = true; - - // WebAssembly treats each function as an independent unit. Force - // -ffunction-sections, effectively, so that we can emit them independently. - this->Options.FunctionSections = true; - this->Options.DataSections = true; - this->Options.UniqueSectionNames = true; - - initAsmInfo(); - - // Note that we don't use setRequiresStructuredCFG(true). It disables - // optimizations than we're ok with, and want, such as critical edge - // splitting and tail merging. -} - -WebAssemblyTargetMachine::~WebAssemblyTargetMachine() = default; // anchor. - -const WebAssemblySubtarget * -WebAssemblyTargetMachine::getSubtargetImpl(std::string CPU, - std::string FS) const { - auto &I = SubtargetMap[CPU + FS]; - if (!I) { - I = llvm::make_unique<WebAssemblySubtarget>(TargetTriple, CPU, FS, *this); - } - return I.get(); -} - -const WebAssemblySubtarget * -WebAssemblyTargetMachine::getSubtargetImpl(const Function &F) const { - Attribute CPUAttr = F.getFnAttribute("target-cpu"); - Attribute FSAttr = F.getFnAttribute("target-features"); - - std::string CPU = !CPUAttr.hasAttribute(Attribute::None) - ? CPUAttr.getValueAsString().str() - : TargetCPU; - std::string FS = !FSAttr.hasAttribute(Attribute::None) - ? FSAttr.getValueAsString().str() - : TargetFS; - - // This needs to be done before we create a new subtarget since any - // creation will depend on the TM and the code generation flags on the - // function that reside in TargetOptions. - resetTargetOptions(F); - - return getSubtargetImpl(CPU, FS); -} - -namespace { - -class CoalesceFeaturesAndStripAtomics final : public ModulePass { - // Take the union of all features used in the module and use it for each - // function individually, since having multiple feature sets in one module - // currently does not make sense for WebAssembly. If atomics are not enabled, - // also strip atomic operations and thread local storage. - static char ID; - WebAssemblyTargetMachine *WasmTM; - -public: - CoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine *WasmTM) - : ModulePass(ID), WasmTM(WasmTM) {} - - bool runOnModule(Module &M) override { - FeatureBitset Features = coalesceFeatures(M); - - std::string FeatureStr = getFeatureString(Features); - for (auto &F : M) - replaceFeatures(F, FeatureStr); - - bool StrippedAtomics = false; - bool StrippedTLS = false; - - if (!Features[WebAssembly::FeatureAtomics]) - StrippedAtomics = stripAtomics(M); - - if (!Features[WebAssembly::FeatureBulkMemory]) - StrippedTLS = stripThreadLocals(M); - - if (StrippedAtomics && !StrippedTLS) - stripThreadLocals(M); - else if (StrippedTLS && !StrippedAtomics) - stripAtomics(M); - - recordFeatures(M, Features, StrippedAtomics || StrippedTLS); - - // Conservatively assume we have made some change - return true; - } - -private: - FeatureBitset coalesceFeatures(const Module &M) { - FeatureBitset Features = - WasmTM - ->getSubtargetImpl(WasmTM->getTargetCPU(), - WasmTM->getTargetFeatureString()) - ->getFeatureBits(); - for (auto &F : M) - Features |= WasmTM->getSubtargetImpl(F)->getFeatureBits(); - return Features; - } - - std::string getFeatureString(const FeatureBitset &Features) { - std::string Ret; - for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { - if (Features[KV.Value]) - Ret += (StringRef("+") + KV.Key + ",").str(); - } - return Ret; - } - - void replaceFeatures(Function &F, const std::string &Features) { - F.removeFnAttr("target-features"); - F.removeFnAttr("target-cpu"); - F.addFnAttr("target-features", Features); - } - - bool stripAtomics(Module &M) { - // Detect whether any atomics will be lowered, since there is no way to tell - // whether the LowerAtomic pass lowers e.g. stores. - bool Stripped = false; - for (auto &F : M) { - for (auto &B : F) { - for (auto &I : B) { - if (I.isAtomic()) { - Stripped = true; - goto done; - } - } - } - } - - done: - if (!Stripped) - return false; - - LowerAtomicPass Lowerer; - FunctionAnalysisManager FAM; - for (auto &F : M) - Lowerer.run(F, FAM); - - return true; - } - - bool stripThreadLocals(Module &M) { - bool Stripped = false; - for (auto &GV : M.globals()) { - if (GV.getThreadLocalMode() != - GlobalValue::ThreadLocalMode::NotThreadLocal) { - Stripped = true; - GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal); - } - } - return Stripped; - } - - void recordFeatures(Module &M, const FeatureBitset &Features, bool Stripped) { - for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { - std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str(); - if (KV.Value == WebAssembly::FeatureAtomics && Stripped) { - // "atomics" is special: code compiled without atomics may have had its - // atomics lowered to nonatomic operations. In that case, atomics is - // disallowed to prevent unsafe linking with atomics-enabled objects. - assert(!Features[WebAssembly::FeatureAtomics] || - !Features[WebAssembly::FeatureBulkMemory]); - M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey, - wasm::WASM_FEATURE_PREFIX_DISALLOWED); - } else if (Features[KV.Value]) { - // Otherwise features are marked Used or not mentioned - M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey, - wasm::WASM_FEATURE_PREFIX_USED); - } - } - } -}; -char CoalesceFeaturesAndStripAtomics::ID = 0; - -/// WebAssembly Code Generator Pass Configuration Options. -class WebAssemblyPassConfig final : public TargetPassConfig { -public: - WebAssemblyPassConfig(WebAssemblyTargetMachine &TM, PassManagerBase &PM) - : TargetPassConfig(TM, PM) {} - - WebAssemblyTargetMachine &getWebAssemblyTargetMachine() const { - return getTM<WebAssemblyTargetMachine>(); - } - - FunctionPass *createTargetRegisterAllocator(bool) override; - - void addIRPasses() override; - bool addInstSelector() override; - void addPostRegAlloc() override; - bool addGCPasses() override { return false; } - void addPreEmitPass() override; - - // No reg alloc - bool addRegAssignmentFast() override { return false; } - - // No reg alloc - bool addRegAssignmentOptimized() override { return false; } -}; -} // end anonymous namespace - -TargetTransformInfo -WebAssemblyTargetMachine::getTargetTransformInfo(const Function &F) { - return TargetTransformInfo(WebAssemblyTTIImpl(this, F)); -} - -TargetPassConfig * -WebAssemblyTargetMachine::createPassConfig(PassManagerBase &PM) { - return new WebAssemblyPassConfig(*this, PM); -} - -FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) { - return nullptr; // No reg alloc -} - -//===----------------------------------------------------------------------===// -// The following functions are called from lib/CodeGen/Passes.cpp to modify -// the CodeGen pass sequence. -//===----------------------------------------------------------------------===// - -void WebAssemblyPassConfig::addIRPasses() { - // Runs LowerAtomicPass if necessary - addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine())); - - // This is a no-op if atomics are not used in the module - addPass(createAtomicExpandPass()); - - // Add signatures to prototype-less function declarations - addPass(createWebAssemblyAddMissingPrototypes()); - - // Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls. - addPass(createWebAssemblyLowerGlobalDtors()); - - // Fix function bitcasts, as WebAssembly requires caller and callee signatures - // to match. - addPass(createWebAssemblyFixFunctionBitcasts()); - - // Optimize "returned" function attributes. - if (getOptLevel() != CodeGenOpt::None) - addPass(createWebAssemblyOptimizeReturned()); - - // If exception handling is not enabled and setjmp/longjmp handling is - // enabled, we lower invokes into calls and delete unreachable landingpad - // blocks. Lowering invokes when there is no EH support is done in - // TargetPassConfig::addPassesToHandleExceptions, but this runs after this - // function and SjLj handling expects all invokes to be lowered before. - if (!EnableEmException && - TM->Options.ExceptionModel == ExceptionHandling::None) { - addPass(createLowerInvokePass()); - // The lower invoke pass may create unreachable code. Remove it in order not - // to process dead blocks in setjmp/longjmp handling. - addPass(createUnreachableBlockEliminationPass()); - } - - // Handle exceptions and setjmp/longjmp if enabled. - if (EnableEmException || EnableEmSjLj) - addPass(createWebAssemblyLowerEmscriptenEHSjLj(EnableEmException, - EnableEmSjLj)); - - // Expand indirectbr instructions to switches. - addPass(createIndirectBrExpandPass()); - - TargetPassConfig::addIRPasses(); -} - -bool WebAssemblyPassConfig::addInstSelector() { - (void)TargetPassConfig::addInstSelector(); - addPass( - createWebAssemblyISelDag(getWebAssemblyTargetMachine(), getOptLevel())); - // Run the argument-move pass immediately after the ScheduleDAG scheduler - // so that we can fix up the ARGUMENT instructions before anything else - // sees them in the wrong place. - addPass(createWebAssemblyArgumentMove()); - // Set the p2align operands. This information is present during ISel, however - // it's inconvenient to collect. Collect it now, and update the immediate - // operands. - addPass(createWebAssemblySetP2AlignOperands()); - return false; -} - -void WebAssemblyPassConfig::addPostRegAlloc() { - // TODO: The following CodeGen passes don't currently support code containing - // virtual registers. Consider removing their restrictions and re-enabling - // them. - - // These functions all require the NoVRegs property. - disablePass(&MachineCopyPropagationID); - disablePass(&PostRAMachineSinkingID); - disablePass(&PostRASchedulerID); - disablePass(&FuncletLayoutID); - disablePass(&StackMapLivenessID); - disablePass(&LiveDebugValuesID); - disablePass(&PatchableFunctionID); - disablePass(&ShrinkWrapID); - - // This pass hurts code size for wasm because it can generate irreducible - // control flow. - disablePass(&MachineBlockPlacementID); - - TargetPassConfig::addPostRegAlloc(); -} - -void WebAssemblyPassConfig::addPreEmitPass() { - TargetPassConfig::addPreEmitPass(); - - // Rewrite pseudo call_indirect instructions as real instructions. - // This needs to run before register stackification, because we change the - // order of the arguments. - addPass(createWebAssemblyCallIndirectFixup()); - - // Eliminate multiple-entry loops. - addPass(createWebAssemblyFixIrreducibleControlFlow()); - - // Do various transformations for exception handling. - // Every CFG-changing optimizations should come before this. - addPass(createWebAssemblyLateEHPrepare()); - - // 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. - addPass(createWebAssemblyReplacePhysRegs()); - - // Preparations and optimizations related to register stackification. - if (getOptLevel() != CodeGenOpt::None) { - // LiveIntervals isn't commonly run this late. Re-establish preconditions. - addPass(createWebAssemblyPrepareForLiveIntervals()); - - // Depend on LiveIntervals and perform some optimizations on it. - addPass(createWebAssemblyOptimizeLiveIntervals()); - - // 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 - // 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. - // This runs after stackification so that it doesn't consider registers - // that become stackified. - addPass(createWebAssemblyRegColoring()); - } - - // Sort the blocks of the CFG into topological order, a prerequisite for - // BLOCK and LOOP markers. - addPass(createWebAssemblyCFGSort()); - - // Insert BLOCK and LOOP markers. - addPass(createWebAssemblyCFGStackify()); - - // Insert explicit local.get and local.set operators. - addPass(createWebAssemblyExplicitLocals()); - - // Lower br_unless into br_if. - addPass(createWebAssemblyLowerBrUnless()); - - // Perform the very last peephole optimizations on the code. - if (getOptLevel() != CodeGenOpt::None) - addPass(createWebAssemblyPeephole()); - - // Create a mapping from LLVM CodeGen virtual registers to wasm registers. - addPass(createWebAssemblyRegNumbering()); -} - -yaml::MachineFunctionInfo * -WebAssemblyTargetMachine::createDefaultFuncInfoYAML() const { - return new yaml::WebAssemblyFunctionInfo(); -} - -yaml::MachineFunctionInfo *WebAssemblyTargetMachine::convertFuncInfoToYAML( - const MachineFunction &MF) const { - const auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>(); - return new yaml::WebAssemblyFunctionInfo(*MFI); -} - -bool WebAssemblyTargetMachine::parseMachineFunctionInfo( - const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS, - SMDiagnostic &Error, SMRange &SourceRange) const { - const auto &YamlMFI = - reinterpret_cast<const yaml::WebAssemblyFunctionInfo &>(MFI); - MachineFunction &MF = PFS.MF; - MF.getInfo<WebAssemblyFunctionInfo>()->initializeBaseYamlFields(YamlMFI); - return false; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h deleted file mode 100644 index 850e6b9a9e9e..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h +++ /dev/null @@ -1,63 +0,0 @@ -// WebAssemblyTargetMachine.h - Define TargetMachine for WebAssembly -*- C++ -*- -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file declares the WebAssembly-specific subclass of -/// TargetMachine. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYTARGETMACHINE_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYTARGETMACHINE_H - -#include "WebAssemblySubtarget.h" -#include "llvm/Target/TargetMachine.h" - -namespace llvm { - -class WebAssemblyTargetMachine final : public LLVMTargetMachine { - std::unique_ptr<TargetLoweringObjectFile> TLOF; - mutable StringMap<std::unique_ptr<WebAssemblySubtarget>> SubtargetMap; - -public: - WebAssemblyTargetMachine(const Target &T, const Triple &TT, StringRef CPU, - StringRef FS, const TargetOptions &Options, - Optional<Reloc::Model> RM, - Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, - bool JIT); - - ~WebAssemblyTargetMachine() override; - - const WebAssemblySubtarget *getSubtargetImpl(std::string CPU, - std::string FS) const; - const WebAssemblySubtarget * - getSubtargetImpl(const Function &F) const override; - - // Pass Pipeline Configuration - TargetPassConfig *createPassConfig(PassManagerBase &PM) override; - - TargetLoweringObjectFile *getObjFileLowering() const override { - return TLOF.get(); - } - - TargetTransformInfo getTargetTransformInfo(const Function &F) override; - - bool usesPhysRegsForPEI() const override { return false; } - - yaml::MachineFunctionInfo *createDefaultFuncInfoYAML() const override; - yaml::MachineFunctionInfo * - convertFuncInfoToYAML(const MachineFunction &MF) const override; - bool parseMachineFunctionInfo(const yaml::MachineFunctionInfo &, - PerFunctionMIParsingState &PFS, - SMDiagnostic &Error, - SMRange &SourceRange) const override; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetObjectFile.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetObjectFile.cpp deleted file mode 100644 index ad57c600db10..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetObjectFile.cpp +++ /dev/null @@ -1,24 +0,0 @@ -//===-- WebAssemblyTargetObjectFile.cpp - WebAssembly Object Info ---------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines the functions of the WebAssembly-specific subclass -/// of TargetLoweringObjectFile. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyTargetObjectFile.h" -#include "WebAssemblyTargetMachine.h" - -using namespace llvm; - -void WebAssemblyTargetObjectFile::Initialize(MCContext &Ctx, - const TargetMachine &TM) { - TargetLoweringObjectFileWasm::Initialize(Ctx, TM); - InitializeWasm(); -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetObjectFile.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetObjectFile.h deleted file mode 100644 index f46bb2040a7d..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetObjectFile.h +++ /dev/null @@ -1,29 +0,0 @@ -//===-- WebAssemblyTargetObjectFile.h - WebAssembly Object Info -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file declares the WebAssembly-specific subclass of -/// TargetLoweringObjectFile. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYTARGETOBJECTFILE_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYTARGETOBJECTFILE_H - -#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" - -namespace llvm { - -class WebAssemblyTargetObjectFile final : public TargetLoweringObjectFileWasm { -public: - void Initialize(MCContext &Ctx, const TargetMachine &TM) override; -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp deleted file mode 100644 index 46ef765ce0f4..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.cpp +++ /dev/null @@ -1,82 +0,0 @@ -//===-- WebAssemblyTargetTransformInfo.cpp - WebAssembly-specific TTI -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file defines the WebAssembly-specific TargetTransformInfo -/// implementation. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyTargetTransformInfo.h" -#include "llvm/CodeGen/CostTable.h" -#include "llvm/Support/Debug.h" -using namespace llvm; - -#define DEBUG_TYPE "wasmtti" - -TargetTransformInfo::PopcntSupportKind -WebAssemblyTTIImpl::getPopcntSupport(unsigned TyWidth) const { - assert(isPowerOf2_32(TyWidth) && "Ty width must be power of 2"); - return TargetTransformInfo::PSK_FastHardware; -} - -unsigned WebAssemblyTTIImpl::getNumberOfRegisters(bool Vector) { - unsigned Result = BaseT::getNumberOfRegisters(Vector); - - // For SIMD, use at least 16 registers, as a rough guess. - if (Vector) - Result = std::max(Result, 16u); - - return Result; -} - -unsigned WebAssemblyTTIImpl::getRegisterBitWidth(bool Vector) const { - if (Vector && getST()->hasSIMD128()) - return 128; - - return 64; -} - -unsigned WebAssemblyTTIImpl::getArithmeticInstrCost( - unsigned Opcode, Type *Ty, TTI::OperandValueKind Opd1Info, - TTI::OperandValueKind Opd2Info, TTI::OperandValueProperties Opd1PropInfo, - TTI::OperandValueProperties Opd2PropInfo, ArrayRef<const Value *> Args) { - - unsigned Cost = BasicTTIImplBase<WebAssemblyTTIImpl>::getArithmeticInstrCost( - Opcode, Ty, Opd1Info, Opd2Info, Opd1PropInfo, Opd2PropInfo); - - if (auto *VTy = dyn_cast<VectorType>(Ty)) { - switch (Opcode) { - case Instruction::LShr: - case Instruction::AShr: - case Instruction::Shl: - // SIMD128's shifts currently only accept a scalar shift count. For each - // element, we'll need to extract, op, insert. The following is a rough - // approxmation. - if (Opd2Info != TTI::OK_UniformValue && - Opd2Info != TTI::OK_UniformConstantValue) - Cost = VTy->getNumElements() * - (TargetTransformInfo::TCC_Basic + - getArithmeticInstrCost(Opcode, VTy->getElementType()) + - TargetTransformInfo::TCC_Basic); - break; - } - } - return Cost; -} - -unsigned WebAssemblyTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, - unsigned Index) { - unsigned Cost = BasicTTIImplBase::getVectorInstrCost(Opcode, Val, Index); - - // SIMD128's insert/extract currently only take constant indices. - if (Index == -1u) - return Cost + 25 * TargetTransformInfo::TCC_Expensive; - - return Cost; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h deleted file mode 100644 index 1b11b4b631eb..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyTargetTransformInfo.h +++ /dev/null @@ -1,72 +0,0 @@ -//==- WebAssemblyTargetTransformInfo.h - WebAssembly-specific TTI -*- C++ -*-=// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file a TargetTransformInfo::Concept conforming object specific -/// to the WebAssembly target machine. -/// -/// It uses the target's detailed information to provide more precise answers to -/// certain TTI queries, while letting the target independent and default TTI -/// implementations handle the rest. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYTARGETTRANSFORMINFO_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYTARGETTRANSFORMINFO_H - -#include "WebAssemblyTargetMachine.h" -#include "llvm/CodeGen/BasicTTIImpl.h" -#include <algorithm> - -namespace llvm { - -class WebAssemblyTTIImpl final : public BasicTTIImplBase<WebAssemblyTTIImpl> { - typedef BasicTTIImplBase<WebAssemblyTTIImpl> BaseT; - typedef TargetTransformInfo TTI; - friend BaseT; - - const WebAssemblySubtarget *ST; - const WebAssemblyTargetLowering *TLI; - - const WebAssemblySubtarget *getST() const { return ST; } - const WebAssemblyTargetLowering *getTLI() const { return TLI; } - -public: - WebAssemblyTTIImpl(const WebAssemblyTargetMachine *TM, const Function &F) - : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), - TLI(ST->getTargetLowering()) {} - - /// \name Scalar TTI Implementations - /// @{ - - // TODO: Implement more Scalar TTI for WebAssembly - - TTI::PopcntSupportKind getPopcntSupport(unsigned TyWidth) const; - - /// @} - - /// \name Vector TTI Implementations - /// @{ - - unsigned getNumberOfRegisters(bool Vector); - unsigned getRegisterBitWidth(bool Vector) const; - unsigned getArithmeticInstrCost( - unsigned Opcode, Type *Ty, - TTI::OperandValueKind Opd1Info = TTI::OK_AnyValue, - TTI::OperandValueKind Opd2Info = TTI::OK_AnyValue, - TTI::OperandValueProperties Opd1PropInfo = TTI::OP_None, - TTI::OperandValueProperties Opd2PropInfo = TTI::OP_None, - ArrayRef<const Value *> Args = ArrayRef<const Value *>()); - unsigned getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index); - - /// @} -}; - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp deleted file mode 100644 index e9d88d4818a5..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file implements several utility functions for WebAssembly. -/// -//===----------------------------------------------------------------------===// - -#include "WebAssemblyUtilities.h" -#include "WebAssemblyMachineFunctionInfo.h" -#include "llvm/CodeGen/MachineInstr.h" -#include "llvm/CodeGen/MachineLoopInfo.h" -using namespace llvm; - -const char *const WebAssembly::ClangCallTerminateFn = "__clang_call_terminate"; -const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch"; -const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow"; -const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev"; -const char *const WebAssembly::PersonalityWrapperFn = - "_Unwind_Wasm_CallPersonality"; - -/// Test whether MI is a child of some other node in an expression tree. -bool WebAssembly::isChild(const MachineInstr &MI, - const WebAssemblyFunctionInfo &MFI) { - if (MI.getNumOperands() == 0) - return false; - const MachineOperand &MO = MI.getOperand(0); - if (!MO.isReg() || MO.isImplicit() || !MO.isDef()) - return false; - unsigned Reg = MO.getReg(); - return TargetRegisterInfo::isVirtualRegister(Reg) && - MFI.isVRegStackified(Reg); -} - -bool WebAssembly::mayThrow(const MachineInstr &MI) { - switch (MI.getOpcode()) { - case WebAssembly::THROW: - case WebAssembly::THROW_S: - case WebAssembly::RETHROW: - case WebAssembly::RETHROW_S: - return true; - } - if (isCallIndirect(MI.getOpcode())) - return true; - if (!MI.isCall()) - return false; - - const MachineOperand &MO = MI.getOperand(getCalleeOpNo(MI.getOpcode())); - assert(MO.isGlobal()); - const auto *F = dyn_cast<Function>(MO.getGlobal()); - if (!F) - return true; - if (F->doesNotThrow()) - return false; - // These functions never throw - if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn || - F->getName() == ClangCallTerminateFn || F->getName() == StdTerminateFn) - return false; - - // TODO Can we exclude call instructions that are marked as 'nounwind' in the - // original LLVm IR? (Even when the callee may throw) - return true; -} diff --git a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/contrib/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h deleted file mode 100644 index 26cf84de89b9..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h +++ /dev/null @@ -1,51 +0,0 @@ -//===-- WebAssemblyUtilities - WebAssembly Utility Functions ---*- C++ -*-====// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file contains the declaration of the WebAssembly-specific -/// utility functions. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYUTILITIES_H -#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYUTILITIES_H - -#include "llvm/CodeGen/MachineBasicBlock.h" - -namespace llvm { - -class WebAssemblyFunctionInfo; - -namespace WebAssembly { - -bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI); -bool mayThrow(const MachineInstr &MI); - -// Exception-related function names -extern const char *const ClangCallTerminateFn; -extern const char *const CxaBeginCatchFn; -extern const char *const CxaRethrowFn; -extern const char *const StdTerminateFn; -extern const char *const PersonalityWrapperFn; - -/// Return the "bottom" block of an entity, which can be either a MachineLoop or -/// WebAssemblyException. This differs from MachineLoop::getBottomBlock in that -/// it works even if the entity is discontiguous. -template <typename T> MachineBasicBlock *getBottom(const T *Unit) { - MachineBasicBlock *Bottom = Unit->getHeader(); - for (MachineBasicBlock *MBB : Unit->blocks()) - if (MBB->getNumber() > Bottom->getNumber()) - Bottom = MBB; - return Bottom; -} - -} // end namespace WebAssembly - -} // end namespace llvm - -#endif diff --git a/contrib/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt b/contrib/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt deleted file mode 100644 index 701b347bcbd7..000000000000 --- a/contrib/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt +++ /dev/null @@ -1,120 +0,0 @@ -# Tests which are known to fail from the GCC torture test suite. - -# Syntax: Each line has a single test to be marked as a 'known failure' (or -# 'exclusion'. Known failures are expected to fail, and will cause an error if -# they pass. (Known failures that do not run at all will not cause an -# error). The format is -# <name> <attributes> # comment - -# blockaddress without an indirectbr still can't be supported -20071220-1.c O2 # Relocation against a BB address -20071220-2.c -990208-1.c -label13.C O0 -label13a.C O0 -label3.C - -# WebAssembly hasn't implemented (will never?) __builtin_return_address -20010122-1.c -20030323-1.c -20030811-1.c -pr17377.c - -# Error: invalid output constraint '=t' in asm. -990413-2.c - -# Error: __builtin_setjmp / __builtin_longjmp is not supported for the current target. -built-in-setjmp.c -pr60003.c - -# Error in the program / unsupported by Clang. -20000822-1.c -20010209-1.c -20010605-1.c -20030501-1.c -20040520-1.c -20061220-1.c -20090219-1.c -920415-1.c -920428-2.c -920501-7.c -920612-2.c -920721-4.c -921017-1.c -921215-1.c -931002-1.c -comp-goto-2.c -nest-align-1.c -nest-stdar-1.c -nestfunc-1.c -nestfunc-2.c -nestfunc-3.c -nestfunc-5.c -nestfunc-6.c -nestfunc-7.c -pr22061-3.c -pr22061-4.c -pr24135.c -pr51447.c -20020412-1.c -20040308-1.c -20040423-1.c -20041218-2.c -20070919-1.c -align-nest.c -pr41935.c -920302-1.c -920501-3.c -920728-1.c -pr28865.c -attr-alias-1.C -attr-alias-2.C -attr-ifunc-1.C -attr-ifunc-2.C -attr-ifunc-3.C -attr-ifunc-4.C -complit12.C -va-arg-pack-1.C -va-arg-pack-len-1.C -builtin-line1.C -devirt-6.C # bad main signature -devirt-13.C # bad main signature -devirt-14.C # bad main signature -devirt-21.C # bad main signature -devirt-23.C # bad main signature -lifetime2.C # violates C++ DR1696 - -# WASI doesn't have stdjmp.h yet -pr56982.c -simd-2.C - -# WASI doesn't have pthread.h yet -thread_local3.C -thread_local3g.C -thread_local4.C -thread_local4g.C -thread_local5.C -thread_local5g.C - -# Untriaged C++ failures -spec5.C -addr1.C -ef_test.C -member2.C -new39.C -new40.C -nrv8.C -offsetof9.C -opaque-1.C -pr19650.C -pr37146-1.C -pr46149.C -pr59470.C -rtti2.C -self1.C -type-generic-1.C -vbase8-10.C -vbase8-21.C -vbase8-22.C -vbase8-4.C -vector1.C |
