diff options
64 files changed, 2074 insertions, 389 deletions
diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h index a473ebc823d..41b76c9f1f0 100644 --- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -172,6 +172,8 @@ public: }; class TargetLoweringObjectFileWasm : public TargetLoweringObjectFile { + mutable unsigned NextUniqueID = 0; + public: TargetLoweringObjectFileWasm() {} diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h index 515e441a16c..fdb191f9336 100644 --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -270,6 +270,7 @@ public: VK_Hexagon_IE_GOT, VK_WebAssembly_FUNCTION, // Function table index, rather than virtual addr + VK_WebAssembly_TYPEINDEX,// Type table index VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi diff --git a/llvm/include/llvm/MC/MCSectionWasm.h b/llvm/include/llvm/MC/MCSectionWasm.h index cafd845f4fc..4e19196175c 100644 --- a/llvm/include/llvm/MC/MCSectionWasm.h +++ b/llvm/include/llvm/MC/MCSectionWasm.h @@ -40,12 +40,15 @@ class MCSectionWasm final : public MCSection { const MCSymbolWasm *Group; + // The offset of the MC function section in the wasm code section. + uint64_t SectionOffset; + private: friend class MCContext; MCSectionWasm(StringRef Section, unsigned type, unsigned flags, SectionKind K, const MCSymbolWasm *group, unsigned UniqueID, MCSymbol *Begin) : MCSection(SV_Wasm, K, Begin), SectionName(Section), Type(type), - Flags(flags), UniqueID(UniqueID), Group(group) { + Flags(flags), UniqueID(UniqueID), Group(group), SectionOffset(0) { } void setSectionName(StringRef Name) { SectionName = Name; } @@ -72,6 +75,9 @@ public: bool isUnique() const { return UniqueID != ~0U; } unsigned getUniqueID() const { return UniqueID; } + uint64_t getSectionOffset() const { return SectionOffset; } + void setSectionOffset(uint64_t Offset) { SectionOffset = Offset; } + static bool classof(const MCSection *S) { return S->getVariant() == SV_Wasm; } }; diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h index 9ac688c7f56..4f8820f4426 100644 --- a/llvm/include/llvm/MC/MCSymbolWasm.h +++ b/llvm/include/llvm/MC/MCSymbolWasm.h @@ -13,11 +13,41 @@ namespace llvm { class MCSymbolWasm : public MCSymbol { +private: + bool IsFunction = false; + std::string ModuleName; + SmallVector<unsigned, 1> Returns; + SmallVector<unsigned, 4> Params; + + /// An expression describing how to calculate the size of a symbol. If a + /// symbol has no size this field will be NULL. + const MCExpr *SymbolSize = nullptr; + public: + // Use a module name of "env" for now, for compatibility with existing tools. + // This is temporary, and may change, as the ABI is not yet stable. MCSymbolWasm(const StringMapEntry<bool> *Name, bool isTemporary) - : MCSymbol(SymbolKindWasm, Name, isTemporary) {} - + : MCSymbol(SymbolKindWasm, Name, isTemporary), + ModuleName("env") {} static bool classof(const MCSymbol *S) { return S->isWasm(); } + + const MCExpr *getSize() const { return SymbolSize; } + void setSize(const MCExpr *SS) { SymbolSize = SS; } + + bool isFunction() const { return IsFunction; } + void setIsFunction(bool isFunc) { IsFunction = isFunc; } + + const StringRef getModuleName() const { return ModuleName; } + + const SmallVector<unsigned, 1> &getReturns() const { return Returns; } + void setReturns(SmallVectorImpl<unsigned> &&Rets) { + Returns = std::move(Rets); + } + + const SmallVector<unsigned, 4> &getParams() const { return Params; } + void setParams(SmallVectorImpl<unsigned> &&Pars) { + Params = std::move(Pars); + } }; } diff --git a/llvm/include/llvm/MC/MCWasmObjectWriter.h b/llvm/include/llvm/MC/MCWasmObjectWriter.h index f019eea5ce3..6e458eaac9c 100644 --- a/llvm/include/llvm/MC/MCWasmObjectWriter.h +++ b/llvm/include/llvm/MC/MCWasmObjectWriter.h @@ -22,22 +22,29 @@ class MCContext; class MCFixup; class MCFragment; class MCObjectWriter; +class MCSectionWasm; class MCSymbol; class MCSymbolWasm; class MCValue; class raw_pwrite_stream; +// Information about a single relocation. struct WasmRelocationEntry { uint64_t Offset; // Where is the relocation. const MCSymbolWasm *Symbol; // The symbol to relocate with. + uint64_t Addend; // A value to add to the symbol. unsigned Type; // The type of the relocation. + MCSectionWasm *FixupSection;// The section the relocation is targeting. WasmRelocationEntry(uint64_t Offset, const MCSymbolWasm *Symbol, - unsigned Type) - : Offset(Offset), Symbol(Symbol), Type(Type) {} + uint64_t Addend, unsigned Type, + MCSectionWasm *FixupSection) + : Offset(Offset), Symbol(Symbol), Addend(Addend), Type(Type), + FixupSection(FixupSection) {} void print(raw_ostream &Out) const { - Out << "Off=" << Offset << ", Sym=" << Symbol << ", Type=" << Type; + Out << "Off=" << Offset << ", Sym=" << Symbol << ", Addend=" << Addend + << ", Type=" << Type << ", FixupSection=" << FixupSection; } void dump() const { print(errs()); } }; diff --git a/llvm/include/llvm/MC/MCWasmStreamer.h b/llvm/include/llvm/MC/MCWasmStreamer.h index 6b3d8c0bf91..24c566a959b 100644 --- a/llvm/include/llvm/MC/MCWasmStreamer.h +++ b/llvm/include/llvm/MC/MCWasmStreamer.h @@ -54,6 +54,8 @@ public: void EmitCOFFSymbolType(int Type) override; void EndCOFFSymbolDef() override; + void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; + void EmitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) override; diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index 6b4aef127de..21431a166c0 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1162,18 +1162,73 @@ void TargetLoweringObjectFileCOFF::emitLinkerFlagsForGlobal( // Wasm //===----------------------------------------------------------------------===// +static const Comdat *getWasmComdat(const GlobalValue *GV) { + const Comdat *C = GV->getComdat(); + if (!C) + return nullptr; + + if (C->getSelectionKind() != Comdat::Any) + report_fatal_error("Wasm COMDATs only support SelectionKind::Any, '" + + C->getName() + "' cannot be lowered."); + + return C; +} + MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { llvm_unreachable("getExplicitSectionGlobal not yet implemented"); return nullptr; } +static MCSectionWasm * +selectWasmSectionForGlobal(MCContext &Ctx, const GlobalObject *GO, + SectionKind Kind, Mangler &Mang, + const TargetMachine &TM, bool EmitUniqueSection, + unsigned Flags, unsigned *NextUniqueID) { + StringRef Group = ""; + if (getWasmComdat(GO)) + llvm_unreachable("comdat not yet supported for wasm"); + + bool UniqueSectionNames = TM.getUniqueSectionNames(); + SmallString<128> Name = getSectionPrefixForGlobal(Kind); + + if (const auto *F = dyn_cast<Function>(GO)) { + const auto &OptionalPrefix = F->getSectionPrefix(); + if (OptionalPrefix) + Name += *OptionalPrefix; + } + + if (EmitUniqueSection && UniqueSectionNames) { + Name.push_back('.'); + TM.getNameWithPrefix(Name, GO, Mang, true); + } + unsigned UniqueID = MCContext::GenericSectionID; + if (EmitUniqueSection && !UniqueSectionNames) { + UniqueID = *NextUniqueID; + (*NextUniqueID)++; + } + return Ctx.getWasmSection(Name, /*Type=*/0, Flags, + Group, UniqueID); +} + MCSection *TargetLoweringObjectFileWasm::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + + if (Kind.isCommon()) + report_fatal_error("mergable sections not supported yet on wasm"); + + // If we have -ffunction-section or -fdata-section then we should emit the + // global value to a uniqued section specifically for it. + bool EmitUniqueSection = false; if (Kind.isText()) - return TextSection; - assert(!Kind.isMetadata() && "metadata sections not yet implemented"); - return DataSection; + EmitUniqueSection = TM.getFunctionSections(); + else + EmitUniqueSection = TM.getDataSections(); + EmitUniqueSection |= GO->hasComdat(); + + return selectWasmSectionForGlobal(getContext(), GO, Kind, getMangler(), TM, + EmitUniqueSection, /*Flags=*/0, + &NextUniqueID); } bool TargetLoweringObjectFileWasm::shouldPutJumpTableInFunctionSection( diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index 7522e921160..b3dfa9e08de 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -286,6 +286,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) { case VK_Hexagon_IE: return "IE"; case VK_Hexagon_IE_GOT: return "IEGOT"; case VK_WebAssembly_FUNCTION: return "FUNCTION"; + case VK_WebAssembly_TYPEINDEX: return "TYPEINDEX"; case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo"; case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi"; case VK_AMDGPU_REL32_LO: return "rel32@lo"; diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index a6650134f77..687ad3dc83a 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -801,8 +801,8 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { void MCObjectFileInfo::initWasmMCObjectFileInfo(const Triple &T) { // TODO: Set the section types and flags. - TextSection = Ctx->getWasmSection("", 0, 0); - DataSection = Ctx->getWasmSection("", 0, 0); + TextSection = Ctx->getWasmSection(".text", 0, 0); + DataSection = Ctx->getWasmSection(".data", 0, 0); // TODO: Set the section types and flags. DwarfLineSection = Ctx->getWasmSection(".debug_line", 0, 0); diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp index f0a2f460a75..11a00adca65 100644 --- a/llvm/lib/MC/MCWasmStreamer.cpp +++ b/llvm/lib/MC/MCWasmStreamer.cpp @@ -89,7 +89,29 @@ bool MCWasmStreamer::EmitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { // the symbol with the assembler. getAssembler().registerSymbol(*Symbol); - // TODO: Set the symbol binding, type, etc. + switch (Attribute) { + case MCSA_LazyReference: + case MCSA_Reference: + case MCSA_SymbolResolver: + case MCSA_PrivateExtern: + case MCSA_WeakDefinition: + case MCSA_WeakDefAutoPrivate: + case MCSA_Invalid: + case MCSA_IndirectSymbol: + return false; + case MCSA_Global: + Symbol->setExternal(true); + break; + case MCSA_ELF_TypeFunction: + Symbol->setIsFunction(true); + break; + case MCSA_ELF_TypeObject: + Symbol->setIsFunction(false); + break; + default: + // unrecognized directive + return false; + } return true; } @@ -99,6 +121,10 @@ void MCWasmStreamer::EmitCommonSymbol(MCSymbol *S, uint64_t Size, llvm_unreachable("Common symbols are not yet implemented for Wasm"); } +void MCWasmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) { + cast<MCSymbolWasm>(Symbol)->setSize(Value); +} + void MCWasmStreamer::EmitLocalCommonSymbol(MCSymbol *S, uint64_t Size, unsigned ByteAlignment) { llvm_unreachable("Local common symbols are not yet implemented for Wasm"); @@ -124,7 +150,17 @@ void MCWasmStreamer::EmitFileDirective(StringRef Filename) { } void MCWasmStreamer::EmitIdent(StringRef IdentString) { - llvm_unreachable("Ident sections not yet implemented for wasm"); + MCSection *Comment = getAssembler().getContext().getWasmSection( + ".comment", 0, 0); + PushSection(); + SwitchSection(Comment); + if (!SeenIdent) { + EmitIntValue(0, 1); + SeenIdent = true; + } + EmitBytes(IdentString); + EmitIntValue(0, 1); + PopSection(); } void MCWasmStreamer::EmitInstToFragment(const MCInst &Inst, diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index 3a6114ad285..32d8b8c4de9 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -13,8 +13,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringMap.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmLayout.h" @@ -28,9 +26,10 @@ #include "llvm/MC/MCSymbolWasm.h" #include "llvm/MC/MCValue.h" #include "llvm/MC/MCWasmObjectWriter.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/Wasm.h" #include <vector> @@ -41,7 +40,25 @@ using namespace llvm; #define DEBUG_TYPE "reloc-info" namespace { -typedef DenseMap<const MCSectionWasm *, uint32_t> SectionIndexMapTy; +// For patching purposes, we need to remember where each section starts, both +// for patching up the section size field, and for patching up references to +// locations within the section. +struct SectionBookkeeping { + // Where the size of the section is written. + uint64_t SizeOffset; + // Where the contents of the section starts (after the header). + uint64_t ContentsOffset; +}; + +// This record records information about a call_indirect which needs its +// type index fixed up once we've computed type indices. +struct TypeIndexFixup { + uint64_t Offset; + const MCSymbolWasm *Symbol; + const MCSectionWasm *FixupSection; + TypeIndexFixup(uint64_t O, const MCSymbolWasm *S, MCSectionWasm *F) + : Offset(O), Symbol(S), FixupSection(F) {} +}; class WasmObjectWriter : public MCObjectWriter { /// Helper struct for containing some precomputed information on symbols. @@ -56,6 +73,18 @@ class WasmObjectWriter : public MCObjectWriter { /// The target specific Wasm writer instance. std::unique_ptr<MCWasmObjectTargetWriter> TargetObjectWriter; + // Relocations for fixing up references in the code section. + std::vector<WasmRelocationEntry> CodeRelocations; + + // Relocations for fixing up references in the data section. + std::vector<WasmRelocationEntry> DataRelocations; + + // Fixups for call_indirect type indices. + std::vector<TypeIndexFixup> TypeIndexFixups; + + // Index values to use for fixing up call_indirect type indices. + std::vector<uint32_t> TypeIndexFixupTypes; + // TargetObjectWriter wrappers. bool is64Bit() const { return TargetObjectWriter->is64Bit(); } unsigned getRelocType(MCContext &Ctx, const MCValue &Target, @@ -63,10 +92,15 @@ class WasmObjectWriter : public MCObjectWriter { return TargetObjectWriter->getRelocType(Ctx, Target, Fixup, IsPCRel); } + void startSection(SectionBookkeeping &Section, unsigned SectionId, + const char *Name = nullptr); + void endSection(SectionBookkeeping &Section); + public: WasmObjectWriter(MCWasmObjectTargetWriter *MOTW, raw_pwrite_stream &OS) : MCObjectWriter(OS, /*IsLittleEndian=*/true), TargetObjectWriter(MOTW) {} +private: void reset() override { MCObjectWriter::reset(); } @@ -89,6 +123,58 @@ public: WasmObjectWriter::~WasmObjectWriter() {} +// Return the padding size to write a 32-bit value into a 5-byte ULEB128. +static unsigned PaddingFor5ByteULEB128(uint32_t X) { + return X == 0 ? 4 : (4u - (31u - countLeadingZeros(X)) / 7u); +} + +// Return the padding size to write a 32-bit value into a 5-byte SLEB128. +static unsigned PaddingFor5ByteSLEB128(int32_t X) { + return 5 - getSLEB128Size(X); +} + +// Write out a section header and a patchable section size field. +void WasmObjectWriter::startSection(SectionBookkeeping &Section, + unsigned SectionId, + const char *Name) { + assert((Name != nullptr) == (SectionId == wasm::WASM_SEC_CUSTOM) && + "Only custom sections can have names"); + + write8(SectionId); + + Section.SizeOffset = getStream().tell(); + + // The section size. We don't know the size yet, so reserve enough space + // for any 32-bit value; we'll patch it later. + encodeULEB128(UINT32_MAX, getStream()); + + // The position where the section starts, for measuring its size. + Section.ContentsOffset = getStream().tell(); + + // Custom sections in wasm also have a string identifier. + if (SectionId == wasm::WASM_SEC_CUSTOM) { + encodeULEB128(strlen(Name), getStream()); + writeBytes(Name); + } +} + +// Now that the section is complete and we know how big it is, patch up the +// section size field at the start of the section. +void WasmObjectWriter::endSection(SectionBookkeeping &Section) { + uint64_t Size = getStream().tell() - Section.ContentsOffset; + if (uint32_t(Size) != Size) + report_fatal_error("section size does not fit in a uint32_t"); + + unsigned Padding = PaddingFor5ByteULEB128(Size); + + // Write the final section size to the payload_len field, which follows + // the section id byte. + uint8_t Buffer[16]; + unsigned SizeLen = encodeULEB128(Size, Buffer, Padding); + assert(SizeLen == 5); + getStream().pwrite((char *)Buffer, SizeLen, Section.SizeOffset); +} + // Emit the Wasm header. void WasmObjectWriter::writeHeader(const MCAssembler &Asm) { writeBytes(StringRef(wasm::WasmMagic, sizeof(wasm::WasmMagic))); @@ -104,15 +190,823 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, bool &IsPCRel, uint64_t &FixedValue) { - // TODO: Implement + MCSectionWasm &FixupSection = cast<MCSectionWasm>(*Fragment->getParent()); + uint64_t C = Target.getConstant(); + uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); + MCContext &Ctx = Asm.getContext(); + + if (const MCSymbolRefExpr *RefB = Target.getSymB()) { + assert(RefB->getKind() == MCSymbolRefExpr::VK_None && + "Should not have constructed this"); + + // Let A, B and C being the components of Target and R be the location of + // the fixup. If the fixup is not pcrel, we want to compute (A - B + C). + // If it is pcrel, we want to compute (A - B + C - R). + + // In general, Wasm has no relocations for -B. It can only represent (A + C) + // or (A + C - R). If B = R + K and the relocation is not pcrel, we can + // replace B to implement it: (A - R - K + C) + if (IsPCRel) { + Ctx.reportError( + Fixup.getLoc(), + "No relocation available to represent this relative expression"); + return; + } + + const auto &SymB = cast<MCSymbolWasm>(RefB->getSymbol()); + + if (SymB.isUndefined()) { + Ctx.reportError(Fixup.getLoc(), + Twine("symbol '") + SymB.getName() + + "' can not be undefined in a subtraction expression"); + return; + } + + assert(!SymB.isAbsolute() && "Should have been folded"); + const MCSection &SecB = SymB.getSection(); + if (&SecB != &FixupSection) { + Ctx.reportError(Fixup.getLoc(), + "Cannot represent a difference across sections"); + return; + } + + uint64_t SymBOffset = Layout.getSymbolOffset(SymB); + uint64_t K = SymBOffset - FixupOffset; + IsPCRel = true; + C -= K; + } + + // We either rejected the fixup or folded B into C at this point. + const MCSymbolRefExpr *RefA = Target.getSymA(); + const auto *SymA = RefA ? cast<MCSymbolWasm>(&RefA->getSymbol()) : nullptr; + + bool ViaWeakRef = false; + if (SymA && SymA->isVariable()) { + const MCExpr *Expr = SymA->getVariableValue(); + if (const auto *Inner = dyn_cast<MCSymbolRefExpr>(Expr)) { + if (Inner->getKind() == MCSymbolRefExpr::VK_WEAKREF) { + SymA = cast<MCSymbolWasm>(&Inner->getSymbol()); + ViaWeakRef = true; + } + } + } + + // Put any constant offset in an addend. Offsets can be negative, and + // LLVM expects wrapping, in contrast to wasm's immediates which can't + // be negative and don't wrap. + FixedValue = 0; + + if (SymA) { + if (ViaWeakRef) + llvm_unreachable("weakref used in reloc not yet implemented"); + else + SymA->setUsedInReloc(); + } + + if (RefA) { + if (RefA->getKind() == MCSymbolRefExpr::VK_WebAssembly_TYPEINDEX) { + TypeIndexFixups.push_back(TypeIndexFixup(FixupOffset, SymA, + &FixupSection)); + return; + } + } + + unsigned Type = getRelocType(Ctx, Target, Fixup, IsPCRel); + + WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection); + + if (FixupSection.hasInstructions()) + CodeRelocations.push_back(Rec); + else + DataRelocations.push_back(Rec); +} + +namespace { + +// +struct WasmFunctionType { + // Support empty and tombstone instances, needed by DenseMap. + enum { Plain, Empty, Tombstone } State; + + // The return types of the function. + SmallVector<unsigned, 1> Returns; + + // The parameter types of the function. + SmallVector<unsigned, 4> Params; + + WasmFunctionType() : State(Plain) {} + + bool operator==(const WasmFunctionType &Other) const { + return State == Other.State && Returns == Other.Returns && + Params == Other.Params; + } +}; + +// Traits for using WasmFunctionType in a DenseMap. +struct WasmFunctionTypeDenseMapInfo { + static WasmFunctionType getEmptyKey() { + WasmFunctionType FuncTy; + FuncTy.State = WasmFunctionType::Empty; + return FuncTy; + } + static WasmFunctionType getTombstoneKey() { + WasmFunctionType FuncTy; + FuncTy.State = WasmFunctionType::Tombstone; + return FuncTy; + } + static unsigned getHashValue(const WasmFunctionType &FuncTy) { + uintptr_t Value = FuncTy.State; + for (unsigned Ret : FuncTy.Returns) + Value += DenseMapInfo<unsigned>::getHashValue(Ret); + for (unsigned Param : FuncTy.Params) + Value += DenseMapInfo<unsigned>::getHashValue(Param); + return Value; + } + static bool isEqual(const WasmFunctionType &LHS, + const WasmFunctionType &RHS) { + return LHS == RHS; + } +}; + +// A wasm import to be written into the import section. +struct WasmImport { + StringRef ModuleName; + StringRef FieldName; + unsigned Kind; + uint32_t Type; +}; + +// A wasm function to be written into the function section. +struct WasmFunction { + unsigned Type; + const MCSymbolWasm *Sym; +}; + +// A wasm export to be written into the export section. +struct WasmExport { + StringRef FieldName; + unsigned Kind; + uint32_t Index; +}; + +// A wasm global to be written into the global section. +struct WasmGlobal { + unsigned Type; + bool IsMutable; + uint32_t InitialValue; +}; + +} // end anonymous namespace + +// Write X as an (unsigned) LEB value at offset Offset in Stream, padded +// to allow patching. +static void +WritePatchableLEB(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { + uint8_t Buffer[5]; + unsigned Padding = PaddingFor5ByteULEB128(X); + unsigned SizeLen = encodeULEB128(X, Buffer, Padding); + assert(SizeLen == 5); + Stream.pwrite((char *)Buffer, SizeLen, Offset); +} + +// Write X as an signed LEB value at offset Offset in Stream, padded +// to allow patching. +static void +WritePatchableSLEB(raw_pwrite_stream &Stream, int32_t X, uint64_t Offset) { + uint8_t Buffer[5]; + unsigned Padding = PaddingFor5ByteSLEB128(X); + unsigned SizeLen = encodeSLEB128(X, Buffer, Padding); + assert(SizeLen == 5); + Stream.pwrite((char *)Buffer, SizeLen, Offset); +} + +// Write X as a plain integer value at offset Offset in Stream. +static void WriteI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { + uint8_t Buffer[4]; + support::endian::write32le(Buffer, X); + Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); +} + +// Compute a value to write into the code at the location covered +// by RelEntry. This value isn't used by the static linker, since +// we have addends; it just serves to make the code more readable +// and to make standalone wasm modules directly usable. +static uint32_t ProvisionalValue(const WasmRelocationEntry &RelEntry) { + const MCSymbolWasm *Sym = RelEntry.Symbol; + + // For undefined symbols, use a hopefully invalid value. + if (!Sym->isDefined(false)) + return UINT32_MAX; + + MCSectionWasm &Section = + cast<MCSectionWasm>(RelEntry.Symbol->getSection(false)); + uint64_t Address = Section.getSectionOffset() + RelEntry.Addend; + + // Ignore overflow. LLVM allows address arithmetic to silently wrap. + uint32_t Value = Address; + + return Value; +} + +// Apply the portions of the relocation records that we can handle ourselves +// directly. +static void ApplyRelocations( + ArrayRef<WasmRelocationEntry> Relocations, + raw_pwrite_stream &Stream, + DenseMap<const MCSymbolWasm *, uint32_t> &SymbolIndices, + uint64_t ContentsOffset) +{ + for (const WasmRelocationEntry &RelEntry : Relocations) { + uint64_t Offset = ContentsOffset + + RelEntry.FixupSection->getSectionOffset() + + RelEntry.Offset; + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: { + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + assert(RelEntry.Addend == 0); + + WritePatchableLEB(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: { + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + assert(RelEntry.Addend == 0); + + WritePatchableSLEB(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: { + uint32_t Value = ProvisionalValue(RelEntry); + + WritePatchableSLEB(Stream, Value, Offset); + break; + } + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: { + uint32_t Value = ProvisionalValue(RelEntry); + + WritePatchableLEB(Stream, Value, Offset); + break; + } + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: { + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + assert(RelEntry.Addend == 0); + + WriteI32(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: { + uint32_t Value = ProvisionalValue(RelEntry); + + WriteI32(Stream, Value, Offset); + break; + } + default: + break; + } + } +} + +// Write out the portions of the relocation records that the linker will +// need to handle. +static void WriteRelocations( + ArrayRef<WasmRelocationEntry> Relocations, + raw_pwrite_stream &Stream, + DenseMap<const MCSymbolWasm *, uint32_t> &SymbolIndices) +{ + for (const WasmRelocationEntry RelEntry : Relocations) { + encodeULEB128(RelEntry.Type, Stream); + + uint64_t Offset = RelEntry.Offset + + RelEntry.FixupSection->getSectionOffset(); + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + int64_t Addend = RelEntry.Addend; + + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + encodeULEB128(Offset, Stream); + encodeULEB128(Index, Stream); + assert(Addend == 0 && "addends not supported for functions"); + break; + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + encodeULEB128(Offset, Stream); + encodeULEB128(Index, Stream); + encodeSLEB128(Addend, Stream); + break; + default: + llvm_unreachable("unsupported relocation type"); + } + } } void WasmObjectWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { + unsigned PtrType = is64Bit() ? wasm::WASM_TYPE_I64 : wasm::WASM_TYPE_I32; + + // Collect information from the available symbols. + DenseMap<WasmFunctionType, unsigned, WasmFunctionTypeDenseMapInfo> + FunctionTypeIndices; + SmallVector<WasmFunctionType, 4> FunctionTypes; + SmallVector<WasmFunction, 4> Functions; + SmallVector<uint32_t, 4> TableElems; + SmallVector<WasmGlobal, 4> Globals; + SmallVector<WasmImport, 4> Imports; + SmallVector<WasmExport, 4> Exports; + DenseMap<const MCSymbolWasm *, uint32_t> SymbolIndices; + SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken; + unsigned NumFuncImports = 0; + unsigned NumGlobalImports = 0; + SmallVector<char, 0> DataBytes; + + // Populate the IsAddressTaken set. + for (WasmRelocationEntry RelEntry : CodeRelocations) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: + IsAddressTaken.insert(RelEntry.Symbol); + break; + default: + break; + } + } + for (WasmRelocationEntry RelEntry : DataRelocations) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + IsAddressTaken.insert(RelEntry.Symbol); + break; + default: + break; + } + } + + // Populate the Imports set. + for (const MCSymbol &S : Asm.symbols()) { + const auto &WS = static_cast<const MCSymbolWasm &>(S); + unsigned Type; + + if (WS.isFunction()) { + // Prepare the function's type, if we haven't seen it yet. + WasmFunctionType F; + F.Returns = WS.getReturns(); + F.Params = WS.getParams(); + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + + Type = Pair.first->second; + } else { + Type = PtrType; + } + + // If the symbol is not defined in this translation unit, import it. + if (!WS.isTemporary() && !WS.isDefined(/*SetUsed=*/false)) { + WasmImport Import; + Import.ModuleName = WS.getModuleName(); + Import.FieldName = WS.getName(); + + if (WS.isFunction()) { + Import.Kind = wasm::WASM_EXTERNAL_FUNCTION; + Import.Type = Type; + SymbolIndices[&WS] = NumFuncImports; + ++NumFuncImports; + } else { + Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; + Import.Type = Type; + SymbolIndices[&WS] = NumGlobalImports; + ++NumGlobalImports; + } + + Imports.push_back(Import); + } + } + + // Handle defined symbols. + for (const MCSymbol &S : Asm.symbols()) { + // Ignore unnamed temporary symbols, which aren't ever exported, imported, + // or used in relocations. + if (S.isTemporary() && S.getName().empty()) + continue; + const auto &WS = static_cast<const MCSymbolWasm &>(S); + unsigned Index; + if (WS.isFunction()) { + // Prepare the function's type, if we haven't seen it yet. + WasmFunctionType F; + F.Returns = WS.getReturns(); + F.Params = WS.getParams(); + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + + unsigned Type = Pair.first->second; + + if (WS.isDefined(/*SetUsed=*/false)) { + // A definition. Take the next available index. + Index = NumFuncImports + Functions.size(); + + // Prepare the function. + WasmFunction Func; + Func.Type = Type; + Func.Sym = &WS; + SymbolIndices[&WS] = Index; + Functions.push_back(Func); + } else { + // An import; the index was assigned above. + Index = SymbolIndices.find(&WS)->second; + } + + // If needed, prepare the function to be called indirectly. + if (IsAddressTaken.count(&WS)) + TableElems.push_back(Index); + } else { + // For now, ignore temporary non-function symbols. + if (S.isTemporary()) + continue; + + if (WS.getOffset() != 0) + report_fatal_error("data sections must contain one variable each"); + if (!WS.getSize()) + report_fatal_error("data symbols must have a size set with .size"); + + int64_t Size = 0; + if (!WS.getSize()->evaluateAsAbsolute(Size, Layout)) + report_fatal_error(".size expression must be evaluatable"); + + if (WS.isDefined(false)) { + MCSectionWasm &DataSection = + static_cast<MCSectionWasm &>(WS.getSection()); + + if (uint64_t(Size) != Layout.getSectionFileSize(&DataSection)) + report_fatal_error("data sections must contain at most one variable"); + + DataBytes.resize(alignTo(DataBytes.size(), DataSection.getAlignment())); + + DataSection.setSectionOffset(DataBytes.size()); + + for (MCSection::iterator I = DataSection.begin(), E = DataSection.end(); + I != E; ++I) { + const MCFragment &Frag = *I; + if (Frag.hasInstructions()) + report_fatal_error("only data supported in data sections"); + + if (const MCAlignFragment *Align = dyn_cast<MCAlignFragment>(&Frag)) { + if (Align->getValueSize() != 1) + report_fatal_error("only byte values supported for alignment"); + // If nops are requested, use zeros, as this is the data section. + uint8_t Value = Align->hasEmitNops() ? 0 : Align->getValue(); + uint64_t Size = std::min<uint64_t>(alignTo(DataBytes.size(), + Align->getAlignment()), + DataBytes.size() + + Align->getMaxBytesToEmit()); + DataBytes.resize(Size, Value); + } else if (const MCFillFragment *Fill = + dyn_cast<MCFillFragment>(&Frag)) { + DataBytes.insert(DataBytes.end(), Size, Fill->getValue()); + } else { + const MCDataFragment &DataFrag = cast<MCDataFragment>(Frag); + const SmallVectorImpl<char> &Contents = DataFrag.getContents(); + + DataBytes.insert(DataBytes.end(), Contents.begin(), Contents.end()); + } + } + + // For each external global, prepare a corresponding wasm global + // holding its address. + if (WS.isExternal()) { + Index = NumGlobalImports + Globals.size(); + + WasmGlobal Global; + Global.Type = PtrType; + Global.IsMutable = false; + Global.InitialValue = DataSection.getSectionOffset(); + SymbolIndices[&WS] = Index; + Globals.push_back(Global); + } + } + } + + // If the symbol is visible outside this translation unit, export it. + if (WS.isExternal()) { + assert(WS.isDefined(false)); + WasmExport Export; + Export.FieldName = WS.getName(); + Export.Index = Index; + + if (WS.isFunction()) + Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; + else + Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; + + Exports.push_back(Export); + } + } + + // Add types for indirect function calls. + for (const TypeIndexFixup &Fixup : TypeIndexFixups) { + WasmFunctionType F; + F.Returns = Fixup.Symbol->getReturns(); + F.Params = Fixup.Symbol->getParams(); + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + + TypeIndexFixupTypes.push_back(Pair.first->second); + } + // Write out the Wasm header. writeHeader(Asm); - // TODO: Write the contents. + SectionBookkeeping Section; + + // === Type Section ========================================================= + if (!FunctionTypes.empty()) { + startSection(Section, wasm::WASM_SEC_TYPE); + + encodeULEB128(FunctionTypes.size(), getStream()); + + for (WasmFunctionType &FuncTy : FunctionTypes) { + write8(wasm::WASM_TYPE_FUNC); + encodeULEB128(FuncTy.Params.size(), getStream()); + for (unsigned Ty : FuncTy.Params) + write8(Ty); + encodeULEB128(FuncTy.Returns.size(), getStream()); + for (unsigned Ty : FuncTy.Returns) + write8(Ty); + } + + endSection(Section); + } + + // === Import Section ======================================================== + if (!Imports.empty()) { + startSection(Section, wasm::WASM_SEC_IMPORT); + + encodeULEB128(Imports.size(), getStream()); + for (const WasmImport &Import : Imports) { + StringRef ModuleName = Import.ModuleName; + encodeULEB128(ModuleName.size(), getStream()); + writeBytes(ModuleName); + + StringRef FieldName = Import.FieldName; + encodeULEB128(FieldName.size(), getStream()); + writeBytes(FieldName); + + write8(Import.Kind); + + switch (Import.Kind) { + case wasm::WASM_EXTERNAL_FUNCTION: + encodeULEB128(Import.Type, getStream()); + break; + case wasm::WASM_EXTERNAL_GLOBAL: + write8(Import.Type); + write8(0); // mutability + break; + default: + llvm_unreachable("unsupported import kind"); + } + } + + endSection(Section); + } + + // === Function Section ====================================================== + if (!Functions.empty()) { + startSection(Section, wasm::WASM_SEC_FUNCTION); + + encodeULEB128(Functions.size(), getStream()); + for (const WasmFunction &Func : Functions) + encodeULEB128(Func.Type, getStream()); + + endSection(Section); + } + + // === Table Section ========================================================= + // For now, always emit the table section, since indirect calls are not + // valid without it. In the future, we could perhaps be more clever and omit + // it if there are no indirect calls. + startSection(Section, wasm::WASM_SEC_TABLE); + + // The number of tables, fixed to 1 for now. + encodeULEB128(1, getStream()); + + write8(wasm::WASM_TYPE_ANYFUNC); + + encodeULEB128(0, getStream()); // flags + encodeULEB128(TableElems.size(), getStream()); // initial + + endSection(Section); + + // === Memory Section ======================================================== + // For now, always emit the memory section, since loads and stores are not + // valid without it. In the future, we could perhaps be more clever and omit + // it if there are no loads or stores. + startSection(Section, wasm::WASM_SEC_MEMORY); + + encodeULEB128(1, getStream()); // number of memory spaces + + encodeULEB128(0, getStream()); // flags + encodeULEB128(DataBytes.size(), getStream()); // initial + + endSection(Section); + + // === Global Section ======================================================== + if (!Globals.empty()) { + startSection(Section, wasm::WASM_SEC_GLOBAL); + + encodeULEB128(Globals.size(), getStream()); + for (const WasmGlobal &Global : Globals) { + write8(Global.Type); + write8(Global.IsMutable); + + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(Global.InitialValue, getStream()); // offset + write8(wasm::WASM_OPCODE_END); + } + + endSection(Section); + } + + // === Export Section ======================================================== + if (!Exports.empty()) { + startSection(Section, wasm::WASM_SEC_EXPORT); + + encodeULEB128(Exports.size(), getStream()); + for (const WasmExport &Export : Exports) { + encodeULEB128(Export.FieldName.size(), getStream()); + writeBytes(Export.FieldName); + + write8(Export.Kind); + + encodeULEB128(Export.Index, getStream()); + } + + endSection(Section); + } + +#if 0 // TODO: Start Section + if (HaveStartFunction) { + // === Start Section ========================================================= + startSection(Section, wasm::WASM_SEC_START); + + encodeSLEB128(StartFunction, getStream()); + + endSection(Section); + } +#endif + + // === Elem Section ========================================================== + if (!TableElems.empty()) { + startSection(Section, wasm::WASM_SEC_ELEM); + + encodeULEB128(1, getStream()); // number of "segments" + encodeULEB128(0, getStream()); // the table index + + // init expr for starting offset + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(0, getStream()); + write8(wasm::WASM_OPCODE_END); + + encodeULEB128(TableElems.size(), getStream()); + for (uint32_t Elem : TableElems) + encodeULEB128(Elem, getStream()); + + endSection(Section); + } + + // === Code Section ========================================================== + if (!Functions.empty()) { + startSection(Section, wasm::WASM_SEC_CODE); + + encodeULEB128(Functions.size(), getStream()); + + for (const WasmFunction &Func : Functions) { + MCSectionWasm &FuncSection = + static_cast<MCSectionWasm &>(Func.Sym->getSection()); + + if (Func.Sym->isVariable()) + report_fatal_error("weak symbols not supported yet"); + + if (Func.Sym->getOffset() != 0) + report_fatal_error("function sections must contain one function each"); + + if (!Func.Sym->getSize()) + report_fatal_error("function symbols must have a size set with .size"); + + int64_t Size = 0; + if (!Func.Sym->getSize()->evaluateAsAbsolute(Size, Layout)) + report_fatal_error(".size expression must be evaluatable"); + + encodeULEB128(Size, getStream()); + + FuncSection.setSectionOffset(getStream().tell() - + Section.ContentsOffset); + + Asm.writeSectionData(&FuncSection, Layout); + } + + // Apply the type index fixups for call_indirect etc. instructions. + for (size_t i = 0, e = TypeIndexFixups.size(); i < e; ++i) { + uint32_t Type = TypeIndexFixupTypes[i]; + unsigned Padding = PaddingFor5ByteULEB128(Type); + + const TypeIndexFixup &Fixup = TypeIndexFixups[i]; + uint64_t Offset = Fixup.Offset + + Fixup.FixupSection->getSectionOffset(); + + uint8_t Buffer[16]; + unsigned SizeLen = encodeULEB128(Type, Buffer, Padding); + assert(SizeLen == 5); + getStream().pwrite((char *)Buffer, SizeLen, + Section.ContentsOffset + Offset); + } + + // Apply fixups. + ApplyRelocations(CodeRelocations, getStream(), SymbolIndices, + Section.ContentsOffset); + + endSection(Section); + } + + // === Data Section ========================================================== + if (!DataBytes.empty()) { + startSection(Section, wasm::WASM_SEC_DATA); + + encodeULEB128(1, getStream()); // count + encodeULEB128(0, getStream()); // memory index + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(0, getStream()); // offset + write8(wasm::WASM_OPCODE_END); + encodeULEB128(DataBytes.size(), getStream()); // size + writeBytes(DataBytes); // data + + // Apply fixups. + ApplyRelocations(DataRelocations, getStream(), SymbolIndices, + Section.ContentsOffset); + + endSection(Section); + } + + // === Name Section ========================================================== + if (NumFuncImports != 0 || !Functions.empty()) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "name"); + + encodeULEB128(NumFuncImports + Functions.size(), getStream()); + for (const WasmImport &Import : Imports) { + if (Import.Kind == wasm::WASM_EXTERNAL_FUNCTION) { + encodeULEB128(Import.FieldName.size(), getStream()); + writeBytes(Import.FieldName); + encodeULEB128(0, getStream()); // local count, meaningless for imports + } + } + for (const WasmFunction &Func : Functions) { + encodeULEB128(Func.Sym->getName().size(), getStream()); + writeBytes(Func.Sym->getName()); + + // TODO: Local names. + encodeULEB128(0, getStream()); // local count + } + + endSection(Section); + } + + // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md + // for descriptions of the reloc sections. + + // === Code Reloc Section ==================================================== + if (!CodeRelocations.empty()) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "reloc.CODE"); + + write8(wasm::WASM_SEC_CODE); + + encodeULEB128(CodeRelocations.size(), getStream()); + + WriteRelocations(CodeRelocations, getStream(), SymbolIndices); + + endSection(Section); + } + + // === Data Reloc Section ==================================================== + if (!DataRelocations.empty()) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "reloc.DATA"); + + write8(wasm::WASM_SEC_DATA); + + encodeULEB128(DataRelocations.size(), getStream()); + + WriteRelocations(DataRelocations, getStream(), SymbolIndices); + + endSection(Section); + } + + // TODO: Translate the .comment section to the output. + + // TODO: Translate debug sections to the output. } MCObjectWriter *llvm::createWasmObjectWriter(MCWasmObjectTargetWriter *MOTW, diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp index 337c18ca57d..5b210742063 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyAsmBackend.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "MCTargetDesc/WebAssemblyFixupKinds.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCDirectives.h" @@ -70,6 +71,12 @@ public: : MCAsmBackend(), Is64Bit(Is64Bit) {} ~WebAssemblyAsmBackend() override {} + unsigned getNumFixupKinds() const override { + return WebAssembly::NumTargetFixupKinds; + } + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize, uint64_t Value, bool IsPCRel) const override; @@ -82,12 +89,6 @@ public: return false; } - unsigned getNumFixupKinds() const override { - // We currently just use the generic fixups in MCFixup.h and don't have any - // target-specific fixups. - return 0; - } - bool mayNeedRelaxation(const MCInst &Inst) const override { return false; } void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, @@ -131,6 +132,26 @@ WebAssemblyAsmBackendELF::createObjectWriter(raw_pwrite_stream &OS) const { return createWebAssemblyELFObjectWriter(OS, Is64Bit, 0); } +const MCFixupKindInfo & +WebAssemblyAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo Infos[WebAssembly::NumTargetFixupKinds] = { + // This table *must* be in the order that the fixup_* kinds are defined in + // WebAssemblyFixupKinds.h. + // + // Name Offset (bits) Size (bits) Flags + { "fixup_code_sleb128_i32", 0, 5*8, 0 }, + { "fixup_code_sleb128_i64", 0, 10*8, 0 }, + { "fixup_code_uleb128_i32", 0, 5*8, 0 }, + }; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + bool WebAssemblyAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const { if (Count == 0) diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h new file mode 100644 index 00000000000..b0af63c924b --- /dev/null +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyFixupKinds.h @@ -0,0 +1,31 @@ +//=- WebAssemblyFixupKinds.h - WebAssembly Specific Fixup Entries -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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_code_sleb128_i32 = FirstTargetFixupKind, // 32-bit signed + fixup_code_sleb128_i64, // 64-bit signed + fixup_code_uleb128_i32, // 32-bit unsigned + + fixup_code_global_index, // 32-bit unsigned + + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; +} // end namespace WebAssembly +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp index 91583ef119e..a0b00894749 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "MCTargetDesc/WebAssemblyFixupKinds.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/MC/MCCodeEmitter.h" @@ -48,9 +49,7 @@ class WebAssemblyMCCodeEmitter final : public MCCodeEmitter { public: WebAssemblyMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx) - : MCII(mcii), Ctx(ctx) { - (void)Ctx; - } + : MCII(mcii), Ctx(ctx) {} }; } // end anonymous namespace @@ -68,6 +67,13 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( assert(Binary < UINT8_MAX && "Multi-byte opcodes not supported yet"); OS << uint8_t(Binary); + // For br_table instructions, encode the size of the table. In the MCInst, + // there's an index operand, one operand for each table entry, and the + // default operand. + 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); @@ -82,6 +88,12 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( encodeSLEB128(int32_t(MO.getImm()), OS); } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) { encodeSLEB128(int64_t(MO.getImm()), OS); + } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) { + Fixups.push_back(MCFixup::create( + OS.tell() - Start, MCConstantExpr::create(MO.getImm(), Ctx), + MCFixupKind(WebAssembly::fixup_code_global_index), MI.getLoc())); + ++MCNumFixups; + encodeULEB128(uint64_t(MO.getImm()), OS); } else { encodeULEB128(uint64_t(MO.getImm()), OS); } @@ -107,14 +119,28 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( support::endian::Writer<support::little>(OS).write<double>(d); } } else if (MO.isExpr()) { + const MCOperandInfo &Info = Desc.OpInfo[i]; + llvm::MCFixupKind FixupKind; + size_t PaddedSize; + if (Info.OperandType == WebAssembly::OPERAND_I32IMM) { + FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i32); + PaddedSize = 5; + } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) { + FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i64); + PaddedSize = 10; + } else if (Info.OperandType == WebAssembly::OPERAND_FUNCTION32 || + Info.OperandType == WebAssembly::OPERAND_OFFSET32 || + Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) { + FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32); + PaddedSize = 5; + } else { + llvm_unreachable("unexpected symbolic operand kind"); + } Fixups.push_back(MCFixup::create( OS.tell() - Start, MO.getExpr(), - STI.getTargetTriple().isArch64Bit() ? FK_Data_8 : FK_Data_4, - MI.getLoc())); + FixupKind, MI.getLoc())); ++MCNumFixups; - encodeULEB128(STI.getTargetTriple().isArch64Bit() ? UINT64_MAX - : uint64_t(UINT32_MAX), - OS); + encodeULEB128(0, OS, PaddedSize - 1); } else { llvm_unreachable("unexpected operand kind"); } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index 93ad57407e2..1108f5f5a32 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -68,7 +68,9 @@ enum OperandType { /// p2align immediate for load and store address alignment. OPERAND_P2ALIGN, /// signature immediate for block/loop. - OPERAND_SIGNATURE + OPERAND_SIGNATURE, + /// type signature immediate for call_indirect. + OPERAND_TYPEINDEX, }; } // end namespace WebAssembly diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp index b8a0f589d7f..c198a262241 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -22,8 +22,10 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCSymbolWasm.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Wasm.h" using namespace llvm; WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S) @@ -51,14 +53,28 @@ static void PrintTypes(formatted_raw_ostream &OS, ArrayRef<MVT> Types) { OS << '\n'; } -void WebAssemblyTargetAsmStreamer::emitParam(ArrayRef<MVT> Types) { - OS << "\t.param \t"; - PrintTypes(OS, Types); +void WebAssemblyTargetAsmStreamer::emitParam(MCSymbol *Symbol, + ArrayRef<MVT> Types) { + if (!Types.empty()) { + OS << "\t.param \t"; + + // FIXME: Currently this applies to the "current" function; it may + // be cleaner to specify an explicit symbol as part of the directive. + + PrintTypes(OS, Types); + } } -void WebAssemblyTargetAsmStreamer::emitResult(ArrayRef<MVT> Types) { - OS << "\t.result \t"; - PrintTypes(OS, Types); +void WebAssemblyTargetAsmStreamer::emitResult(MCSymbol *Symbol, + ArrayRef<MVT> Types) { + if (!Types.empty()) { + OS << "\t.result \t"; + + // FIXME: Currently this applies to the "current" function; it may + // be cleaner to specify an explicit symbol as part of the directive. + + PrintTypes(OS, Types); + } } void WebAssemblyTargetAsmStreamer::emitLocal(ArrayRef<MVT> Types) { @@ -92,11 +108,13 @@ void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) { OS << "\t.indidx \t" << *Value << '\n'; } -void WebAssemblyTargetELFStreamer::emitParam(ArrayRef<MVT> Types) { +void WebAssemblyTargetELFStreamer::emitParam(MCSymbol *Symbol, + ArrayRef<MVT> Types) { // Nothing to emit; params are declared as part of the function signature. } -void WebAssemblyTargetELFStreamer::emitResult(ArrayRef<MVT> Types) { +void WebAssemblyTargetELFStreamer::emitResult(MCSymbol *Symbol, + ArrayRef<MVT> Types) { // Nothing to emit; results are declared as part of the function signature. } @@ -123,22 +141,52 @@ void WebAssemblyTargetELFStreamer::emitIndirectFunctionType( void WebAssemblyTargetELFStreamer::emitGlobalImport(StringRef name) { } -void WebAssemblyTargetWasmStreamer::emitParam(ArrayRef<MVT> Types) { - // Nothing to emit; params are declared as part of the function signature. +static unsigned MVT2WasmType(MVT Ty) { + switch (Ty.SimpleTy) { + case MVT::i32: return wasm::WASM_TYPE_I32; + case MVT::i64: return wasm::WASM_TYPE_I64; + case MVT::f32: return wasm::WASM_TYPE_F32; + case MVT::f64: return wasm::WASM_TYPE_F64; + default: llvm_unreachable("unsupported type"); + } } -void WebAssemblyTargetWasmStreamer::emitResult(ArrayRef<MVT> Types) { - // Nothing to emit; results are declared as part of the function signature. +void WebAssemblyTargetWasmStreamer::emitParam(MCSymbol *Symbol, + ArrayRef<MVT> Types) { + SmallVector<unsigned, 4> Params; + for (MVT Ty : Types) + Params.push_back(MVT2WasmType(Ty)); + + cast<MCSymbolWasm>(Symbol)->setParams(std::move(Params)); +} + +void WebAssemblyTargetWasmStreamer::emitResult(MCSymbol *Symbol, + ArrayRef<MVT> Types) { + SmallVector<unsigned, 4> Returns; + for (MVT Ty : Types) + Returns.push_back(MVT2WasmType(Ty)); + + cast<MCSymbolWasm>(Symbol)->setReturns(std::move(Returns)); } void WebAssemblyTargetWasmStreamer::emitLocal(ArrayRef<MVT> Types) { - Streamer.EmitULEB128IntValue(Types.size()); - for (MVT Type : Types) - Streamer.EmitIntValue(int64_t(WebAssembly::toValType(Type)), 1); + SmallVector<std::pair<MVT, uint32_t>, 4> Grouped; + for (MVT 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); + Streamer.EmitULEB128IntValue(uint64_t(WebAssembly::toValType(Pair.first))); + } } void WebAssemblyTargetWasmStreamer::emitEndFunc() { - Streamer.EmitIntValue(WebAssembly::End, 1); + llvm_unreachable(".end_func is not needed for direct wasm output"); } void WebAssemblyTargetWasmStreamer::emitIndIdx(const MCExpr *Value) { diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h index 35b14bc0ba5..1ad3ffcf408 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -31,9 +31,9 @@ public: explicit WebAssemblyTargetStreamer(MCStreamer &S); /// .param - virtual void emitParam(ArrayRef<MVT> Types) = 0; + virtual void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) = 0; /// .result - virtual void emitResult(ArrayRef<MVT> Types) = 0; + virtual void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) = 0; /// .local virtual void emitLocal(ArrayRef<MVT> Types) = 0; /// .endfunc @@ -57,8 +57,8 @@ class WebAssemblyTargetAsmStreamer final : public WebAssemblyTargetStreamer { public: WebAssemblyTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); - void emitParam(ArrayRef<MVT> Types) override; - void emitResult(ArrayRef<MVT> Types) override; + void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override; + void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override; void emitLocal(ArrayRef<MVT> Types) override; void emitEndFunc() override; void emitIndirectFunctionType(StringRef name, @@ -73,8 +73,8 @@ class WebAssemblyTargetELFStreamer final : public WebAssemblyTargetStreamer { public: explicit WebAssemblyTargetELFStreamer(MCStreamer &S); - void emitParam(ArrayRef<MVT> Types) override; - void emitResult(ArrayRef<MVT> Types) override; + void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override; + void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override; void emitLocal(ArrayRef<MVT> Types) override; void emitEndFunc() override; void emitIndirectFunctionType(StringRef name, @@ -89,8 +89,8 @@ class WebAssemblyTargetWasmStreamer final : public WebAssemblyTargetStreamer { public: explicit WebAssemblyTargetWasmStreamer(MCStreamer &S); - void emitParam(ArrayRef<MVT> Types) override; - void emitResult(ArrayRef<MVT> Types) override; + void emitParam(MCSymbol *Symbol, ArrayRef<MVT> Types) override; + void emitResult(MCSymbol *Symbol, ArrayRef<MVT> Types) override; void emitLocal(ArrayRef<MVT> Types) override; void emitEndFunc() override; void emitIndirectFunctionType(StringRef name, diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp index 3fb3e65fb91..2846ec5e933 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -14,9 +14,13 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "MCTargetDesc/WebAssemblyFixupKinds.h" #include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCSymbolWasm.h" #include "llvm/MC/MCWasmObjectWriter.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Wasm.h" using namespace llvm; namespace { @@ -33,12 +37,52 @@ private: WebAssemblyWasmObjectWriter::WebAssemblyWasmObjectWriter(bool Is64Bit) : MCWasmObjectTargetWriter(Is64Bit) {} +// Test whether the given expression computes a function address. +static bool IsFunctionExpr(const MCExpr *Expr) { + if (const MCSymbolRefExpr *SyExp = + dyn_cast<MCSymbolRefExpr>(Expr)) + return cast<MCSymbolWasm>(SyExp->getSymbol()).isFunction(); + + if (const MCBinaryExpr *BinOp = + dyn_cast<MCBinaryExpr>(Expr)) + return IsFunctionExpr(BinOp->getLHS()) != IsFunctionExpr(BinOp->getRHS()); + + if (const MCUnaryExpr *UnOp = + dyn_cast<MCUnaryExpr>(Expr)) + return IsFunctionExpr(UnOp->getSubExpr()); + + return false; +} + unsigned WebAssemblyWasmObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const { - llvm_unreachable("Relocations not yet implemented"); - return 0; + // WebAssembly functions are not allocated in the data address space. To + // resolve a pointer to a function, we must use a special relocation type. + bool IsFunction = IsFunctionExpr(Fixup.getValue()); + + assert(!IsPCRel); + switch (unsigned(Fixup.getKind())) { + case WebAssembly::fixup_code_sleb128_i32: + if (IsFunction) + return wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB; + return wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB; + case WebAssembly::fixup_code_sleb128_i64: + llvm_unreachable("fixup_sleb128_i64 not implemented yet"); + case WebAssembly::fixup_code_uleb128_i32: + if (IsFunction) + return wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB; + return wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB; + case FK_Data_4: + if (IsFunction) + return wasm::R_WEBASSEMBLY_TABLE_INDEX_I32; + return wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32; + case FK_Data_8: + llvm_unreachable("FK_Data_8 not implemented yet"); + default: + llvm_unreachable("unimplemented fixup kind"); + } } MCObjectWriter *llvm::createWebAssemblyWasmObjectWriter(raw_pwrite_stream &OS, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 5b4b82eb560..56bb550c72f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -14,6 +14,7 @@ /// //===----------------------------------------------------------------------===// +#include "WebAssemblyAsmPrinter.h" #include "InstPrinter/WebAssemblyInstPrinter.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "MCTargetDesc/WebAssemblyTargetStreamer.h" @@ -21,13 +22,13 @@ #include "WebAssemblyMCInstLower.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyRegisterInfo.h" -#include "WebAssemblySubtarget.h" #include "llvm/ADT/StringExtras.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" @@ -38,56 +39,6 @@ using namespace llvm; #define DEBUG_TYPE "asm-printer" -namespace { - -class WebAssemblyAsmPrinter final : public AsmPrinter { - const MachineRegisterInfo *MRI; - WebAssemblyFunctionInfo *MFI; - -public: - WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) - : AsmPrinter(TM, std::move(Streamer)), MRI(nullptr), MFI(nullptr) {} - -private: - StringRef getPassName() const override { - return "WebAssembly Assembly Printer"; - } - - //===------------------------------------------------------------------===// - // MachineFunctionPass Implementation. - //===------------------------------------------------------------------===// - - bool runOnMachineFunction(MachineFunction &MF) override { - MRI = &MF.getRegInfo(); - MFI = MF.getInfo<WebAssemblyFunctionInfo>(); - return AsmPrinter::runOnMachineFunction(MF); - } - - //===------------------------------------------------------------------===// - // AsmPrinter Implementation. - //===------------------------------------------------------------------===// - - void EmitEndOfAsmFile(Module &M) override; - void EmitJumpTableInfo() override; - void EmitConstantPool() override; - void EmitFunctionBodyStart() override; - void EmitFunctionBodyEnd() override; - void EmitInstruction(const MachineInstr *MI) override; - const MCExpr *lowerConstant(const Constant *CV) override; - bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, - unsigned AsmVariant, const char *ExtraCode, - raw_ostream &OS) override; - bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, - unsigned AsmVariant, const char *ExtraCode, - raw_ostream &OS) override; - - MVT getRegType(unsigned RegNo) const; - std::string regToString(const MachineOperand &MO); - WebAssemblyTargetStreamer *getTargetStreamer(); -}; - -} // end anonymous namespace - //===----------------------------------------------------------------------===// // Helpers. //===----------------------------------------------------------------------===// @@ -135,7 +86,10 @@ void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { } for (const auto &G : M.globals()) { if (!G.hasInitializer() && G.hasExternalLinkage()) { + uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType()); getTargetStreamer()->emitGlobalImport(G.getGlobalIdentifier()); + OutStreamer->emitELFSize(getSymbol(&G), + MCConstantExpr::create(Size, OutContext)); } } } @@ -150,8 +104,7 @@ void WebAssemblyAsmPrinter::EmitJumpTableInfo() { } void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { - if (!MFI->getParams().empty()) - getTargetStreamer()->emitParam(MFI->getParams()); + getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams()); SmallVector<MVT, 4> ResultVTs; const Function &F(*MF->getFunction()); @@ -169,23 +122,26 @@ void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { // If the return type needs to be legalized it will get converted into // passing a pointer. if (ResultVTs.size() == 1) - getTargetStreamer()->emitResult(ResultVTs); - - // FIXME: When ExplicitLocals is enabled by default, we won't need - // to define the locals here (and MFI can go back to being pointer-to-const). - for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) { - unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx); - unsigned WAReg = MFI->getWAReg(VReg); - // Don't declare unused registers. - if (WAReg == WebAssemblyFunctionInfo::UnusedReg) - continue; - // Don't redeclare parameters. - if (WAReg < MFI->getParams().size()) - continue; - // Don't declare stackified registers. - if (int(WAReg) < 0) - continue; - MFI->addLocal(getRegType(VReg)); + getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs); + else + getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>()); + + if (TM.getTargetTriple().isOSBinFormatELF()) { + assert(MFI->getLocals().empty()); + for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) { + unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx); + unsigned WAReg = MFI->getWAReg(VReg); + // Don't declare unused registers. + if (WAReg == WebAssemblyFunctionInfo::UnusedReg) + continue; + // Don't redeclare parameters. + if (WAReg < MFI->getParams().size()) + continue; + // Don't declare stackified registers. + if (int(WAReg) < 0) + continue; + MFI->addLocal(getRegType(VReg)); + } } getTargetStreamer()->emitLocal(MFI->getLocals()); @@ -194,7 +150,8 @@ void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { } void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() { - getTargetStreamer()->emitEndFunc(); + if (TM.getTargetTriple().isOSBinFormatELF()) + getTargetStreamer()->emitEndFunc(); } void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h new file mode 100644 index 00000000000..c8917b8d7e4 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -0,0 +1,77 @@ +// WebAssemblyAsmPrinter.h - WebAssembly implementation of AsmPrinter-*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYASMPRINTER_H +#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYASMPRINTER_H + +#include "WebAssemblySubtarget.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { +class MCSymbol; +class WebAssemblyFunctionInfo; +class WebAssemblyTargetStreamer; +class WebAssemblyMCInstLower; + +class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : public AsmPrinter { + const WebAssemblySubtarget *Subtarget; + const MachineRegisterInfo *MRI; + WebAssemblyFunctionInfo *MFI; + +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; } + + //===------------------------------------------------------------------===// + // 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 EmitJumpTableInfo() override; + void EmitConstantPool() override; + void EmitFunctionBodyStart() override; + void EmitFunctionBodyEnd() override; + void EmitInstruction(const MachineInstr *MI) override; + const MCExpr *lowerConstant(const Constant *CV) override; + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &OS) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, 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/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 49b9754e6b6..cf094b9b4d7 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -488,6 +488,15 @@ static void FixEndsAtEndOfFunction( } } +// WebAssembly functions end with an end instruction, as if the function body +// were a block. +static void AppendEndToFunction( + MachineFunction &MF, + const WebAssemblyInstrInfo &TII) { + BuildMI(MF.back(), MF.back().end(), DebugLoc(), + TII.get(WebAssembly::END_FUNCTION)); +} + /// Insert LOOP and BLOCK markers at appropriate places. static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI, const WebAssemblyInstrInfo &TII, @@ -555,6 +564,11 @@ static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI, // Fix up block/loop signatures at the end of the function to conform to // WebAssembly's rules. FixEndsAtEndOfFunction(MF, MFI, BlockTops, LoopTops); + + // Add an end instruction at the end of the function body. + if (!MF.getSubtarget<WebAssemblySubtarget>() + .getTargetTriple().isOSBinFormatELF()) + AppendEndToFunction(MF, TII); } bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp index fc0a01ca30e..bc6360aafd6 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp @@ -97,15 +97,28 @@ bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) { MI.setDesc(Desc); // Rewrite argument order - auto Uses = MI.explicit_uses(); - MachineInstr::mop_iterator it = Uses.begin(); - const MachineOperand MO = *it; + 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. - it->ChangeToImmediate(0); - - MI.addOperand(MF, MO); + 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); DEBUG(dbgs() << " After transform: " << MI); Changed = true; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp index 04ede7ff110..50ee79b7079 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -60,7 +60,25 @@ FunctionPass *llvm::createWebAssemblyExplicitLocals() { /// if it doesn't yet have one. static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local, unsigned &CurLocal, unsigned Reg) { - return Reg2Local.insert(std::make_pair(Reg, CurLocal++)).first->second; + 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; + llvm_unreachable("Unexpected register class"); } /// Get the appropriate get_local opcode for the given register class. @@ -176,6 +194,12 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { // Start assigning local numbers after the last parameter. unsigned CurLocal = 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;) { @@ -224,15 +248,26 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { assert(MI.getDesc().getNumDefs() <= 1); if (MI.getDesc().getNumDefs() == 1) { unsigned OldReg = MI.getOperand(0).getReg(); - if (!MFI.isVRegStackified(OldReg) && !MRI.use_empty(OldReg)) { - unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + if (!MFI.isVRegStackified(OldReg)) { const TargetRegisterClass *RC = MRI.getRegClass(OldReg); unsigned NewReg = MRI.createVirtualRegister(RC); auto InsertPt = std::next(MachineBasicBlock::iterator(&MI)); - unsigned Opc = getSetLocalOpcode(RC); - BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) - .addImm(LocalId) - .addReg(NewReg); + if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) { + MI.eraseFromParent(); + Changed = true; + continue; + } + if (UseEmpty[TargetRegisterInfo::virtReg2Index(OldReg)]) { + unsigned Opc = getDropOpcode(RC); + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) + .addReg(NewReg); + } else { + unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + unsigned Opc = getSetLocalOpcode(RC); + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) + .addImm(LocalId) + .addReg(NewReg); + } MI.getOperand(0).setReg(NewReg); MFI.stackifyVReg(NewReg); Changed = true; @@ -278,13 +313,16 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { } // Define the locals. + // TODO: Sort the locals for better compression. + MFI.setNumLocals(CurLocal - MFI.getParams().size()); for (size_t i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) { unsigned Reg = TargetRegisterInfo::index2VirtReg(i); auto I = Reg2Local.find(Reg); if (I == Reg2Local.end() || I->second < MFI.getParams().size()) continue; - MFI.addLocal(typeForRegClass(MRI.getRegClass(Reg))); + MFI.setLocal(I->second - MFI.getParams().size(), + typeForRegClass(MRI.getRegClass(Reg))); Changed = true; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp index a6a2c0bf06a..b325b0953d0 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -24,6 +24,7 @@ #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" @@ -151,6 +152,8 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, auto &MRI = MF.getRegInfo(); auto InsertPt = MBB.begin(); + while (InsertPt != MBB.end() && WebAssembly::isArgument(*InsertPt)) + ++InsertPt; DebugLoc DL; const TargetRegisterClass *PtrRC = diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td index 047f4be066c..73d1d4be293 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -30,13 +30,15 @@ multiclass CALL<WebAssemblyRegClass vt, string prefix> { [(set vt:$dst, (WebAssemblycall1 (i32 imm:$callee)))], !strconcat(prefix, "call\t$dst, $callee"), 0x10>; + let isCodeGenOnly = 1 in { def PCALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops), [(set vt:$dst, (WebAssemblycall1 I32:$callee))], "PSEUDO CALL INDIRECT\t$callee">; } // isCodeGenOnly = 1 - def CALL_INDIRECT_#vt : I<(outs vt:$dst), (ins i32imm:$flags, variable_ops), + def CALL_INDIRECT_#vt : I<(outs vt:$dst), + (ins TypeIndex:$type, i32imm:$flags, variable_ops), [], !strconcat(prefix, "call_indirect\t$dst"), 0x11>; @@ -48,6 +50,7 @@ multiclass SIMD_CALL<ValueType vt, string prefix> { (WebAssemblycall1 (i32 imm:$callee)))], !strconcat(prefix, "call\t$dst, $callee"), 0x10>; + let isCodeGenOnly = 1 in { def PCALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst), (ins I32:$callee, variable_ops), @@ -57,7 +60,8 @@ multiclass SIMD_CALL<ValueType vt, string prefix> { } // isCodeGenOnly = 1 def CALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst), - (ins i32imm:$flags, variable_ops), + (ins TypeIndex:$type, i32imm:$flags, + variable_ops), [], !strconcat(prefix, "call_indirect\t$dst"), 0x11>; @@ -76,13 +80,15 @@ let Uses = [SP32, SP64], isCall = 1 in { def CALL_VOID : I<(outs), (ins function32_op:$callee, variable_ops), [(WebAssemblycall0 (i32 imm:$callee))], "call \t$callee", 0x10>; + let isCodeGenOnly = 1 in { def PCALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops), [(WebAssemblycall0 I32:$callee)], "PSEUDO CALL INDIRECT\t$callee">; } // isCodeGenOnly = 1 - def CALL_INDIRECT_VOID : I<(outs), (ins i32imm:$flags, variable_ops), + def CALL_INDIRECT_VOID : I<(outs), + (ins TypeIndex:$type, i32imm:$flags, variable_ops), [], "call_indirect\t", 0x11>; } // Uses = [SP32,SP64], isCall = 1 diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td index 1146431e6b7..39cb1ca336f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -64,9 +64,12 @@ let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in { def BLOCK : I<(outs), (ins Signature:$sig), [], "block \t$sig", 0x02>; def LOOP : I<(outs), (ins Signature:$sig), [], "loop \t$sig", 0x03>; -// END_BLOCK and END_LOOP are represented with the same opcode in wasm. +// END_BLOCK, END_LOOP, and END_FUNCTION are represented with the same opcode +// in wasm. def END_BLOCK : I<(outs), (ins), [], "end_block", 0x0b>; def END_LOOP : I<(outs), (ins), [], "end_loop", 0x0b>; +let isTerminator = 1, isBarrier = 1 in +def END_FUNCTION : I<(outs), (ins), [], "end_function", 0x0b>; } // Uses = [VALUE_STACK], Defs = [VALUE_STACK] multiclass RETURN<WebAssemblyRegClass vt> { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index 79472cd8e48..a601b575f57 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -107,6 +107,9 @@ def Signature : Operand<i32> { } } // OperandType = "OPERAND_SIGNATURE" +let OperandType = "OPERAND_TYPEINDEX" in +def TypeIndex : Operand<i32>; + } // OperandNamespace = "WebAssembly" //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index f3384210b0e..354662d9d3d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -14,7 +14,10 @@ //===----------------------------------------------------------------------===// #include "WebAssemblyMCInstLower.h" +#include "WebAssemblyAsmPrinter.h" #include "WebAssemblyMachineFunctionInfo.h" +#include "WebAssemblyRuntimeLibcallSignatures.h" +#include "WebAssemblyUtilities.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/IR/Constants.h" @@ -22,18 +25,85 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCSymbolWasm.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; MCSymbol * WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { - return Printer.getSymbol(MO.getGlobal()); + const GlobalValue *Global = MO.getGlobal(); + MCSymbol *Sym = Printer.getSymbol(Global); + if (isa<MCSymbolELF>(Sym)) + return Sym; + + MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym); + + 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<unsigned, 4> Returns; + SmallVector<unsigned, 4> Params; + + WebAssembly::ValType iPTR = + MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() ? + WebAssembly::ValType::I64 : + WebAssembly::ValType::I32; + + SmallVector<MVT, 4> ResultMVTs; + ComputeLegalValueVTs(CurrentFunc, TM, FuncTy->getReturnType(), ResultMVTs); + // WebAssembly can't currently handle returning tuples. + if (ResultMVTs.size() <= 1) + for (MVT ResultMVT : ResultMVTs) + Returns.push_back(unsigned(WebAssembly::toValType(ResultMVT))); + else + Params.push_back(unsigned(iPTR)); + + for (Type *Ty : FuncTy->params()) { + SmallVector<MVT, 4> ParamMVTs; + ComputeLegalValueVTs(CurrentFunc, TM, Ty, ParamMVTs); + for (MVT ParamMVT : ParamMVTs) + Params.push_back(unsigned(WebAssembly::toValType(ParamMVT))); + } + + if (FuncTy->isVarArg()) + Params.push_back(unsigned(iPTR)); + + WasmSym->setReturns(std::move(Returns)); + WasmSym->setParams(std::move(Params)); + WasmSym->setIsFunction(true); + } + + return WasmSym; } MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol( const MachineOperand &MO) const { - return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); + const char *Name = MO.getSymbolName(); + MCSymbol *Sym = Printer.GetExternalSymbolSymbol(Name); + if (isa<MCSymbolELF>(Sym)) + return Sym; + + MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym); + const WebAssemblySubtarget &Subtarget = Printer.getSubtarget(); + + // __stack_pointer is a global variable; all other external symbols used by + // CodeGen are functions. + if (strcmp(Name, "__stack_pointer") == 0) + return WasmSym; + + SmallVector<unsigned, 4> Returns; + SmallVector<unsigned, 4> Params; + GetSignature(Subtarget, Name, Returns, Params); + + WasmSym->setReturns(std::move(Returns)); + WasmSym->setParams(std::move(Params)); + WasmSym->setIsFunction(true); + + return WasmSym; } MCOperand WebAssemblyMCInstLower::LowerSymbolOperand(MCSymbol *Sym, @@ -42,6 +112,9 @@ MCOperand WebAssemblyMCInstLower::LowerSymbolOperand(MCSymbol *Sym, MCSymbolRefExpr::VariantKind VK = IsFunc ? MCSymbolRefExpr::VK_WebAssembly_FUNCTION : MCSymbolRefExpr::VK_None; + if (!isa<MCSymbolELF>(Sym)) + cast<MCSymbolWasm>(Sym)->setIsFunction(IsFunc); + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, VK, Ctx); if (Offset != 0) { @@ -54,10 +127,24 @@ MCOperand WebAssemblyMCInstLower::LowerSymbolOperand(MCSymbol *Sym, return MCOperand::createExpr(Expr); } +// Return the WebAssembly type associated with the given register class. +static unsigned getType(const TargetRegisterClass *RC) { + if (RC == &WebAssembly::I32RegClass) + return unsigned(WebAssembly::ExprType::I32); + if (RC == &WebAssembly::I64RegClass) + return unsigned(WebAssembly::ExprType::I64); + if (RC == &WebAssembly::F32RegClass) + return unsigned(WebAssembly::ExprType::F32); + if (RC == &WebAssembly::F64RegClass) + return unsigned(WebAssembly::ExprType::F64); + 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); @@ -80,6 +167,41 @@ void WebAssemblyMCInstLower::Lower(const MachineInstr *MI, 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"); + if (!isa<MCSymbolELF>(Sym)) { + SmallVector<unsigned, 4> Returns; + SmallVector<unsigned, 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)) + Params.pop_back(); + + MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym); + WasmSym->setReturns(std::move(Returns)); + WasmSym->setParams(std::move(Params)); + WasmSym->setIsFunction(true); + + const MCExpr *Expr = + MCSymbolRefExpr::create(WasmSym, + MCSymbolRefExpr::VK_WebAssembly_TYPEINDEX, + Ctx); + MCOp = MCOperand::createExpr(Expr); + break; + } + } + } MCOp = MCOperand::createImm(MO.getImm()); break; case MachineOperand::MO_FPImmediate: { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h index ab4ba1c28d5..d1d2794c3b8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h @@ -20,7 +20,7 @@ #include "llvm/Support/Compiler.h" namespace llvm { -class AsmPrinter; +class WebAssemblyAsmPrinter; class MCContext; class MCSymbol; class MachineInstr; @@ -29,7 +29,7 @@ class MachineOperand; /// This class is used to lower an MachineInstr into an MCInst. class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower { MCContext &Ctx; - AsmPrinter &Printer; + WebAssemblyAsmPrinter &Printer; MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; @@ -37,7 +37,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower { bool IsFunc) const; public: - WebAssemblyMCInstLower(MCContext &ctx, AsmPrinter &printer) + WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer) : Ctx(ctx), Printer(printer) {} void Lower(const MachineInstr *MI, MCInst &OutMI) const; }; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h index 756619bebbe..1fcbb7791d4 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h @@ -60,6 +60,8 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo { void addResult(MVT VT) { Results.push_back(VT); } const std::vector<MVT> &getResults() const { return Results; } + 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; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp index 6303a1638bc..d2fbc5a2230 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp @@ -80,8 +80,18 @@ static bool MaybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB, return false; if (&MBB != &MF.back()) return false; - if (&MI != &MBB.back()) - return false; + if (MF.getSubtarget<WebAssemblySubtarget>() + .getTargetTriple().isOSBinFormatELF()) { + if (&MI != &MBB.back()) + return false; + } else { + 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 diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 0651a1c11b4..04bf7b118b7 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -85,6 +85,14 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine( // '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. + if (!TT.isOSBinFormatELF()) { + this->Options.FunctionSections = true; + this->Options.DataSections = true; + this->Options.UniqueSectionNames = true; + } + initAsmInfo(); // Note that we don't use setRequiresStructuredCFG(true). It disables @@ -264,12 +272,14 @@ void WebAssemblyPassConfig::addPreEmitPass() { addPass(createWebAssemblyRegColoring()); } + // Eliminate multiple-entry loops. Do this before inserting explicit get_local + // and set_local operators because we create a new variable that we want + // converted into a local. + addPass(createWebAssemblyFixIrreducibleControlFlow()); + // Insert explicit get_local and set_local operators. addPass(createWebAssemblyExplicitLocals()); - // Eliminate multiple-entry loops. - addPass(createWebAssemblyFixIrreducibleControlFlow()); - // Put the CFG in structured form; insert BLOCK and LOOP markers. addPass(createWebAssemblyCFGStackify()); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp index a0049c147d2..2fb2327cf67 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -69,3 +69,20 @@ bool WebAssembly::isChild(const MachineInstr &MI, return TargetRegisterInfo::isVirtualRegister(Reg) && MFI.isVRegStackified(Reg); } + +bool WebAssembly::isCallIndirect(const MachineInstr &MI) { + switch (MI.getOpcode()) { + case WebAssembly::CALL_INDIRECT_VOID: + case WebAssembly::CALL_INDIRECT_I32: + case WebAssembly::CALL_INDIRECT_I64: + case WebAssembly::CALL_INDIRECT_F32: + case WebAssembly::CALL_INDIRECT_F64: + case WebAssembly::CALL_INDIRECT_v16i8: + case WebAssembly::CALL_INDIRECT_v8i16: + case WebAssembly::CALL_INDIRECT_v4i32: + case WebAssembly::CALL_INDIRECT_v4f32: + return true; + default: + return false; + } +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h index eb114403d14..cccc96152f2 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h @@ -27,6 +27,7 @@ bool isArgument(const MachineInstr &MI); bool isCopy(const MachineInstr &MI); bool isTee(const MachineInstr &MI); bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI); +bool isCallIndirect(const MachineInstr &MI); } // end namespace WebAssembly } // end namespace llvm diff --git a/llvm/test/CodeGen/WebAssembly/call.ll b/llvm/test/CodeGen/WebAssembly/call.ll index 1a9d5b8fb8e..1cf42242a6c 100644 --- a/llvm/test/CodeGen/WebAssembly/call.ll +++ b/llvm/test/CodeGen/WebAssembly/call.ll @@ -4,7 +4,7 @@ ; Test that basic call operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare i32 @i32_nullary() declare i32 @i32_unary(i32) @@ -61,7 +61,8 @@ define void @call_void_nullary() { ; CHECK-LABEL: call_i32_unary: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_unary@FUNCTION, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_unary@FUNCTION, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @call_i32_unary(i32 %a) { %r = call i32 @i32_unary(i32 %a) @@ -71,7 +72,9 @@ define i32 @call_i32_unary(i32 %a) { ; CHECK-LABEL: call_i32_binary: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_binary@FUNCTION, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: {{^}} i32.call $push[[NUM:[0-9]+]]=, i32_binary@FUNCTION, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @call_i32_binary(i32 %a, i32 %b) { %r = call i32 @i32_binary(i32 %a, i32 %b) @@ -80,7 +83,8 @@ define i32 @call_i32_binary(i32 %a, i32 %b) { ; CHECK-LABEL: call_indirect_void: ; CHECK-NEXT: .param i32{{$}} -; CHECK-NEXT: {{^}} call_indirect $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: {{^}} call_indirect $pop[[L0]]{{$}} ; CHECK-NEXT: return{{$}} define void @call_indirect_void(void ()* %callee) { call void %callee() @@ -90,7 +94,8 @@ define void @call_indirect_void(void ()* %callee) { ; CHECK-LABEL: call_indirect_i32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: {{^}} i32.call_indirect $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: {{^}} i32.call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @call_indirect_i32(i32 ()* %callee) { %t = call i32 %callee() @@ -99,7 +104,9 @@ define i32 @call_indirect_i32(i32 ()* %callee) { ; CHECK-LABEL: call_indirect_arg: ; CHECK-NEXT: .param i32, i32{{$}} -; CHECK-NEXT: {{^}} call_indirect $1, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: {{^}} call_indirect $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return{{$}} define void @call_indirect_arg(void (i32)* %callee, i32 %arg) { call void %callee(i32 %arg) @@ -108,7 +115,11 @@ define void @call_indirect_arg(void (i32)* %callee, i32 %arg) { ; CHECK-LABEL: call_indirect_arg_2: ; CHECK-NEXT: .param i32, i32, i32{{$}} -; CHECK-NEXT: {{^}} i32.call_indirect $drop=, $1, $2, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 2{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: {{^}} i32.call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]], $pop[[L2]]{{$}} +; CHECK-NEXT: drop $pop[[NUM]]{{$}} ; CHECK-NEXT: return{{$}} define void @call_indirect_arg_2(i32 (i32, i32)* %callee, i32 %arg, i32 %arg2) { call i32 %callee(i32 %arg, i32 %arg2) diff --git a/llvm/test/CodeGen/WebAssembly/cfi.ll b/llvm/test/CodeGen/WebAssembly/cfi.ll index e5664ba73a0..992e0f0c63d 100644 --- a/llvm/test/CodeGen/WebAssembly/cfi.ll +++ b/llvm/test/CodeGen/WebAssembly/cfi.ll @@ -3,7 +3,7 @@ ; Tests that we correctly assign indexes for control flow integrity. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" @0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16 diff --git a/llvm/test/CodeGen/WebAssembly/comparisons_f32.ll b/llvm/test/CodeGen/WebAssembly/comparisons_f32.ll index 10e037d57a7..8051b25689d 100644 --- a/llvm/test/CodeGen/WebAssembly/comparisons_f32.ll +++ b/llvm/test/CodeGen/WebAssembly/comparisons_f32.ll @@ -4,13 +4,17 @@ ; expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: ord_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.eq $push[[NUM0:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.eq $push[[NUM1:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.eq $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.eq $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} ; CHECK-NEXT: i32.and $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} ; CHECK-NEXT: return $pop[[NUM2]]{{$}} define i32 @ord_f32(float %x, float %y) { @@ -22,8 +26,12 @@ define i32 @ord_f32(float %x, float %y) { ; CHECK-LABEL: uno_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.ne $push[[NUM0:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.ne $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} ; CHECK-NEXT: return $pop[[NUM2]]{{$}} define i32 @uno_f32(float %x, float %y) { @@ -35,7 +43,9 @@ define i32 @uno_f32(float %x, float %y) { ; CHECK-LABEL: oeq_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.eq $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @oeq_f32(float %x, float %y) { %a = fcmp oeq float %x, %y @@ -44,7 +54,7 @@ define i32 @oeq_f32(float %x, float %y) { } ; CHECK-LABEL: une_f32: -; CHECK: f32.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f32.ne $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @une_f32(float %x, float %y) { %a = fcmp une float %x, %y @@ -53,7 +63,7 @@ define i32 @une_f32(float %x, float %y) { } ; CHECK-LABEL: olt_f32: -; CHECK: f32.lt $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f32.lt $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @olt_f32(float %x, float %y) { %a = fcmp olt float %x, %y @@ -62,7 +72,7 @@ define i32 @olt_f32(float %x, float %y) { } ; CHECK-LABEL: ole_f32: -; CHECK: f32.le $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f32.le $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ole_f32(float %x, float %y) { %a = fcmp ole float %x, %y @@ -71,7 +81,7 @@ define i32 @ole_f32(float %x, float %y) { } ; CHECK-LABEL: ogt_f32: -; CHECK: f32.gt $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f32.gt $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ogt_f32(float %x, float %y) { %a = fcmp ogt float %x, %y @@ -80,7 +90,7 @@ define i32 @ogt_f32(float %x, float %y) { } ; CHECK-LABEL: oge_f32: -; CHECK: f32.ge $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f32.ge $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @oge_f32(float %x, float %y) { %a = fcmp oge float %x, %y @@ -93,9 +103,15 @@ define i32 @oge_f32(float %x, float %y) { ; CHECK-LABEL: ueq_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.eq $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.eq $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -108,9 +124,15 @@ define i32 @ueq_f32(float %x, float %y) { ; CHECK-LABEL: one_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.ne $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f32.eq $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.eq $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.eq $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.eq $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.and $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.and $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]] @@ -123,9 +145,15 @@ define i32 @one_f32(float %x, float %y) { ; CHECK-LABEL: ult_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.lt $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.lt $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -138,9 +166,15 @@ define i32 @ult_f32(float %x, float %y) { ; CHECK-LABEL: ule_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.le $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.le $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -153,9 +187,15 @@ define i32 @ule_f32(float %x, float %y) { ; CHECK-LABEL: ugt_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.gt $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.gt $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -168,9 +208,15 @@ define i32 @ugt_f32(float %x, float %y) { ; CHECK-LABEL: uge_f32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f32.ge $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ge $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} diff --git a/llvm/test/CodeGen/WebAssembly/comparisons_f64.ll b/llvm/test/CodeGen/WebAssembly/comparisons_f64.ll index 7d038a09ccb..6694f989627 100644 --- a/llvm/test/CodeGen/WebAssembly/comparisons_f64.ll +++ b/llvm/test/CodeGen/WebAssembly/comparisons_f64.ll @@ -4,13 +4,17 @@ ; expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: ord_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.eq $push[[NUM0:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.eq $push[[NUM1:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.eq $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.eq $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} ; CHECK-NEXT: i32.and $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} ; CHECK-NEXT: return $pop[[NUM2]]{{$}} define i32 @ord_f64(double %x, double %y) { @@ -22,8 +26,12 @@ define i32 @ord_f64(double %x, double %y) { ; CHECK-LABEL: uno_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.ne $push[[NUM0:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.ne $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}} ; CHECK-NEXT: return $pop[[NUM2]]{{$}} define i32 @uno_f64(double %x, double %y) { @@ -35,7 +43,9 @@ define i32 @uno_f64(double %x, double %y) { ; CHECK-LABEL: oeq_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.eq $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @oeq_f64(double %x, double %y) { %a = fcmp oeq double %x, %y @@ -44,7 +54,7 @@ define i32 @oeq_f64(double %x, double %y) { } ; CHECK-LABEL: une_f64: -; CHECK: f64.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f64.ne $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @une_f64(double %x, double %y) { %a = fcmp une double %x, %y @@ -53,7 +63,7 @@ define i32 @une_f64(double %x, double %y) { } ; CHECK-LABEL: olt_f64: -; CHECK: f64.lt $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f64.lt $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @olt_f64(double %x, double %y) { %a = fcmp olt double %x, %y @@ -62,7 +72,7 @@ define i32 @olt_f64(double %x, double %y) { } ; CHECK-LABEL: ole_f64: -; CHECK: f64.le $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f64.le $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ole_f64(double %x, double %y) { %a = fcmp ole double %x, %y @@ -71,7 +81,7 @@ define i32 @ole_f64(double %x, double %y) { } ; CHECK-LABEL: ogt_f64: -; CHECK: f64.gt $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f64.gt $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ogt_f64(double %x, double %y) { %a = fcmp ogt double %x, %y @@ -80,7 +90,7 @@ define i32 @ogt_f64(double %x, double %y) { } ; CHECK-LABEL: oge_f64: -; CHECK: f64.ge $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: f64.ge $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @oge_f64(double %x, double %y) { %a = fcmp oge double %x, %y @@ -93,9 +103,15 @@ define i32 @oge_f64(double %x, double %y) { ; CHECK-LABEL: ueq_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.eq $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.eq $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -108,9 +124,15 @@ define i32 @ueq_f64(double %x, double %y) { ; CHECK-LABEL: one_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.ne $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f64.eq $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.eq $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.eq $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.eq $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.and $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.and $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]] @@ -123,9 +145,15 @@ define i32 @one_f64(double %x, double %y) { ; CHECK-LABEL: ult_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.lt $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.lt $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -138,9 +166,15 @@ define i32 @ult_f64(double %x, double %y) { ; CHECK-LABEL: ule_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.le $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.le $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -153,9 +187,15 @@ define i32 @ule_f64(double %x, double %y) { ; CHECK-LABEL: ugt_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.gt $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.gt $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} @@ -168,9 +208,15 @@ define i32 @ugt_f64(double %x, double %y) { ; CHECK-LABEL: uge_f64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: f64.ge $push[[NUM0:[0-9]+]]=, $0, $1{{$}} -; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $0, $0{{$}} -; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $1, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ge $push[[NUM0:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: get_local $push[[L2:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L3:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.ne $push[[NUM1:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}} +; CHECK-NEXT: get_local $push[[L4:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: get_local $push[[L5:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.ne $push[[NUM2:[0-9]+]]=, $pop[[L4]], $pop[[L5]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}} ; CHECK-NEXT: i32.or $push[[NUM4:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM3]]{{$}} ; CHECK-NEXT: return $pop[[NUM4]]{{$}} diff --git a/llvm/test/CodeGen/WebAssembly/comparisons_i32.ll b/llvm/test/CodeGen/WebAssembly/comparisons_i32.ll index d2ba73f79a3..a9a79c24fb4 100644 --- a/llvm/test/CodeGen/WebAssembly/comparisons_i32.ll +++ b/llvm/test/CodeGen/WebAssembly/comparisons_i32.ll @@ -4,12 +4,14 @@ ; Test that basic 32-bit integer comparison operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: eq_i32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.eq $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @eq_i32(i32 %x, i32 %y) { %a = icmp eq i32 %x, %y @@ -18,7 +20,7 @@ define i32 @eq_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: ne_i32: -; CHECK: i32.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.ne $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ne_i32(i32 %x, i32 %y) { %a = icmp ne i32 %x, %y @@ -27,7 +29,7 @@ define i32 @ne_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: slt_i32: -; CHECK: i32.lt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.lt_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @slt_i32(i32 %x, i32 %y) { %a = icmp slt i32 %x, %y @@ -36,7 +38,7 @@ define i32 @slt_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: sle_i32: -; CHECK: i32.le_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.le_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @sle_i32(i32 %x, i32 %y) { %a = icmp sle i32 %x, %y @@ -45,7 +47,7 @@ define i32 @sle_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: ult_i32: -; CHECK: i32.lt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.lt_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ult_i32(i32 %x, i32 %y) { %a = icmp ult i32 %x, %y @@ -54,7 +56,7 @@ define i32 @ult_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: ule_i32: -; CHECK: i32.le_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.le_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ule_i32(i32 %x, i32 %y) { %a = icmp ule i32 %x, %y @@ -63,7 +65,7 @@ define i32 @ule_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: sgt_i32: -; CHECK: i32.gt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.gt_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @sgt_i32(i32 %x, i32 %y) { %a = icmp sgt i32 %x, %y @@ -72,7 +74,7 @@ define i32 @sgt_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: sge_i32: -; CHECK: i32.ge_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.ge_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @sge_i32(i32 %x, i32 %y) { %a = icmp sge i32 %x, %y @@ -81,7 +83,7 @@ define i32 @sge_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: ugt_i32: -; CHECK: i32.gt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.gt_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ugt_i32(i32 %x, i32 %y) { %a = icmp ugt i32 %x, %y @@ -90,7 +92,7 @@ define i32 @ugt_i32(i32 %x, i32 %y) { } ; CHECK-LABEL: uge_i32: -; CHECK: i32.ge_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i32.ge_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @uge_i32(i32 %x, i32 %y) { %a = icmp uge i32 %x, %y diff --git a/llvm/test/CodeGen/WebAssembly/comparisons_i64.ll b/llvm/test/CodeGen/WebAssembly/comparisons_i64.ll index 80950ae5cd9..106520483c8 100644 --- a/llvm/test/CodeGen/WebAssembly/comparisons_i64.ll +++ b/llvm/test/CodeGen/WebAssembly/comparisons_i64.ll @@ -4,12 +4,14 @@ ; Test that basic 64-bit integer comparison operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: eq_i64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i64.eq $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.eq $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @eq_i64(i64 %x, i64 %y) { %a = icmp eq i64 %x, %y @@ -18,7 +20,7 @@ define i32 @eq_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: ne_i64: -; CHECK: i64.ne $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.ne $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ne_i64(i64 %x, i64 %y) { %a = icmp ne i64 %x, %y @@ -27,7 +29,7 @@ define i32 @ne_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: slt_i64: -; CHECK: i64.lt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.lt_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @slt_i64(i64 %x, i64 %y) { %a = icmp slt i64 %x, %y @@ -36,7 +38,7 @@ define i32 @slt_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: sle_i64: -; CHECK: i64.le_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.le_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @sle_i64(i64 %x, i64 %y) { %a = icmp sle i64 %x, %y @@ -45,7 +47,7 @@ define i32 @sle_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: ult_i64: -; CHECK: i64.lt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.lt_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ult_i64(i64 %x, i64 %y) { %a = icmp ult i64 %x, %y @@ -54,7 +56,7 @@ define i32 @ult_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: ule_i64: -; CHECK: i64.le_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.le_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ule_i64(i64 %x, i64 %y) { %a = icmp ule i64 %x, %y @@ -63,7 +65,7 @@ define i32 @ule_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: sgt_i64: -; CHECK: i64.gt_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.gt_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @sgt_i64(i64 %x, i64 %y) { %a = icmp sgt i64 %x, %y @@ -72,7 +74,7 @@ define i32 @sgt_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: sge_i64: -; CHECK: i64.ge_s $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.ge_s $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @sge_i64(i64 %x, i64 %y) { %a = icmp sge i64 %x, %y @@ -81,7 +83,7 @@ define i32 @sge_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: ugt_i64: -; CHECK: i64.gt_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.gt_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ugt_i64(i64 %x, i64 %y) { %a = icmp ugt i64 %x, %y @@ -90,7 +92,7 @@ define i32 @ugt_i64(i64 %x, i64 %y) { } ; CHECK-LABEL: uge_i64: -; CHECK: i64.ge_u $push[[NUM:[0-9]+]]=, $0, $1{{$}} +; CHECK: i64.ge_u $push[[NUM:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @uge_i64(i64 %x, i64 %y) { %a = icmp uge i64 %x, %y diff --git a/llvm/test/CodeGen/WebAssembly/copysign-casts.ll b/llvm/test/CodeGen/WebAssembly/copysign-casts.ll index f8e50d043ca..7cd40efafcd 100644 --- a/llvm/test/CodeGen/WebAssembly/copysign-casts.ll +++ b/llvm/test/CodeGen/WebAssembly/copysign-casts.ll @@ -4,14 +4,14 @@ ; unfolded. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare double @copysign(double, double) nounwind readnone declare float @copysignf(float, float) nounwind readnone ; CHECK-LABEL: fold_promote: -; CHECK: f64.promote/f32 $push0=, $1{{$}} -; CHECK: f64.copysign $push1=, $0, $pop0{{$}} +; CHECK: f64.promote/f32 $push0=, $pop{{[0-9]+}}{{$}} +; CHECK: f64.copysign $push1=, $pop{{[0-9]+}}, $pop0{{$}} define double @fold_promote(double %a, float %b) { %c = fpext float %b to double %t = call double @copysign(double %a, double %c) @@ -19,8 +19,8 @@ define double @fold_promote(double %a, float %b) { } ; CHECK-LABEL: fold_demote:{{$}} -; CHECK: f32.demote/f64 $push0=, $1{{$}} -; CHECK: f32.copysign $push1=, $0, $pop0{{$}} +; CHECK: f32.demote/f64 $push0=, $pop{{[0-9]+}}{{$}} +; CHECK: f32.copysign $push1=, $pop{{[0-9]+}}, $pop0{{$}} define float @fold_demote(float %a, double %b) { %c = fptrunc double %b to float %t = call float @copysignf(float %a, float %c) diff --git a/llvm/test/CodeGen/WebAssembly/dbgvalue.ll b/llvm/test/CodeGen/WebAssembly/dbgvalue.ll index c6a091bc78c..ca6fa2601cc 100644 --- a/llvm/test/CodeGen/WebAssembly/dbgvalue.ll +++ b/llvm/test/CodeGen/WebAssembly/dbgvalue.ll @@ -6,7 +6,7 @@ ; CHECK: DW_TAG_variable source_filename = "test/CodeGen/WebAssembly/dbgvalue.ll" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" @key = external local_unnamed_addr global [15 x i8], align 1 @.str = external unnamed_addr constant [33 x i8], align 1 diff --git a/llvm/test/CodeGen/WebAssembly/dead-vreg.ll b/llvm/test/CodeGen/WebAssembly/dead-vreg.ll index 190a0856400..06487e4cd36 100644 --- a/llvm/test/CodeGen/WebAssembly/dead-vreg.ll +++ b/llvm/test/CodeGen/WebAssembly/dead-vreg.ll @@ -3,7 +3,7 @@ ; Check that unused vregs aren't assigned registers. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" define void @foo(i32* nocapture %a, i32 %w, i32 %h) { ; CHECK-LABEL: foo: diff --git a/llvm/test/CodeGen/WebAssembly/divrem-constant.ll b/llvm/test/CodeGen/WebAssembly/divrem-constant.ll index 6150cab4d4f..1b4d30ad949 100644 --- a/llvm/test/CodeGen/WebAssembly/divrem-constant.ll +++ b/llvm/test/CodeGen/WebAssembly/divrem-constant.ll @@ -3,7 +3,7 @@ ; Test that integer div and rem by constant are optimized appropriately. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: test_udiv_2: ; CHECK: i32.shr_u diff --git a/llvm/test/CodeGen/WebAssembly/f32.ll b/llvm/test/CodeGen/WebAssembly/f32.ll index 1c1d8191a98..45f00aa5a01 100644 --- a/llvm/test/CodeGen/WebAssembly/f32.ll +++ b/llvm/test/CodeGen/WebAssembly/f32.ll @@ -3,7 +3,7 @@ ; Test that basic 32-bit floating-point operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare float @llvm.fabs.f32(float) declare float @llvm.copysign.f32(float, float) @@ -18,104 +18,106 @@ declare float @llvm.fma.f32(float, float, float) ; CHECK-LABEL: fadd32: ; CHECK-NEXT: .param f32, f32{{$}} ; CHECK-NEXT: .result f32{{$}} -; CHECK-NEXT: f32.add $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.add $push[[LR:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fadd32(float %x, float %y) { %a = fadd float %x, %y ret float %a } ; CHECK-LABEL: fsub32: -; CHECK: f32.sub $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.sub $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fsub32(float %x, float %y) { %a = fsub float %x, %y ret float %a } ; CHECK-LABEL: fmul32: -; CHECK: f32.mul $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.mul $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fmul32(float %x, float %y) { %a = fmul float %x, %y ret float %a } ; CHECK-LABEL: fdiv32: -; CHECK: f32.div $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.div $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fdiv32(float %x, float %y) { %a = fdiv float %x, %y ret float %a } ; CHECK-LABEL: fabs32: -; CHECK: f32.abs $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.abs $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fabs32(float %x) { %a = call float @llvm.fabs.f32(float %x) ret float %a } ; CHECK-LABEL: fneg32: -; CHECK: f32.neg $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.neg $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fneg32(float %x) { %a = fsub float -0., %x ret float %a } ; CHECK-LABEL: copysign32: -; CHECK: f32.copysign $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.copysign $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @copysign32(float %x, float %y) { %a = call float @llvm.copysign.f32(float %x, float %y) ret float %a } ; CHECK-LABEL: sqrt32: -; CHECK: f32.sqrt $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.sqrt $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @sqrt32(float %x) { %a = call float @llvm.sqrt.f32(float %x) ret float %a } ; CHECK-LABEL: ceil32: -; CHECK: f32.ceil $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.ceil $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @ceil32(float %x) { %a = call float @llvm.ceil.f32(float %x) ret float %a } ; CHECK-LABEL: floor32: -; CHECK: f32.floor $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.floor $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @floor32(float %x) { %a = call float @llvm.floor.f32(float %x) ret float %a } ; CHECK-LABEL: trunc32: -; CHECK: f32.trunc $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.trunc $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @trunc32(float %x) { %a = call float @llvm.trunc.f32(float %x) ret float %a } ; CHECK-LABEL: nearest32: -; CHECK: f32.nearest $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.nearest $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @nearest32(float %x) { %a = call float @llvm.nearbyint.f32(float %x) ret float %a } ; CHECK-LABEL: nearest32_via_rint: -; CHECK: f32.nearest $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f32.nearest $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @nearest32_via_rint(float %x) { %a = call float @llvm.rint.f32(float %x) ret float %a @@ -128,7 +130,7 @@ define float @nearest32_via_rint(float %x) { ; tests. ; CHECK-LABEL: fmin32: -; CHECK: f32.min $push1=, $0, $pop0{{$}} +; CHECK: f32.min $push1=, $pop{{[0-9]+}}, $pop[[LR]]{{$}} ; CHECK-NEXT: return $pop1{{$}} define float @fmin32(float %x) { %a = fcmp ult float %x, 0.0 @@ -137,7 +139,7 @@ define float @fmin32(float %x) { } ; CHECK-LABEL: fmax32: -; CHECK: f32.max $push1=, $0, $pop0{{$}} +; CHECK: f32.max $push1=, $pop{{[0-9]+}}, $pop[[LR]]{{$}} ; CHECK-NEXT: return $pop1{{$}} define float @fmax32(float %x) { %a = fcmp ugt float %x, 0.0 @@ -146,8 +148,8 @@ define float @fmax32(float %x) { } ; CHECK-LABEL: fma32: -; CHECK: {{^}} f32.call $push0=, fmaf@FUNCTION, $0, $1, $2{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: {{^}} f32.call $push[[LR:[0-9]+]]=, fmaf@FUNCTION, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define float @fma32(float %a, float %b, float %c) { %d = call float @llvm.fma.f32(float %a, float %b, float %c) ret float %d diff --git a/llvm/test/CodeGen/WebAssembly/f64.ll b/llvm/test/CodeGen/WebAssembly/f64.ll index 670f3f0b697..fb52c3f92ad 100644 --- a/llvm/test/CodeGen/WebAssembly/f64.ll +++ b/llvm/test/CodeGen/WebAssembly/f64.ll @@ -3,7 +3,7 @@ ; Test that basic 64-bit floating-point operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare double @llvm.fabs.f64(double) declare double @llvm.copysign.f64(double, double) @@ -18,104 +18,106 @@ declare double @llvm.fma.f64(double, double, double) ; CHECK-LABEL: fadd64: ; CHECK-NEXT: .param f64, f64{{$}} ; CHECK-NEXT: .result f64{{$}} -; CHECK-NEXT: f64.add $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.add $push[[LR:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fadd64(double %x, double %y) { %a = fadd double %x, %y ret double %a } ; CHECK-LABEL: fsub64: -; CHECK: f64.sub $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.sub $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fsub64(double %x, double %y) { %a = fsub double %x, %y ret double %a } ; CHECK-LABEL: fmul64: -; CHECK: f64.mul $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.mul $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fmul64(double %x, double %y) { %a = fmul double %x, %y ret double %a } ; CHECK-LABEL: fdiv64: -; CHECK: f64.div $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.div $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fdiv64(double %x, double %y) { %a = fdiv double %x, %y ret double %a } ; CHECK-LABEL: fabs64: -; CHECK: f64.abs $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.abs $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fabs64(double %x) { %a = call double @llvm.fabs.f64(double %x) ret double %a } ; CHECK-LABEL: fneg64: -; CHECK: f64.neg $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.neg $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fneg64(double %x) { %a = fsub double -0., %x ret double %a } ; CHECK-LABEL: copysign64: -; CHECK: f64.copysign $push0=, $0, $1{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.copysign $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @copysign64(double %x, double %y) { %a = call double @llvm.copysign.f64(double %x, double %y) ret double %a } ; CHECK-LABEL: sqrt64: -; CHECK: f64.sqrt $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.sqrt $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @sqrt64(double %x) { %a = call double @llvm.sqrt.f64(double %x) ret double %a } ; CHECK-LABEL: ceil64: -; CHECK: f64.ceil $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.ceil $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @ceil64(double %x) { %a = call double @llvm.ceil.f64(double %x) ret double %a } ; CHECK-LABEL: floor64: -; CHECK: f64.floor $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.floor $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @floor64(double %x) { %a = call double @llvm.floor.f64(double %x) ret double %a } ; CHECK-LABEL: trunc64: -; CHECK: f64.trunc $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.trunc $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @trunc64(double %x) { %a = call double @llvm.trunc.f64(double %x) ret double %a } ; CHECK-LABEL: nearest64: -; CHECK: f64.nearest $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.nearest $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @nearest64(double %x) { %a = call double @llvm.nearbyint.f64(double %x) ret double %a } ; CHECK-LABEL: nearest64_via_rint: -; CHECK: f64.nearest $push0=, $0{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: f64.nearest $push[[LR:[0-9]+]]=, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @nearest64_via_rint(double %x) { %a = call double @llvm.rint.f64(double %x) ret double %a @@ -128,7 +130,7 @@ define double @nearest64_via_rint(double %x) { ; tests. ; CHECK-LABEL: fmin64: -; CHECK: f64.min $push1=, $0, $pop0{{$}} +; CHECK: f64.min $push1=, $pop{{[0-9]+}}, $pop[[LR]]{{$}} ; CHECK-NEXT: return $pop1{{$}} define double @fmin64(double %x) { %a = fcmp ult double %x, 0.0 @@ -137,7 +139,7 @@ define double @fmin64(double %x) { } ; CHECK-LABEL: fmax64: -; CHECK: f64.max $push1=, $0, $pop0{{$}} +; CHECK: f64.max $push1=, $pop{{[0-9]+}}, $pop[[LR]]{{$}} ; CHECK-NEXT: return $pop1{{$}} define double @fmax64(double %x) { %a = fcmp ugt double %x, 0.0 @@ -146,8 +148,8 @@ define double @fmax64(double %x) { } ; CHECK-LABEL: fma64: -; CHECK: {{^}} f64.call $push0=, fma@FUNCTION, $0, $1, $2{{$}} -; CHECK-NEXT: return $pop0{{$}} +; CHECK: {{^}} f64.call $push[[LR:[0-9]+]]=, fma@FUNCTION, $pop{{[0-9]+}}, $pop{{[0-9]+}}, $pop{{[0-9]+}}{{$}} +; CHECK-NEXT: return $pop[[LR]]{{$}} define double @fma64(double %a, double %b, double %c) { %d = call double @llvm.fma.f64(double %a, double %b, double %c) ret double %d diff --git a/llvm/test/CodeGen/WebAssembly/fast-isel-noreg.ll b/llvm/test/CodeGen/WebAssembly/fast-isel-noreg.ll index a2504822dd1..229651d093f 100644 --- a/llvm/test/CodeGen/WebAssembly/fast-isel-noreg.ll +++ b/llvm/test/CodeGen/WebAssembly/fast-isel-noreg.ll @@ -4,7 +4,7 @@ ; Test that FastISel does not generate instructions with NoReg target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK: i32.const $push0=, 0 define hidden i32 @a() #0 { diff --git a/llvm/test/CodeGen/WebAssembly/globl.ll b/llvm/test/CodeGen/WebAssembly/globl.ll index 3ebd3d88fb4..ba9f6659d7d 100644 --- a/llvm/test/CodeGen/WebAssembly/globl.ll +++ b/llvm/test/CodeGen/WebAssembly/globl.ll @@ -1,7 +1,7 @@ ; RUN: llc < %s -asm-verbose=false | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK: .globl foo ; CHECK-LABEL: foo: diff --git a/llvm/test/CodeGen/WebAssembly/i128.ll b/llvm/test/CodeGen/WebAssembly/i128.ll index 29bf787863d..2e44af9c518 100644 --- a/llvm/test/CodeGen/WebAssembly/i128.ll +++ b/llvm/test/CodeGen/WebAssembly/i128.ll @@ -3,7 +3,7 @@ ; Test that basic 128-bit integer operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare i128 @llvm.ctlz.i128(i128, i1) declare i128 @llvm.cttz.i128(i128, i1) diff --git a/llvm/test/CodeGen/WebAssembly/i32.ll b/llvm/test/CodeGen/WebAssembly/i32.ll index a07dd02bece..e451695d890 100644 --- a/llvm/test/CodeGen/WebAssembly/i32.ll +++ b/llvm/test/CodeGen/WebAssembly/i32.ll @@ -3,7 +3,7 @@ ; Test that basic 32-bit integer operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare i32 @llvm.ctlz.i32(i32, i1) declare i32 @llvm.cttz.i32(i32, i1) @@ -12,7 +12,9 @@ declare i32 @llvm.ctpop.i32(i32) ; CHECK-LABEL: add32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.add $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.add $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @add32(i32 %x, i32 %y) { %a = add i32 %x, %y @@ -22,7 +24,9 @@ define i32 @add32(i32 %x, i32 %y) { ; CHECK-LABEL: sub32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.sub $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.sub $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @sub32(i32 %x, i32 %y) { %a = sub i32 %x, %y @@ -32,7 +36,9 @@ define i32 @sub32(i32 %x, i32 %y) { ; CHECK-LABEL: mul32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.mul $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.mul $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @mul32(i32 %x, i32 %y) { %a = mul i32 %x, %y @@ -42,7 +48,9 @@ define i32 @mul32(i32 %x, i32 %y) { ; CHECK-LABEL: sdiv32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.div_s $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.div_s $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @sdiv32(i32 %x, i32 %y) { %a = sdiv i32 %x, %y @@ -52,7 +60,9 @@ define i32 @sdiv32(i32 %x, i32 %y) { ; CHECK-LABEL: udiv32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.div_u $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.div_u $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @udiv32(i32 %x, i32 %y) { %a = udiv i32 %x, %y @@ -62,7 +72,9 @@ define i32 @udiv32(i32 %x, i32 %y) { ; CHECK-LABEL: srem32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.rem_s $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.rem_s $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @srem32(i32 %x, i32 %y) { %a = srem i32 %x, %y @@ -72,7 +84,9 @@ define i32 @srem32(i32 %x, i32 %y) { ; CHECK-LABEL: urem32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.rem_u $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.rem_u $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @urem32(i32 %x, i32 %y) { %a = urem i32 %x, %y @@ -82,7 +96,9 @@ define i32 @urem32(i32 %x, i32 %y) { ; CHECK-LABEL: and32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.and $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.and $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @and32(i32 %x, i32 %y) { %a = and i32 %x, %y @@ -92,7 +108,9 @@ define i32 @and32(i32 %x, i32 %y) { ; CHECK-LABEL: or32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.or $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.or $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @or32(i32 %x, i32 %y) { %a = or i32 %x, %y @@ -102,7 +120,9 @@ define i32 @or32(i32 %x, i32 %y) { ; CHECK-LABEL: xor32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.xor $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.xor $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @xor32(i32 %x, i32 %y) { %a = xor i32 %x, %y @@ -112,7 +132,9 @@ define i32 @xor32(i32 %x, i32 %y) { ; CHECK-LABEL: shl32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.shl $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.shl $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @shl32(i32 %x, i32 %y) { %a = shl i32 %x, %y @@ -122,7 +144,9 @@ define i32 @shl32(i32 %x, i32 %y) { ; CHECK-LABEL: shr32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.shr_u $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.shr_u $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @shr32(i32 %x, i32 %y) { %a = lshr i32 %x, %y @@ -132,7 +156,9 @@ define i32 @shr32(i32 %x, i32 %y) { ; CHECK-LABEL: sar32: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.shr_s $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.shr_s $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @sar32(i32 %x, i32 %y) { %a = ashr i32 %x, %y @@ -142,7 +168,8 @@ define i32 @sar32(i32 %x, i32 %y) { ; CHECK-LABEL: clz32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.clz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.clz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @clz32(i32 %x) { %a = call i32 @llvm.ctlz.i32(i32 %x, i1 false) @@ -152,7 +179,8 @@ define i32 @clz32(i32 %x) { ; CHECK-LABEL: clz32_zero_undef: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.clz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.clz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @clz32_zero_undef(i32 %x) { %a = call i32 @llvm.ctlz.i32(i32 %x, i1 true) @@ -162,7 +190,8 @@ define i32 @clz32_zero_undef(i32 %x) { ; CHECK-LABEL: ctz32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.ctz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.ctz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @ctz32(i32 %x) { %a = call i32 @llvm.cttz.i32(i32 %x, i1 false) @@ -172,7 +201,8 @@ define i32 @ctz32(i32 %x) { ; CHECK-LABEL: ctz32_zero_undef: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.ctz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.ctz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @ctz32_zero_undef(i32 %x) { %a = call i32 @llvm.cttz.i32(i32 %x, i1 true) @@ -182,7 +212,8 @@ define i32 @ctz32_zero_undef(i32 %x) { ; CHECK-LABEL: popcnt32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.popcnt $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.popcnt $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @popcnt32(i32 %x) { %a = call i32 @llvm.ctpop.i32(i32 %x) @@ -192,7 +223,8 @@ define i32 @popcnt32(i32 %x) { ; CHECK-LABEL: eqz32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.eqz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.eqz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @eqz32(i32 %x) { %a = icmp eq i32 %x, 0 @@ -203,7 +235,9 @@ define i32 @eqz32(i32 %x) { ; CHECK-LABEL: rotl: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.rotl $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.rotl $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i32 @rotl(i32 %x, i32 %y) { %z = sub i32 32, %y @@ -216,7 +250,9 @@ define i32 @rotl(i32 %x, i32 %y) { ; CHECK-LABEL: masked_rotl: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.rotl $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.rotl $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i32 @masked_rotl(i32 %x, i32 %y) { %a = and i32 %y, 31 @@ -230,7 +266,9 @@ define i32 @masked_rotl(i32 %x, i32 %y) { ; CHECK-LABEL: rotr: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.rotr $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.rotr $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i32 @rotr(i32 %x, i32 %y) { %z = sub i32 32, %y @@ -243,7 +281,9 @@ define i32 @rotr(i32 %x, i32 %y) { ; CHECK-LABEL: masked_rotr: ; CHECK-NEXT: .param i32, i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.rotr $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.rotr $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i32 @masked_rotr(i32 %x, i32 %y) { %a = and i32 %y, 31 diff --git a/llvm/test/CodeGen/WebAssembly/i64.ll b/llvm/test/CodeGen/WebAssembly/i64.ll index 93e32bfc0e1..4386bed4ebf 100644 --- a/llvm/test/CodeGen/WebAssembly/i64.ll +++ b/llvm/test/CodeGen/WebAssembly/i64.ll @@ -3,7 +3,7 @@ ; Test that basic 64-bit integer operations assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare i64 @llvm.ctlz.i64(i64, i1) declare i64 @llvm.cttz.i64(i64, i1) @@ -12,7 +12,9 @@ declare i64 @llvm.ctpop.i64(i64) ; CHECK-LABEL: add64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.add $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.add $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @add64(i64 %x, i64 %y) { %a = add i64 %x, %y @@ -22,7 +24,9 @@ define i64 @add64(i64 %x, i64 %y) { ; CHECK-LABEL: sub64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.sub $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.sub $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @sub64(i64 %x, i64 %y) { %a = sub i64 %x, %y @@ -32,7 +36,9 @@ define i64 @sub64(i64 %x, i64 %y) { ; CHECK-LABEL: mul64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.mul $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.mul $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @mul64(i64 %x, i64 %y) { %a = mul i64 %x, %y @@ -42,7 +48,9 @@ define i64 @mul64(i64 %x, i64 %y) { ; CHECK-LABEL: sdiv64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.div_s $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.div_s $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @sdiv64(i64 %x, i64 %y) { %a = sdiv i64 %x, %y @@ -52,7 +60,9 @@ define i64 @sdiv64(i64 %x, i64 %y) { ; CHECK-LABEL: udiv64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.div_u $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.div_u $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @udiv64(i64 %x, i64 %y) { %a = udiv i64 %x, %y @@ -62,7 +72,9 @@ define i64 @udiv64(i64 %x, i64 %y) { ; CHECK-LABEL: srem64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.rem_s $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.rem_s $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @srem64(i64 %x, i64 %y) { %a = srem i64 %x, %y @@ -72,7 +84,9 @@ define i64 @srem64(i64 %x, i64 %y) { ; CHECK-LABEL: urem64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.rem_u $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.rem_u $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @urem64(i64 %x, i64 %y) { %a = urem i64 %x, %y @@ -82,7 +96,9 @@ define i64 @urem64(i64 %x, i64 %y) { ; CHECK-LABEL: and64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.and $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.and $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @and64(i64 %x, i64 %y) { %a = and i64 %x, %y @@ -92,7 +108,9 @@ define i64 @and64(i64 %x, i64 %y) { ; CHECK-LABEL: or64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.or $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.or $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @or64(i64 %x, i64 %y) { %a = or i64 %x, %y @@ -102,7 +120,9 @@ define i64 @or64(i64 %x, i64 %y) { ; CHECK-LABEL: xor64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.xor $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.xor $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @xor64(i64 %x, i64 %y) { %a = xor i64 %x, %y @@ -112,7 +132,9 @@ define i64 @xor64(i64 %x, i64 %y) { ; CHECK-LABEL: shl64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.shl $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.shl $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @shl64(i64 %x, i64 %y) { %a = shl i64 %x, %y @@ -122,7 +144,9 @@ define i64 @shl64(i64 %x, i64 %y) { ; CHECK-LABEL: shr64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.shr_u $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.shr_u $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @shr64(i64 %x, i64 %y) { %a = lshr i64 %x, %y @@ -132,7 +156,9 @@ define i64 @shr64(i64 %x, i64 %y) { ; CHECK-LABEL: sar64: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.shr_s $push0=, $0, $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.shr_s $push0=, $pop[[L0]], $pop[[L1]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @sar64(i64 %x, i64 %y) { %a = ashr i64 %x, %y @@ -142,7 +168,8 @@ define i64 @sar64(i64 %x, i64 %y) { ; CHECK-LABEL: clz64: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.clz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.clz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @clz64(i64 %x) { %a = call i64 @llvm.ctlz.i64(i64 %x, i1 false) @@ -152,7 +179,8 @@ define i64 @clz64(i64 %x) { ; CHECK-LABEL: clz64_zero_undef: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.clz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.clz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @clz64_zero_undef(i64 %x) { %a = call i64 @llvm.ctlz.i64(i64 %x, i1 true) @@ -162,7 +190,8 @@ define i64 @clz64_zero_undef(i64 %x) { ; CHECK-LABEL: ctz64: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.ctz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.ctz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @ctz64(i64 %x) { %a = call i64 @llvm.cttz.i64(i64 %x, i1 false) @@ -172,7 +201,8 @@ define i64 @ctz64(i64 %x) { ; CHECK-LABEL: ctz64_zero_undef: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.ctz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.ctz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @ctz64_zero_undef(i64 %x) { %a = call i64 @llvm.cttz.i64(i64 %x, i1 true) @@ -182,7 +212,8 @@ define i64 @ctz64_zero_undef(i64 %x) { ; CHECK-LABEL: popcnt64: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.popcnt $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.popcnt $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i64 @popcnt64(i64 %x) { %a = call i64 @llvm.ctpop.i64(i64 %x) @@ -192,7 +223,8 @@ define i64 @popcnt64(i64 %x) { ; CHECK-LABEL: eqz64: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i64.eqz $push0=, $0{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.eqz $push0=, $pop[[L0]]{{$}} ; CHECK-NEXT: return $pop0{{$}} define i32 @eqz64(i64 %x) { %a = icmp eq i64 %x, 0 @@ -203,7 +235,9 @@ define i32 @eqz64(i64 %x) { ; CHECK-LABEL: rotl: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.rotl $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.rotl $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i64 @rotl(i64 %x, i64 %y) { %z = sub i64 64, %y @@ -216,7 +250,9 @@ define i64 @rotl(i64 %x, i64 %y) { ; CHECK-LABEL: masked_rotl: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.rotl $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.rotl $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i64 @masked_rotl(i64 %x, i64 %y) { %a = and i64 %y, 63 @@ -230,7 +266,9 @@ define i64 @masked_rotl(i64 %x, i64 %y) { ; CHECK-LABEL: rotr: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.rotr $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.rotr $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i64 @rotr(i64 %x, i64 %y) { %z = sub i64 64, %y @@ -243,7 +281,9 @@ define i64 @rotr(i64 %x, i64 %y) { ; CHECK-LABEL: masked_rotr: ; CHECK-NEXT: .param i64, i64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.rotr $push0=, $0, $1 +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.rotr $push0=, $pop[[L0]], $pop[[L1]] ; CHECK-NEXT: return $pop0{{$}} define i64 @masked_rotr(i64 %x, i64 %y) { %a = and i64 %y, 63 diff --git a/llvm/test/CodeGen/WebAssembly/ident.ll b/llvm/test/CodeGen/WebAssembly/ident.ll index 49c188ec257..e5d85d090f1 100644 --- a/llvm/test/CodeGen/WebAssembly/ident.ll +++ b/llvm/test/CodeGen/WebAssembly/ident.ll @@ -3,7 +3,7 @@ ; Test llvm.ident. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK: .ident "hello world" diff --git a/llvm/test/CodeGen/WebAssembly/immediates.ll b/llvm/test/CodeGen/WebAssembly/immediates.ll index 3d11f9410a7..1182423a594 100644 --- a/llvm/test/CodeGen/WebAssembly/immediates.ll +++ b/llvm/test/CodeGen/WebAssembly/immediates.ll @@ -3,7 +3,7 @@ ; Test that basic immediates assemble as expected. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: zero_i32: ; CHECK-NEXT: .result i32{{$}} diff --git a/llvm/test/CodeGen/WebAssembly/implicit-def.ll b/llvm/test/CodeGen/WebAssembly/implicit-def.ll index 01ee171b449..1f9f74887e8 100644 --- a/llvm/test/CodeGen/WebAssembly/implicit-def.ll +++ b/llvm/test/CodeGen/WebAssembly/implicit-def.ll @@ -1,6 +1,6 @@ ; RUN: llc -o - %s | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; Test that stackified IMPLICIT_DEF instructions are converted into ; CONST_I32 to provide an explicit push. diff --git a/llvm/test/CodeGen/WebAssembly/load.ll b/llvm/test/CodeGen/WebAssembly/load.ll index a8e174e914e..165d145fde1 100644 --- a/llvm/test/CodeGen/WebAssembly/load.ll +++ b/llvm/test/CodeGen/WebAssembly/load.ll @@ -4,12 +4,13 @@ ; Test that basic loads are assembled properly. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: ldi32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($pop[[L0]]){{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ldi32(i32 *%p) { %v = load i32, i32* %p @@ -19,7 +20,8 @@ define i32 @ldi32(i32 *%p) { ; CHECK-LABEL: ldi64: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($pop[[L0]]){{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i64 @ldi64(i64 *%p) { %v = load i64, i64* %p @@ -29,7 +31,8 @@ define i64 @ldi64(i64 *%p) { ; CHECK-LABEL: ldf32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result f32{{$}} -; CHECK-NEXT: f32.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f32.load $push[[NUM:[0-9]+]]=, 0($pop[[L0]]){{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define float @ldf32(float *%p) { %v = load float, float* %p @@ -39,7 +42,8 @@ define float @ldf32(float *%p) { ; CHECK-LABEL: ldf64: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result f64{{$}} -; CHECK-NEXT: f64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: f64.load $push[[NUM:[0-9]+]]=, 0($pop[[L0]]){{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define double @ldf64(double *%p) { %v = load double, double* %p diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll index 8283b49cd58..91fde29ea59 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll @@ -3,7 +3,7 @@ ; RUN: llc < %s | FileCheck %s --check-prefix=NONE target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" %struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] } diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll index 5fcc39909b0..3864e445f63 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions-whitelist.ll @@ -1,7 +1,7 @@ ; RUN: opt < %s -wasm-lower-em-ehsjlj -emscripten-cxx-exceptions-whitelist=do_catch -S | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" define void @dont_catch() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { ; CHECK-LABEL: @dont_catch( diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll index 60953cdb6ef..060f481c326 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll @@ -1,7 +1,7 @@ ; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" @_ZTIi = external constant i8* @_ZTIc = external constant i8* diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll index 40b9d62a036..cf42219c011 100644 --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll @@ -1,7 +1,7 @@ ; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" %struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] } diff --git a/llvm/test/CodeGen/WebAssembly/non-executable-stack.ll b/llvm/test/CodeGen/WebAssembly/non-executable-stack.ll index b81063724e9..f1e1ba36a79 100644 --- a/llvm/test/CodeGen/WebAssembly/non-executable-stack.ll +++ b/llvm/test/CodeGen/WebAssembly/non-executable-stack.ll @@ -4,6 +4,6 @@ ; because wasm's stack is always non-executable. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-NOT: .note.GNU-stack diff --git a/llvm/test/CodeGen/WebAssembly/offset-folding.ll b/llvm/test/CodeGen/WebAssembly/offset-folding.ll index 863549fc20f..e8e98ecc330 100644 --- a/llvm/test/CodeGen/WebAssembly/offset-folding.ll +++ b/llvm/test/CodeGen/WebAssembly/offset-folding.ll @@ -3,7 +3,7 @@ ; Test that constant offsets can be folded into global addresses. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" @x = external global [0 x i32] @y = global [50 x i32] zeroinitializer diff --git a/llvm/test/CodeGen/WebAssembly/store.ll b/llvm/test/CodeGen/WebAssembly/store.ll index 3852b6e420c..153d7d9addf 100644 --- a/llvm/test/CodeGen/WebAssembly/store.ll +++ b/llvm/test/CodeGen/WebAssembly/store.ll @@ -4,11 +4,13 @@ ; Test that basic stores are assembled properly. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" ; CHECK-LABEL: sti32: ; CHECK-NEXT: .param i32, i32{{$}} -; CHECK-NEXT: i32.store 0($0), $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.store 0($pop[[L0]]), $pop[[L1]]{{$}} ; CHECK-NEXT: return{{$}} define void @sti32(i32 *%p, i32 %v) { store i32 %v, i32* %p @@ -17,7 +19,9 @@ define void @sti32(i32 *%p, i32 %v) { ; CHECK-LABEL: sti64: ; CHECK-NEXT: .param i32, i64{{$}} -; CHECK-NEXT: i64.store 0($0), $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i64.store 0($pop[[L0]]), $pop[[L1]]{{$}} ; CHECK-NEXT: return{{$}} define void @sti64(i64 *%p, i64 %v) { store i64 %v, i64* %p @@ -26,7 +30,9 @@ define void @sti64(i64 *%p, i64 %v) { ; CHECK-LABEL: stf32: ; CHECK-NEXT: .param i32, f32{{$}} -; CHECK-NEXT: f32.store 0($0), $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f32.store 0($pop[[L0]]), $pop[[L1]]{{$}} ; CHECK-NEXT: return{{$}} define void @stf32(float *%p, float %v) { store float %v, float* %p @@ -35,7 +41,9 @@ define void @stf32(float *%p, float %v) { ; CHECK-LABEL: stf64: ; CHECK-NEXT: .param i32, f64{{$}} -; CHECK-NEXT: f64.store 0($0), $1{{$}} +; CHECK-NEXT: get_local $push[[L0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[L1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: f64.store 0($pop[[L0]]), $pop[[L1]]{{$}} ; CHECK-NEXT: return{{$}} define void @stf64(double *%p, double %v) { store double %v, double* %p diff --git a/llvm/test/CodeGen/WebAssembly/switch.ll b/llvm/test/CodeGen/WebAssembly/switch.ll index c6354baa57a..18eac5534a4 100644 --- a/llvm/test/CodeGen/WebAssembly/switch.ll +++ b/llvm/test/CodeGen/WebAssembly/switch.ll @@ -4,7 +4,7 @@ ; the blocks in a way that isn't interesting here. target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare void @foo0() declare void @foo1() diff --git a/llvm/test/CodeGen/WebAssembly/unreachable.ll b/llvm/test/CodeGen/WebAssembly/unreachable.ll index 77fda44d5ff..de96b092756 100644 --- a/llvm/test/CodeGen/WebAssembly/unreachable.ll +++ b/llvm/test/CodeGen/WebAssembly/unreachable.ll @@ -5,7 +5,7 @@ ; wasm unreachable target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-unknown-wasm" declare void @llvm.trap() declare void @llvm.debugtrap() |