diff --git a/lld/ELF/Arch/EVM.cpp b/lld/ELF/Arch/EVM.cpp new file mode 100644 index 000000000000..ac2ab5ff9f25 --- /dev/null +++ b/lld/ELF/Arch/EVM.cpp @@ -0,0 +1,94 @@ +//===- EVM.cpp ------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// EVM is a stack-based virtual machine with a word size of 256 bits intendent +// for execution of smart contracts in Ethereum blockchain environment. +// +// Since it is baremetal programming, there's usually no loader to load +// ELF files on EVMs. You are expected to link your program against address +// 0 and pull out a .text section from the result using objcopy, so that you +// can write the linked code to on-chip flush memory. You can do that with +// the following commands: +// +// ld.lld -Ttext=0 -o foo foo.o +// objcopy -O binary --only-section=.text foo output.bin +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class EVM final : public TargetInfo { +public: + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; +}; +} // namespace + +RelExpr EVM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_EVM_DATA: + return R_ABS; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +void EVM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { + case R_EVM_DATA: { + if (val > std::numeric_limits::max()) + llvm_unreachable("R_EVM_DATA: to big relocation value"); + write32be(loc, val); + break; + } + default: + llvm_unreachable("unknown relocation"); + } +} + +TargetInfo *elf::getEVMTargetInfo() { + static EVM target; + return ⌖ +} + +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader().e_flags; +} + +uint32_t EVM::calcEFlags() const { + assert(!ctx.objectFiles.empty()); + + const uint32_t flags = getEFlags(ctx.objectFiles[0]); + if (auto it = std::find_if_not( + ctx.objectFiles.begin(), ctx.objectFiles.end(), + [flags](InputFile *f) { return flags == getEFlags(f); }); + it != ctx.objectFiles.end()) + error(toString(*it) + + ": cannot link object files with incompatible target ISA"); + + return flags; +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index df509c29b4d8..b4d3c36323c3 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -24,6 +24,7 @@ add_lld_library(lldELF Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp + Arch/EVM.cpp Arch/EraVM.cpp Arch/Hexagon.cpp Arch/LoongArch.cpp diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 2ce13c10342f..a1850456ed45 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -52,6 +52,9 @@ #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" +// EVM local begin +#include "llvm/Object/ELF.h" +// EVM local end #include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 2ecdd74cd6b3..b5c8ba088fa7 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1614,6 +1614,10 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; + // EVM local begin + case Triple::evm: + return EM_EVM; + // EVM local end default: error(path + ": could not infer e_machine from bitcode target triple " + t.str()); diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index d29d0d156950..08d8d5b0804d 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -91,6 +91,10 @@ TargetInfo *elf::getTarget() { return getSPARCV9TargetInfo(); case EM_X86_64: return getX86_64TargetInfo(); + // EVM local begin + case EM_EVM: + return getEVMTargetInfo(); + // EVM local end } llvm_unreachable("unknown target machine"); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index a77fe0213ae0..3097bc2bae60 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -188,6 +188,9 @@ TargetInfo *getRISCVTargetInfo(); TargetInfo *getSPARCV9TargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); +// EVM local begin +TargetInfo *getEVMTargetInfo(); +// EVM local end template TargetInfo *getMipsTargetInfo(); struct ErrorPlace { diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h index 5c1567983b0a..f38b8565e0b9 100644 --- a/lld/include/lld-c/LLDAsLibraryC.h +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -123,6 +123,32 @@ char **LLVMGetUndefinedLinkerSymbolsEraVM(LLVMMemoryBufferRef inBuffer, * LLVMGetUndefinedSymbolsEraVM(). */ void LLVMDisposeUndefinedLinkerSymbolsEraVM(char *linkerSymbolNames[], uint64_t numLinkerSymbols); + +/** Links the deploy and runtime ELF object files using the information about + * dependencies. + * \p inBuffers - array of input memory buffers with following structure: + * + * inBuffers[0] - deploy ELF object code + * inBuffers[1] - deployed (runtime) ELF object code + * -------------------------- + * inBuffers[2] - 1-st sub-contract (final EVM bytecode) + * ... + * inBuffers[N] - N-st sub-contract (final EVM bytecode) + * + * Sub-contracts are optional. They should have the same ordering as in + * the YUL layout. + * + * \p inBuffersIDs - array of string identifiers of the buffers. IDs correspond + * to the object names in the YUL layout. + * On success, outBuffers[0] will contain the deploy bytecode and outBuffers[1] + * the runtime bytecode. + * In case of an error the function returns 'true' and the error message is + * passes in \p errorMessage. The message should be disposed by + * 'LLVMDisposeMessage'. */ +LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef *inBuffers, const char *inBuffersIDs[], + uint64_t numInBuffers, LLVMMemoryBufferRef outBuffers[2], + char **errorMessage); + LLVM_C_EXTERN_C_END #endif // LLD_C_LLDASLIBRARYC_H diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp index 4de4aed67e91..2ecf8332e0cc 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -443,3 +443,199 @@ void LLVMDisposeUndefinedLinkerSymbolsEraVM(char *linkerSymbolNames[], std::free(linkerSymbolNames[idx]); std::free(linkerSymbolNames); } + +//----------------------------------------------------------------------------// + +/// This function generates a linker script for EVM architecture. +/// \p memBufs - array of input memory buffers with following structure: +/// +/// memBufs[0] - deploy object code +/// memBufs[1] - deployed object code +/// -------------------------- +/// memBufs[2] - 1-st sub-contract (final EVM bytecode) +/// ... +/// memBufs[N] - N-st sub-contract (final EVM bytecode) +/// +/// Sub-contracts are optional. They should have the same ordering as in +/// the YUL layout. +/// +/// \p bufIDs - array of string identifiers of the buffers. IDs correspond +/// to the object names in the YUL layout. +/// +/// For example, the YUL object: +/// +/// |--D_105_deploy --||--D_105_deployed --||-- B_40 --| +/// +/// __datasize_B_40 = 1384; +/// SECTIONS { +/// . = 0; +/// .text : SUBALIGN(1) { +/// D_105(.text); +/// __dataoffset_D_105_deployed = .; +/// D_105_deployed(.text); +/// __datasize_D_105_deployed = . - __dataoffset_D_105_deployed; +/// __dataoffset_B_40 = .; +/// __datasize_D_105 = __dataoffset_B_40 + __datasize_B_40; +/// LONG(__dataoffset_D_105_deployed); +/// } +/// +/// The dot '.' denotes current location in the resulting file. +/// The purpose of the script is to define datasize/dataoffset absolute symbols +/// that reflect the YUL layout. +static std::string creteEVMLinkerScript(ArrayRef memBufs, + ArrayRef bufIDs) { + assert(memBufs.size() == bufIDs.size()); + size_t numObjectsToLink = memBufs.size(); + StringRef dataSizePrefix("__datasize_"); + StringRef dataOffsetPrefix("__dataoffset_"); + + // Define the script part related to the top-level contract. + StringRef topName(bufIDs[0]); + StringRef deployed(bufIDs[1]); + + // Contains the linker script part corresponding to the top-level contract. + // For the example above, this contains: + // D_105(.text); + // __dataoffset_D_105_deployed = .; + // D_105_deployed(.text); + // __datasize_D_105_deployed = . - __dataoffset_D_105_deployed; + Twine topLevel = topName + "(.text);\n" + dataOffsetPrefix + deployed + + " = .;\n" + deployed + "(.text);\n" + dataSizePrefix + + deployed + " = . - " + dataOffsetPrefix + deployed + ";\n"; + + // Contains symbols whose values are the sizes of the dependent contracts. + // For the example above, this contains: + // __datasize_B_40 = 1384; + std::string symDatasizeDeps; + + // Contains symbols whose values are the offsets of the dependent contracts. + // For the example above, this contains: + // __dataoffset_B_40 = .; + std::string symDataOffsetDeps; + if (numObjectsToLink > 2) { + // Define datasize symbols for the dependent contracts. They start after + // {deploy, deployed} pair of the top-level contract, i.e. at index 2. + for (unsigned idx = 2; idx < numObjectsToLink; ++idx) + symDatasizeDeps += (dataSizePrefix + bufIDs[idx] + " = " + + Twine(LLVMGetBufferSize(memBufs[idx])) + ";\n") + .str(); + + symDataOffsetDeps = (dataOffsetPrefix + bufIDs[2] + " = .;\n").str(); + for (unsigned idx = 3; idx < numObjectsToLink; ++idx) + symDataOffsetDeps += + (dataOffsetPrefix + bufIDs[idx] + " = " + dataOffsetPrefix + + bufIDs[idx - 1] + " + " + dataSizePrefix + bufIDs[idx - 1] + ";\n") + .str(); + } + + // Contains a symbol whose value is the total size of the top-level contract + // with all the dependencies. + std::string symDatasizeTop = (dataSizePrefix + topName + " = ").str(); + if (numObjectsToLink > 2) + symDatasizeTop += (dataOffsetPrefix + bufIDs.back() + " + " + + dataSizePrefix + bufIDs.back() + ";\n") + .str(); + else + symDatasizeTop += ".;\n"; + + // Emit size of the deploy code offset as the 4-byte unsigned integer. + // This is needed to determine which offset the deployed code starts at + // in the linked binary. + Twine deploySize = "LONG(" + dataOffsetPrefix + deployed + ");\n"; + + Twine script = formatv("{0}\n\ +ENTRY(0);\n\ +SECTIONS {\n\ + . = 0;\n\ + .code : SUBALIGN(1) {\n\ +{1}\ +{2}\ +{3}\ +{4}\ + }\n\ +}\n\ +", + symDatasizeDeps, topLevel, symDataOffsetDeps, + symDatasizeTop, deploySize); + + return script.str(); +} + +LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], + const char *inBuffersIDs[], uint64_t numInBuffers, + LLVMMemoryBufferRef outBuffers[2], char **errorMessage) { + assert(numInBuffers > 1); + SmallVector localInMemBufRefs(3); + SmallVector> localInMemBufs(3); + for (unsigned idx = 0; idx < 2; ++idx) { + MemoryBufferRef ref = *unwrap(inBuffers[idx]); + localInMemBufs[idx] = + MemoryBuffer::getMemBuffer(ref.getBuffer(), inBuffersIDs[idx], + /*RequiresNullTerminator*/ false); + localInMemBufRefs[idx] = localInMemBufs[idx]->getMemBufferRef(); + } + + std::string linkerScript = creteEVMLinkerScript( + ArrayRef(inBuffers, numInBuffers), ArrayRef(inBuffersIDs, numInBuffers)); + std::unique_ptr scriptBuf = + MemoryBuffer::getMemBuffer(linkerScript, "script.x"); + localInMemBufRefs[2] = scriptBuf->getMemBufferRef(); + + SmallVector lldArgs; + lldArgs.push_back("ld.lld"); + lldArgs.push_back("-T"); + lldArgs.push_back("script.x"); + + // Use remapping of file names (a linker feature) to replace file names with + // indexes in the array of memory buffers. + Twine remapStr("--remap-inputs="); + std::string remapDeployStr = (remapStr + inBuffersIDs[0] + "=0").str(); + lldArgs.push_back(remapDeployStr.c_str()); + + std::string remapDeployedStr = (remapStr + inBuffersIDs[1] + "=1").str(); + lldArgs.push_back(remapDeployedStr.c_str()); + + lldArgs.push_back("--remap-inputs=script.x=2"); + + // Deploy code + lldArgs.push_back(inBuffersIDs[0]); + // Deployed code + lldArgs.push_back(inBuffersIDs[1]); + + lldArgs.push_back("--oformat=binary"); + + SmallString<0> codeString; + raw_svector_ostream ostream(codeString); + SmallString<0> errorString; + raw_svector_ostream errorOstream(errorString); + + // Lld-as-a-library is not thread safe, as it has a global state, + // so we need to protect lld from simultaneous access from different threads. + std::unique_lock lock(lldMutex); + const lld::Result s = + lld::lldMainMemBuf(localInMemBufRefs, &ostream, lldArgs, outs(), + errorOstream, {{lld::Gnu, &lld::elf::linkMemBuf}}); + lock.unlock(); + + bool ret = !s.retCode && s.canRunAgain; + if (!ret) { + *errorMessage = strdup(errorString.c_str()); + return true; + } + + StringRef data = ostream.str(); + // Linker script adds size of the deploy code as a 8-byte BE unsigned to the + // end of .text section. Knowing this, we can extract final deploy and + // deployed codes. + assert(data.size() > 4); + size_t deploySize = support::endian::read32be(data.data() + data.size() - 4); + assert(deploySize < data.size()); + size_t deployedSize = data.size() - deploySize - 4; + + outBuffers[0] = LLVMCreateMemoryBufferWithMemoryRangeCopy( + data.data(), deploySize, "deploy"); + outBuffers[1] = LLVMCreateMemoryBufferWithMemoryRangeCopy( + data.data() + deploySize, deployedSize, "deployed"); + + return false; +} diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index f9af3d7f2d73..9159afcda78d 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -323,6 +323,9 @@ enum { // EraVM local begin EM_ERAVM = 260, // EraVM // EraVM local end + // EVM local begin + EM_EVM = 261 // EVM + // EVM local end }; // Object file classes. @@ -426,6 +429,13 @@ enum { #include "ELFRelocs/AArch64.def" }; +// EVM local begin +// ELF Relocation types for EVM +enum : uint8_t { +#include "ELFRelocs/EVM.def" +}; +// EVM local end + // Special values for the st_other field in the symbol table entry for AArch64. enum { // Symbol may follow different calling convention than base PCS. diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def new file mode 100644 index 000000000000..003b02562b86 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def @@ -0,0 +1,7 @@ + +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_EVM_NONE, 0) +ELF_RELOC(R_EVM_DATA, 1) diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index ac6c65676ac7..49be4b2ac66a 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -246,4 +246,20 @@ def int_evm_memcpyas1as4 WriteOnly>, ReadOnly>, ImmArg>]>; +// The following intrinsics are used for linking purposes. + +// Returns a code size of the Yul object with the name +// passed in the metadata. +def int_evm_datasize : DefaultAttrsIntrinsic< + [llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrSpeculatable] +>; + +// Returns a code offset of the Yul object with the name +// passed in the metadata. +def int_evm_dataoffset : DefaultAttrsIntrinsic< + [llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrSpeculatable] +>; + } // TargetPrefix = "evm" diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h index 5bc5e04f79ff..f9da82d91e6b 100644 --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -357,7 +357,10 @@ class MCSymbolRefExpr : public MCExpr { VK_VE_TPOFF_LO32, // symbol@tpoff_lo VK_TPREL, - VK_DTPREL + VK_DTPREL, + // EVM local begin + VK_EVM_DATA, + // EVM local end }; private: diff --git a/llvm/include/module.modulemap b/llvm/include/module.modulemap index a1cf25a010a8..3f8cdfc23676 100644 --- a/llvm/include/module.modulemap +++ b/llvm/include/module.modulemap @@ -107,6 +107,7 @@ module LLVM_BinaryFormat { textual header "llvm/BinaryFormat/ELFRelocs/Xtensa.def" textual header "llvm/BinaryFormat/WasmRelocs.def" textual header "llvm/BinaryFormat/MsgPack.def" + textual header "llvm/BinaryFormat/EVM.def" } module LLVM_Config { diff --git a/llvm/lib/BinaryFormat/ELF.cpp b/llvm/lib/BinaryFormat/ELF.cpp index 5ee6b5924fd6..be4d5ff984e5 100644 --- a/llvm/lib/BinaryFormat/ELF.cpp +++ b/llvm/lib/BinaryFormat/ELF.cpp @@ -201,6 +201,9 @@ uint16_t ELF::convertArchNameToEMachine(StringRef Arch) { // EraVM local begin .Case("eravm", EM_ERAVM) // EraVM local end + // EVM local begin + .Case("evm", EM_EVM) + // EVM local end .Default(EM_NONE); } @@ -571,6 +574,10 @@ StringRef ELF::convertEMachineToArchName(uint16_t EMachine) { case EM_ERAVM: return "eravm"; // EraVM local end + // EVM local begin + case EM_EVM: + return "evm"; + // EVM local end default: return "None"; } diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index fe01078a41d4..6a6befdd3054 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -1096,14 +1096,7 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { std::map> GroupMembers; // Write out the ELF header ... - // EVM local begin - // HACK!!! For EVM target we don't need the whole EFL file, - // but just its .text section. The natural way would be to extract - // it using objdump utility, but design of our FE doesn't admit - // usage of any other tool besides the BE itslef. - if (!Ctx.getTargetTriple().isEVM()) - writeHeader(Asm); - // EVM local end + writeHeader(Asm); // ... then the sections ... SectionOffsetsTy SectionOffsets; @@ -1120,14 +1113,6 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { const uint64_t SecStart = align(Section.getAlign()); const MCSymbolELF *SignatureSymbol = Section.getGroup(); - // EVM local begin - if (Ctx.getTargetTriple().isEVM()) { - if (Section.getName() == ".text") - writeSectionData(Asm, Section, Layout); - continue; - } - // EVM local end - writeSectionData(Asm, Section, Layout); uint64_t SecEnd = W.OS.tell(); @@ -1160,11 +1145,6 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { OWriter.TargetObjectWriter->addTargetSectionFlags(Ctx, Section); } - // EVM local begin - if (Ctx.getTargetTriple().isEVM()) - return W.OS.tell() - StartOffset; - // EVM local end - for (MCSectionELF *Group : Groups) { // Remember the offset into the file for this section. const uint64_t SecStart = align(Group->getAlign()); diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index 678b446d062d..4645f5794de2 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -388,6 +388,10 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) { case VK_VE_TLS_GD_LO32: return "tls_gd_lo"; case VK_VE_TPOFF_HI32: return "tpoff_hi"; case VK_VE_TPOFF_LO32: return "tpoff_lo"; + // EVM local begin + case VK_EVM_DATA: + return "evm_data"; + // EVM local end } llvm_unreachable("Invalid variant kind"); } @@ -532,6 +536,9 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) { .Case("tls_gd_lo", VK_VE_TLS_GD_LO32) .Case("tpoff_hi", VK_VE_TPOFF_HI32) .Case("tpoff_lo", VK_VE_TPOFF_LO32) + // EVM local begin + .Case("evm_data", VK_EVM_DATA) + // EVM local end .Default(VK_Invalid); } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 0b5109e41e71..a1b5269e563e 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -386,6 +386,10 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { TextSection = Ctx->getELFSection(".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + // EVM local begin + if (T.getArch() == Triple::evm) + TextSection->setAlignment(Align(1)); + // EVM local end DataSection = Ctx->getELFSection(".data", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index aca6b00a195c..d878bf84cd22 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -190,6 +190,15 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, } break; // EraVM local end + // EVM local begin + case ELF::EM_EVM: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/EVM.def" + default: + break; + } + break; + // EVM local end default: break; } diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index 168dcf273ed7..22b3d476d072 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -39,12 +39,36 @@ class EVMAsmPrinter : public AsmPrinter { StringRef getPassName() const override { return "EVM Assembly "; } + void SetupMachineFunction(MachineFunction &MF) override; + void emitInstruction(const MachineInstr *MI) override; void emitFunctionEntryLabel() override; + + /// Return true if the basic block has exactly one predecessor and the control + /// transfer mechanism between the predecessor and this block is a + /// fall-through. + bool isBlockOnlyReachableByFallthrough( + const MachineBasicBlock *MBB) const override; }; } // end of anonymous namespace +void EVMAsmPrinter::SetupMachineFunction(MachineFunction &MF) { + // Unbundle bundles. + for (MachineBasicBlock &MBB : MF) { + MachineBasicBlock::instr_iterator I = MBB.instr_begin(), + E = MBB.instr_end(); + for (; I != E; ++I) { + if (I->isBundledWithPred()) { + assert(I->isConditionalBranch() || I->isUnconditionalBranch()); + I->unbundleFromPred(); + } + } + } + + AsmPrinter::SetupMachineFunction(MF); +} + void EVMAsmPrinter::emitFunctionEntryLabel() { AsmPrinter::emitFunctionEntryLabel(); @@ -70,12 +94,17 @@ void EVMAsmPrinter::emitFunctionEntryLabel() { void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping, MF->getRegInfo()); - MCInst TmpInst; MCInstLowering.Lower(MI, TmpInst); EmitToStreamer(*OutStreamer, TmpInst); } +bool EVMAsmPrinter::isBlockOnlyReachableByFallthrough( + const MachineBasicBlock *MBB) const { + // For simplicity, always emit BB labels. + return false; +} + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMAsmPrinter() { const RegisterAsmPrinter X(getTheEVMTarget()); } diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index 56eefceafa20..6c8738d2d52f 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -18,6 +18,8 @@ #include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSymbol.h" using namespace llvm; @@ -82,6 +84,7 @@ EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, Custom); setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); setJumpIsExpensive(false); setMaximumJumpTableSize(0); @@ -117,6 +120,8 @@ SDValue EVMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { llvm_unreachable("Unimplemented operation lowering"); case ISD::GlobalAddress: return LowerGlobalAddress(Op, DAG); + case ISD::INTRINSIC_WO_CHAIN: + return lowerINTRINSIC_WO_CHAIN(Op, DAG); case ISD::LOAD: return LowerLOAD(Op, DAG); case ISD::STORE: @@ -141,6 +146,35 @@ SDValue EVMTargetLowering::LowerGlobalAddress(SDValue Op, DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); } +SDValue EVMTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntrID = cast(Op.getOperand(0))->getZExtValue(); + switch (IntrID) { + default: + return SDValue(); + case Intrinsic::evm_datasize: + case Intrinsic::evm_dataoffset: + return lowerIntrinsicDataSize(IntrID, Op, DAG); + } +} + +SDValue EVMTargetLowering::lowerIntrinsicDataSize(unsigned IntrID, SDValue Op, + SelectionDAG &DAG) const { + const SDLoc DL(Op); + EVT Ty = Op.getValueType(); + + MachineFunction &MF = DAG.getMachineFunction(); + const MDNode *Metadata = cast(Op.getOperand(1))->getMD(); + StringRef ContractID = cast(Metadata->getOperand(0))->getString(); + bool IsDataSize = IntrID == Intrinsic::evm_datasize; + Twine SymbolReloc = + Twine(IsDataSize ? "__datasize_" : "__dataoffset_") + ContractID; + MCSymbol *Sym = MF.getContext().getOrCreateSymbol(SymbolReloc); + return SDValue( + DAG.getMachineNode(EVM::DATA, DL, Ty, DAG.getMCSymbol(Sym, MVT::i256)), + 0); +} + SDValue EVMTargetLowering::LowerLOAD(SDValue Op, SelectionDAG &DAG) const { const SDLoc DL(Op); auto *Load = cast(Op); @@ -353,7 +387,7 @@ SDValue EVMTargetLowering::LowerFormalArguments( fail(DL, DAG, "VarArg is not supported yet"); MachineFunction &MF = DAG.getMachineFunction(); - auto *MFI = MF.getInfo(); + auto *MFI = MF.getInfo(); // Set up the incoming ARGUMENTS value, which serves to represent the liveness // of the incoming values before they're represented by virtual registers. diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 16c7c1d68e57..3c20d090c4d8 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -131,6 +131,11 @@ class EVMTargetLowering final : public TargetLowering { SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; + + SDValue lowerIntrinsicDataSize(unsigned IntrID, SDValue Op, + SelectionDAG &DAG) const; + SDValue LowerLOAD(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSTORE(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index 69b98ae7152f..caa9e68cb011 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "EVMInstrInfo.h" +#include "EVMMachineFunctionInfo.h" #include "MCTargetDesc/EVMMCTargetDesc.h" using namespace llvm; @@ -65,6 +66,12 @@ bool EVMInstrInfo::analyzeBranch(MachineBasicBlock &MBB, FBB = nullptr; Cond.clear(); + const auto *MFI = MBB.getParent()->getInfo(); + if (MFI->getIsStackified()) { + LLVM_DEBUG(dbgs() << "Can't analyze terminators in stackified code"); + return true; + } + // Iterate backwards and analyze all terminators. MachineBasicBlock::reverse_iterator I = MBB.rbegin(), E = MBB.rend(); while (I != E) { diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 02d7d745f851..8700834e45d8 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -794,7 +794,7 @@ foreach I = {1-16} in { defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; -def PUSH8_LABEL : NI<(outs), (ins jmptarget:$dst), [], false, "", 0, 0> { +def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], false, "", 0, 0> { let isCodeGenOnly = 1; } @@ -1120,3 +1120,9 @@ def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32 $imm", let BaseName = "PUSH32"; let DecoderMethod = "decodePUSH<32>"; } + +// Pseudo instructions for linkage +let isCodeGenOnly = 1, BaseName = "DATA" in { + def DATA : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATA_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; +} diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index 365734db49f2..297385478bb7 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -27,30 +27,23 @@ using namespace llvm; extern cl::opt EVMKeepRegisters; -static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) { - // Remove all uses of stackified registers to bring the instruction format - // into its final stack form used throughout MC, and transition opcodes to - // their _S variant. +// Stackify instruction that were not stackified before. +// Only two instructions need to be stackified here: PUSH_LABEL and DATA_S, +static void stackifyInstruction(const MachineInstr *MI, MCInst &OutMI) { if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm()) return; - // Transform 'register' instruction to 'stack' one. - unsigned RegOpcode = OutMI.getOpcode(); - if (RegOpcode == EVM::PUSH8_LABEL) { - // Replace PUSH8_LABEL with PUSH8_S opcode. - OutMI.setOpcode(EVM::PUSH8_S); - } else { - unsigned StackOpcode = EVM::getStackOpcode(RegOpcode); - OutMI.setOpcode(StackOpcode); - } + // Check there are no register operands. + assert(std::all_of(OutMI.begin(), OutMI.end(), + [](const MCOperand &MO) { return !MO.isReg(); })); - // Remove register operands. - for (auto I = OutMI.getNumOperands(); I; --I) { - auto &MO = OutMI.getOperand(I - 1); - if (MO.isReg()) { - OutMI.erase(&MO); - } - } + // Set up final opcodes for the following codegen-only instructions. + unsigned Opcode = OutMI.getOpcode(); + if (Opcode == EVM::PUSH_LABEL || Opcode == EVM::DATA_S) + OutMI.setOpcode(EVM::PUSH4_S); + + // Check that all the instructions are in the 'stack' form. + assert(EVM::getRegisterOpcode(OutMI.getOpcode())); } MCSymbol * @@ -130,10 +123,15 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { MCOp = MCOperand::createExpr(EVMCImmMCExpr::create(*Str, Ctx)); } } break; - case MachineOperand::MO_MCSymbol: - MCOp = - MCOperand::createExpr(MCSymbolRefExpr::create(MO.getMCSymbol(), Ctx)); - break; + case MachineOperand::MO_MCSymbol: { + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_None; + unsigned Opc = MI->getOpcode(); + if (Opc == EVM::DATA_S) + Kind = MCSymbolRefExpr::VariantKind::VK_EVM_DATA; + + MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(MO.getMCSymbol(), Kind, Ctx)); + } break; case MachineOperand::MO_MachineBasicBlock: MCOp = MCOperand::createExpr( MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx)); @@ -148,7 +146,7 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { OutMI.addOperand(MCOp); } if (!EVMKeepRegisters) - removeRegisterOperands(MI, OutMI); + stackifyInstruction(MI, OutMI); else if (Desc.variadicOpsAreDefs()) OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs())); } diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp index 63574319e396..50db06c257f9 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp @@ -13,11 +13,26 @@ #include "EVMMachineFunctionInfo.h" using namespace llvm; -EVMFunctionInfo::~EVMFunctionInfo() = default; // anchor. +EVMMachineFunctionInfo::~EVMMachineFunctionInfo() = default; -MachineFunctionInfo * -EVMFunctionInfo::clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, - const DenseMap - &Src2DstMBB) const { - return DestMF.cloneInfo(*this); +MachineFunctionInfo *EVMMachineFunctionInfo::clone( + BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap &Src2DstMBB) + const { + return DestMF.cloneInfo(*this); +} + +yaml::EVMMachineFunctionInfo::~EVMMachineFunctionInfo() = default; + +yaml::EVMMachineFunctionInfo::EVMMachineFunctionInfo( + const llvm::EVMMachineFunctionInfo &MFI) + : IsStackified(MFI.getIsStackified()) {} + +void yaml::EVMMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) { + MappingTraits::mapping(YamlIO, *this); +} + +void EVMMachineFunctionInfo::initializeBaseYamlFields( + const yaml::EVMMachineFunctionInfo &YamlMFI) { + IsStackified = YamlMFI.IsStackified; } diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h index b39709960358..0d49c32cebe8 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h @@ -16,13 +16,33 @@ #include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/CodeGen/MIRYamlMapping.h" #include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/MC/MCSymbolWasm.h" namespace llvm { +class EVMMachineFunctionInfo; + +namespace yaml { + +struct EVMMachineFunctionInfo final : public yaml::MachineFunctionInfo { + bool IsStackified = false; + + EVMMachineFunctionInfo() = default; + explicit EVMMachineFunctionInfo(const llvm::EVMMachineFunctionInfo &MFI); + ~EVMMachineFunctionInfo() override; + + void mappingImpl(yaml::IO &YamlIO) override; +}; + +template <> struct MappingTraits { + static void mapping(IO &YamlIO, EVMMachineFunctionInfo &MFI) { + YamlIO.mapOptional("isStackified", MFI.IsStackified, false); + } +}; +} // end namespace yaml + /// This class is derived from MachineFunctionInfo and contains private /// EVM-specific information for each MachineFunction. -class EVMFunctionInfo final : public MachineFunctionInfo { +class EVMMachineFunctionInfo final : public MachineFunctionInfo { /// A mapping from CodeGen vreg index to a boolean value indicating whether /// the given register is considered to be "stackified", meaning it has been /// determined or made to meet the stack requirements: @@ -34,16 +54,21 @@ class EVMFunctionInfo final : public MachineFunctionInfo { /// Number of parameters. Their type doesn't matter as it always is i256. unsigned NumberOfParameters = 0; + /// If the MF's instructions are in 'stack' form. + bool IsStackified = false; + public: - explicit EVMFunctionInfo(MachineFunction &MF) {} - EVMFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {} - ~EVMFunctionInfo() override; + explicit EVMMachineFunctionInfo(MachineFunction &MF) {} + EVMMachineFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {} + ~EVMMachineFunctionInfo() override; MachineFunctionInfo * clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, const DenseMap &Src2DstMBB) const override; + void initializeBaseYamlFields(const yaml::EVMMachineFunctionInfo &YamlMFI); + void stackifyVReg(MachineRegisterInfo &MRI, unsigned VReg) { assert(MRI.getUniqueVRegDef(VReg)); auto I = Register::virtReg2Index(VReg); @@ -72,7 +97,12 @@ class EVMFunctionInfo final : public MachineFunctionInfo { unsigned getNumParams() const { return NumberOfParameters; } + + void setIsStackified(bool Val = true) { IsStackified = Val; } + + bool getIsStackified() const { return IsStackified; } }; + } // end namespace llvm #endif // LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H diff --git a/llvm/lib/Target/EVM/EVMRegColoring.cpp b/llvm/lib/Target/EVM/EVMRegColoring.cpp index f2f5fe51c6a0..d18461b3a97a 100644 --- a/llvm/lib/Target/EVM/EVMRegColoring.cpp +++ b/llvm/lib/Target/EVM/EVMRegColoring.cpp @@ -80,7 +80,7 @@ bool EVMRegColoring::runOnMachineFunction(MachineFunction &MF) { LiveIntervals *Liveness = &getAnalysis(); const MachineBlockFrequencyInfo *MBFI = &getAnalysis(); - EVMFunctionInfo &MFI = *MF.getInfo(); + EVMMachineFunctionInfo &MFI = *MF.getInfo(); // Gather all register intervals into a list and sort them. unsigned NumVRegs = MRI->getNumVirtRegs(); diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index f383fa7e4ca2..8337dc7875d1 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -268,7 +268,8 @@ static bool shouldRematerialize(const MachineInstr &Def, // TODO: Compute memory dependencies in a way that uses AliasAnalysis to be // more precise. static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, - const MachineInstr *Insert, const EVMFunctionInfo &MFI, + const MachineInstr *Insert, + const EVMMachineFunctionInfo &MFI, const MachineRegisterInfo &MRI) { const MachineInstr *DefI = Def->getParent(); const MachineInstr *UseI = Use->getParent(); @@ -390,7 +391,7 @@ static void shrinkToUses(LiveInterval &LI, LiveIntervals &LIS) { static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, MachineInstr *Def, MachineBasicBlock &MBB, MachineInstr *Insert, LiveIntervals &LIS, - EVMFunctionInfo &MFI, + EVMMachineFunctionInfo &MFI, MachineRegisterInfo &MRI) { LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump()); @@ -430,8 +431,8 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, static MachineInstr *rematerializeCheapDef( unsigned Reg, MachineOperand &Op, MachineInstr &Def, MachineBasicBlock &MBB, MachineBasicBlock::instr_iterator Insert, LiveIntervals &LIS, - EVMFunctionInfo &MFI, MachineRegisterInfo &MRI, const EVMInstrInfo *TII, - const EVMRegisterInfo *TRI) { + EVMMachineFunctionInfo &MFI, MachineRegisterInfo &MRI, + const EVMInstrInfo *TII, const EVMRegisterInfo *TRI) { LLVM_DEBUG(dbgs() << "Rematerializing cheap def: "; Def.dump()); LLVM_DEBUG(dbgs() << " - for use in "; Op.getParent()->dump()); @@ -593,7 +594,7 @@ bool EVMSingleUseExpression::runOnMachineFunction(MachineFunction &MF) { bool Changed = false; MachineRegisterInfo &MRI = MF.getRegInfo(); - EVMFunctionInfo &MFI = *MF.getInfo(); + EVMMachineFunctionInfo &MFI = *MF.getInfo(); const auto *TII = MF.getSubtarget().getInstrInfo(); const auto *TRI = MF.getSubtarget().getRegisterInfo(); auto &MDT = getAnalysis(); diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp index 5c674f4f10c4..d5c32e402c83 100644 --- a/llvm/lib/Target/EVM/EVMStackify.cpp +++ b/llvm/lib/Target/EVM/EVMStackify.cpp @@ -320,6 +320,8 @@ class StackModel { void peelPhysStack(StackType Type, unsigned NumItems, MachineBasicBlock *BB, MIIter Pos); + void stackifyInstruction(MachineInstr *MI); + static unsigned getDUPOpcode(unsigned Depth); static unsigned getSWAPOpcode(unsigned Depth); @@ -724,10 +726,10 @@ void StackModel::handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, // EVM::NoRegister. clearPhysStackAtInst(StackType::L, MI, Reg); - // Insert "PUSH8_LABEL %bb" instruction that should be be replaced with + // Insert "PUSH_LABEL %bb" instruction that should be be replaced with // the actual PUSH* one in the MC layer to contain actual jump target // offset. - BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(EVM::PUSH8_LABEL)) + BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(EVM::PUSH_LABEL)) .addMBB(MBB); // Add JUMPDEST at the beginning of the target MBB. @@ -759,7 +761,7 @@ void StackModel::handleReturn(MachineInstr *MI) { ReturnRegs.push_back(MO.getReg()); } - auto *MFI = MF->getInfo(); + auto *MFI = MF->getInfo(); if (MFI->getNumParams() >= ReturnRegs.size()) { // Move the return registers to the stack location where // arguments were resided. @@ -874,13 +876,12 @@ void StackModel::handleCall(MachineInstr *MI) { It->setPostInstrSymbol(*MF, RetSym); // Create push of the return address. - BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::PUSH8_LABEL)) - .addSym(RetSym); + BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::PUSH_LABEL)).addSym(RetSym); // Create push of the callee's address. const MachineOperand *CalleeOp = MI->explicit_uses().begin(); assert(CalleeOp->isGlobal()); - BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::PUSH8_LABEL)) + BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::PUSH_LABEL)) .addGlobalAddress(CalleeOp->getGlobal()); } @@ -987,6 +988,29 @@ void StackModel::preProcess() { BuildMI(MBB, MBB.begin(), DebugLoc(), TII->get(EVM::JUMPDEST)); } +// Remove all registers operands of the \p MI and repaces the opcode with +// the stack variant variant. +void StackModel::stackifyInstruction(MachineInstr *MI) { + if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm()) + return; + + unsigned RegOpcode = MI->getOpcode(); + if (RegOpcode == EVM::PUSH_LABEL) + return; + + // Remove register operands. + for (unsigned I = MI->getNumOperands(); I > 0; --I) { + auto &MO = MI->getOperand(I - 1); + if (MO.isReg()) { + MI->removeOperand(I - 1); + } + } + + // Transform 'register' instruction to the 'stack' one. + unsigned StackOpcode = EVM::getStackOpcode(RegOpcode); + MI->setDesc(TII->get(StackOpcode)); +} + void StackModel::postProcess() { for (MachineBasicBlock &MBB : *MF) { for (MachineInstr &MI : MBB) { @@ -1013,6 +1037,42 @@ void StackModel::postProcess() { for (auto *MI : ToErase) MI->eraseFromParent(); + + for (MachineBasicBlock &MBB : *MF) + for (MachineInstr &MI : MBB) + stackifyInstruction(&MI); + + auto *MFI = MF->getInfo(); + MFI->setIsStackified(); + + // In a stackified code register liveness has no meaning. + MachineRegisterInfo &MRI = MF->getRegInfo(); + MRI.invalidateLiveness(); + + // In EVM architecture jump target is set up using one of PUSH* instructions + // that come right before the jump instruction. + // For example: + + // PUSH_LABEL %bb.10 + // JUMPI_S + // PUSH_LABEL %bb.9 + // JUMP_S + // + // The problem here is that such MIR is not valid. There should not be + // non-terminator (PUSH) instructions between terminator (JUMP) ones. + // To overcome this issue, we bundle adjacent instructions + // together and unbundle them in the AsmPrinter. + for (MachineBasicBlock &MBB : *MF) { + MachineBasicBlock::instr_iterator I = MBB.instr_begin(), + E = MBB.instr_end(); + for (; I != E; ++I) { + if (I->isBranch()) { + auto P = std::next(I); + if (P != E && P->getOpcode() == EVM::PUSH_LABEL) + I->bundleWithPred(); + } + } + } } void StackModel::dumpState() const { @@ -1129,9 +1189,6 @@ class EVMStackify final : public MachineFunctionPass { AU.addRequired(); AU.addRequired(); AU.addPreserved(); - AU.addPreserved(); - AU.addPreserved(); - AU.addPreservedID(LiveVariablesID); AU.addPreserved(); MachineFunctionPass::getAnalysisUsage(AU); } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index c933eeae4e26..eaf0892de5ff 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -14,10 +14,12 @@ #include "EVMMachineFunctionInfo.h" #include "EVMTargetMachine.h" +#include "EVMTargetObjectFile.h" #include "EVMTargetTransformInfo.h" #include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/MIRParser/MIParser.h" +#include "llvm/CodeGen/MIRYamlMapping.h" #include "llvm/CodeGen/Passes.h" -#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/InitializePasses.h" #include "llvm/MC/MCAsmInfo.h" @@ -70,7 +72,7 @@ EVMTargetMachine::EVMTargetMachine(const Target &T, const Triple &TT, : LLVMTargetMachine(T, computeDataLayout(), TT, CPU, FS, Options, getEffectiveRelocModel(RM), getEffectiveCodeModel(CM, CodeModel::Small), OL), - TLOF(std::make_unique()), + TLOF(std::make_unique()), Subtarget(TT, std::string(CPU), std::string(FS), *this) { setRequiresStructuredCFG(true); initAsmInfo(); @@ -84,7 +86,26 @@ EVMTargetMachine::getTargetTransformInfo(const Function &F) const { MachineFunctionInfo *EVMTargetMachine::createMachineFunctionInfo( BumpPtrAllocator &Allocator, const Function &F, const TargetSubtargetInfo *STI) const { - return EVMFunctionInfo::create(Allocator, F, STI); + return EVMMachineFunctionInfo::create(Allocator, F, + STI); +} + +yaml::MachineFunctionInfo *EVMTargetMachine::createDefaultFuncInfoYAML() const { + return new yaml::EVMMachineFunctionInfo(); +} + +yaml::MachineFunctionInfo * +EVMTargetMachine::convertFuncInfoToYAML(const MachineFunction &MF) const { + const auto *MFI = MF.getInfo(); + return new yaml::EVMMachineFunctionInfo(*MFI); +} + +bool EVMTargetMachine::parseMachineFunctionInfo( + const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS, + SMDiagnostic &Error, SMRange &SourceRange) const { + const auto &YamlMFI = static_cast(MFI); + PFS.MF.getInfo()->initializeBaseYamlFields(YamlMFI); + return false; } void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.h b/llvm/lib/Target/EVM/EVMTargetMachine.h index 10309fc56005..3bb1b542b48e 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.h +++ b/llvm/lib/Target/EVM/EVMTargetMachine.h @@ -43,6 +43,14 @@ class EVMTargetMachine final : public LLVMTargetMachine { createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F, const TargetSubtargetInfo *STI) const override; + yaml::MachineFunctionInfo *createDefaultFuncInfoYAML() const override; + yaml::MachineFunctionInfo * + convertFuncInfoToYAML(const MachineFunction &MF) const override; + bool parseMachineFunctionInfo(const yaml::MachineFunctionInfo &, + PerFunctionMIParsingState &PFS, + SMDiagnostic &Error, + SMRange &SourceRange) const override; + TargetLoweringObjectFile *getObjFileLowering() const override { return TLOF.get(); } diff --git a/llvm/lib/Target/EVM/EVMTargetObjectFile.h b/llvm/lib/Target/EVM/EVMTargetObjectFile.h new file mode 100644 index 000000000000..1afa2bb7fb41 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetObjectFile.h @@ -0,0 +1,32 @@ +//===---------- EVMTargetObjectFile.h - EVM Object Info -*- C++ ---------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the EVM-specific subclass of +// TargetLoweringObjectFile. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMTARGETOBJECTFILE_H +#define LLVM_LIB_TARGET_EVM_EVMTARGETOBJECTFILE_H + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" + +namespace llvm { + +class EVMELFTargetObjectFile final : public TargetLoweringObjectFileELF { +public: + EVMELFTargetObjectFile() = default; + + // Code sections need to be aligned on 1, otherwise linker will add padding + // between .text sections of the object files being linked. + unsigned getTextSectionAlignment() const override { return 1; } +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp index 8d38a9ab88d6..b8af5de6aab8 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp @@ -23,6 +23,9 @@ #include "llvm/MC/MCValue.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" + +#include using namespace llvm; @@ -97,18 +100,14 @@ bool EVMAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, const MCFixupKindInfo &EVMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { const static std::array Infos = { {// This table *must* be in the order that the fixup_* kinds are defined - // in - // EVMFixupKinds.h. + // in EVMFixupKinds.h. // // Name Offset (bits) Size (bits) Flags - {"fixup_SecRel_i64", 8, 8 * 8, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i56", 8, 8 * 7, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i48", 8, 8 * 6, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i40", 8, 8 * 5, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i32", 8, 8 * 4, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i24", 8, 8 * 3, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i16", 8, 8 * 2, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i8", 8, 8 * 1, MCFixupKindInfo::FKF_IsTarget}}}; + {"fixup_SecRel_i32", 0, 8 * 4, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i24", 0, 8 * 3, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i16", 0, 8 * 2, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i8", 0, 8 * 1, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_Data_i32", 0, 8 * 4, MCFixupKindInfo::FKF_IsTarget}}}; if (Kind < FirstTargetFixupKind) llvm_unreachable("Unexpected fixup kind"); @@ -128,7 +127,17 @@ bool EVMAsmBackend::evaluateTargetFixup(const MCAssembler &Asm, assert(unsigned(Fixup.getTargetKind() - FirstTargetFixupKind) < getNumFixupKinds() && "Invalid kind!"); + + // The following fixups should be emited as relocations, + // as they can only be resolved at link time. + unsigned FixUpKind = Fixup.getTargetKind(); + if (FixUpKind == EVM::fixup_Data_i32) + return false; + Value = Target.getConstant(); + if (Value > std::numeric_limits::max()) + report_fatal_error("Fixup value exceeds the displacement 2^32"); + if (const MCSymbolRefExpr *A = Target.getSymA()) { const MCSymbol &Sym = A->getSymbol(); assert(Sym.isDefined()); @@ -143,14 +152,16 @@ void EVMAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { - const MCFixupKindInfo &Info = getFixupKindInfo(Fixup.getKind()); - unsigned NumBytes = alignTo(Info.TargetSize, 8) / 8; + if (!IsResolved) + return; // Doesn't change encoding. if (Value == 0) return; - unsigned Offset = Fixup.getOffset() + Info.TargetOffset / 8; + const MCFixupKindInfo &Info = getFixupKindInfo(Fixup.getKind()); + unsigned NumBytes = alignTo(Info.TargetSize, 8) / 8; + unsigned Offset = Fixup.getOffset(); assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); LLVM_DEBUG(dbgs() << "applyFixup: value: " << Value @@ -171,18 +182,6 @@ void EVMAsmBackend::relaxInstruction(MCInst &Inst, switch (Inst.getOpcode()) { default: llvm_unreachable("Unexpected instruction for relaxation"); - case EVM::PUSH8_S: - Inst.setOpcode(EVM::PUSH7_S); - break; - case EVM::PUSH7_S: - Inst.setOpcode(EVM::PUSH6_S); - break; - case EVM::PUSH6_S: - Inst.setOpcode(EVM::PUSH5_S); - break; - case EVM::PUSH5_S: - Inst.setOpcode(EVM::PUSH4_S); - break; case EVM::PUSH4_S: Inst.setOpcode(EVM::PUSH3_S); break; @@ -200,6 +199,12 @@ bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, const MCRelaxableFragment *DF, const MCAsmLayout &Layout, const bool WasForced) const { + unsigned FixUpKind = Fixup.getTargetKind(); + // The following fixups shouls always be emited as relocations, + // as they can only be resolved at linking time. + if (FixUpKind == EVM::fixup_Data_i32) + return false; + assert(Resolved); unsigned Opcode = EVM::getPUSHOpcode(APInt(256, Value)); // The first byte of an instruction is an opcode, so @@ -211,17 +216,9 @@ bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, LLVM_DEBUG(Fixup.getValue()->print(dbgs(), nullptr)); LLVM_DEBUG(dbgs() << '\n'); - switch (Fixup.getTargetKind()) { + switch (FixUpKind) { default: llvm_unreachable("Unexpected target fixup kind"); - case EVM::fixup_SecRel_i64: - return OffsetByteWidth < 8; - case EVM::fixup_SecRel_i56: - return OffsetByteWidth < 7; - case EVM::fixup_SecRel_i48: - return OffsetByteWidth < 6; - case EVM::fixup_SecRel_i40: - return OffsetByteWidth < 5; case EVM::fixup_SecRel_i32: return OffsetByteWidth < 4; case EVM::fixup_SecRel_i24: @@ -236,10 +233,6 @@ bool EVMAsmBackend::mayNeedRelaxation(const MCInst &Inst, switch (Inst.getOpcode()) { default: return false; - case EVM::PUSH8_S: - case EVM::PUSH7_S: - case EVM::PUSH6_S: - case EVM::PUSH5_S: case EVM::PUSH4_S: case EVM::PUSH3_S: case EVM::PUSH2_S: diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp index 143f2be2aaea..802cac1eea6d 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/EVMFixupKinds.h" #include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/MC/MCELFObjectWriter.h" @@ -20,8 +21,8 @@ namespace { class EVMELFObjectWriter final : public MCELFObjectTargetWriter { public: explicit EVMELFObjectWriter(uint8_t OSABI) - : MCELFObjectTargetWriter(false, OSABI, ELF::EM_NONE, - /*HasRelocationAddend*/ true){}; + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_EVM, + /*HasRelocationAddend*/ true) {} EVMELFObjectWriter(const EVMELFObjectWriter &) = delete; EVMELFObjectWriter(EVMELFObjectWriter &&) = delete; @@ -30,17 +31,32 @@ class EVMELFObjectWriter final : public MCELFObjectTargetWriter { ~EVMELFObjectWriter() override = default; protected: + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override; + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, - const MCFixup &Fixup, bool IsPCRel) const override { - // Translate fixup kind to ELF relocation type. - switch (Fixup.getTargetKind()) { - default: - llvm_unreachable("Fixups are not supported for EVM"); - } - } + const MCFixup &Fixup, bool IsPCRel) const override; }; } // end of anonymous namespace +bool EVMELFObjectWriter::needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const { + // We support only relocations with a symbol, not section. + return true; +} + +unsigned EVMELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + // Translate fixup kind to ELF relocation type. + switch (Fixup.getTargetKind()) { + default: + llvm_unreachable("Unexpected EVM fixup"); + case EVM::fixup_Data_i32: + return ELF::R_EVM_DATA; + } +} + std::unique_ptr llvm::createEVMELFObjectWriter(uint8_t OSABI) { return std::make_unique(OSABI); diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h index fa16da8cb23f..ed5ea473e603 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h @@ -14,14 +14,11 @@ namespace llvm { namespace EVM { enum Fixups { - fixup_SecRel_i64 = FirstTargetFixupKind, // 64-bit unsigned - fixup_SecRel_i56, // 56-bit unsigned - fixup_SecRel_i48, // 48-bit unsigned - fixup_SecRel_i40, // 40-bit unsigned - fixup_SecRel_i32, // 32-bit unsigned + fixup_SecRel_i32 = FirstTargetFixupKind, // 32-bit unsigned fixup_SecRel_i24, // 24-bit unsigned fixup_SecRel_i16, // 16-bit unsigned fixup_SecRel_i8, // 8-bit unsigned + fixup_Data_i32, // 32-bit unsigned // Marker LastTargetFixupKind, diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp index 7980c6d1e070..9363d06e4d2f 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -50,20 +50,20 @@ class EVMMCCodeEmitter final : public MCCodeEmitter { EVMMCCodeEmitter(MCContext &Ctx, MCInstrInfo const &MCII) : MCII(MCII) {} }; -EVM::Fixups getFixupForOpc(unsigned Opcode) { +EVM::Fixups getFixupForOpc(unsigned Opcode, MCSymbolRefExpr::VariantKind Kind) { switch (Opcode) { default: llvm_unreachable("Unexpected MI for the SymbolRef MO"); - case EVM::PUSH8_S: - return EVM::fixup_SecRel_i64; - case EVM::PUSH7_S: - return EVM::fixup_SecRel_i56; - case EVM::PUSH6_S: - return EVM::fixup_SecRel_i48; - case EVM::PUSH5_S: - return EVM::fixup_SecRel_i40; case EVM::PUSH4_S: - return EVM::fixup_SecRel_i32; + switch (Kind) { + default: + llvm_unreachable("Unexpected variant kind for MI"); + case MCSymbolRefExpr::VariantKind::VK_EVM_DATA: + return EVM::fixup_Data_i32; + case MCSymbolRefExpr::VariantKind::VK_None: + return EVM::fixup_SecRel_i32; + } + break; case EVM::PUSH3_S: return EVM::fixup_SecRel_i24; case EVM::PUSH2_S: @@ -112,9 +112,13 @@ unsigned EVMMCCodeEmitter::getMachineOpValue(const MCInst &MI, const auto *CImmExp = cast(MO.getExpr()); Op = APInt(Op.getBitWidth(), CImmExp->getString(), /*radix=*/10); } else if (Kind == MCExpr::ExprKind::SymbolRef) { - EVM::Fixups Fixup = getFixupForOpc(MI.getOpcode()); + const auto *RefExpr = cast(MO.getExpr()); + MCSymbolRefExpr::VariantKind Kind = RefExpr->getKind(); + EVM::Fixups Fixup = getFixupForOpc(MI.getOpcode(), Kind); + // The byte index of start of the relocation is always 1, as + // we need to skip the instruction opcode which is always one byte. Fixups.push_back( - MCFixup::create(0, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); + MCFixup::create(1, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); } } else { llvm_unreachable("Unexpected MC operand type"); diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp index c505d06c95bd..b601421607e9 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp @@ -11,6 +11,9 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/EVMTargetStreamer.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Casting.h" using namespace llvm; @@ -18,7 +21,12 @@ using namespace llvm; EVMTargetStreamer::EVMTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} -EVMTargetStreamer::~EVMTargetStreamer() = default; +void EVMTargetStreamer::emitLabel(MCSymbol *Symbol) { + // This is mostly a workaround for the current linking scheme. + // Mark all the symbols as local to their translation units. + auto *ELFSymbol = cast(Symbol); + ELFSymbol->setBinding(ELF::STB_LOCAL); +} EVMTargetObjStreamer::EVMTargetObjStreamer(MCStreamer &S) : EVMTargetStreamer(S) {} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h index 06afc76d8fc2..a09b7b8c86c8 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h @@ -24,7 +24,8 @@ class EVMTargetStreamer : public MCTargetStreamer { EVMTargetStreamer(EVMTargetStreamer &&) = delete; EVMTargetStreamer &operator=(const EVMTargetStreamer &) = delete; EVMTargetStreamer &operator=(EVMTargetStreamer &&) = delete; - ~EVMTargetStreamer() override; + + void emitLabel(MCSymbol *Symbol) override; }; /// This part is for ASCII assembly output diff --git a/llvm/test/CodeGen/EVM/fallthrough.mir b/llvm/test/CodeGen/EVM/fallthrough.mir new file mode 100644 index 000000000000..b5efb4f62911 --- /dev/null +++ b/llvm/test/CodeGen/EVM/fallthrough.mir @@ -0,0 +1,45 @@ +# RUN: llc -x mir --start-after=evm-stackify < %s | FileCheck %s + + +--- | + + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + define void @test_fallthrough() { ret void } + +... +--- +# CHECK: PUSH4 @.BB0_1 +# CHECK-NEXT: JUMPI +# CHECK-NEXT: PUSH4 @.BB0_2 +# CHECK-NEXT: JUMP +# CHECK-NEXT: .BB0_1: + +name: test_fallthrough +tracksRegLiveness: false +machineFunctionInfo: + isStackified: true +body: | + bb.0: + JUMPDEST_S + PUSH_LABEL %bb.10 { + JUMPI_S + } + PUSH_LABEL %bb.13 { + JUMP_S + } + + bb.10: + liveins: $value_stack + JUMPDEST_S + PUSH0_S + PUSH0_S + REVERT_S + + bb.13: + liveins: $value_stack + JUMPDEST_S + PUSH0_S + PUSH0_S + REVERT_S +... diff --git a/llvm/test/CodeGen/Generic/undef-phi.ll b/llvm/test/CodeGen/Generic/undef-phi.ll index 89d73901436d..0e221fe612ab 100644 --- a/llvm/test/CodeGen/Generic/undef-phi.ll +++ b/llvm/test/CodeGen/Generic/undef-phi.ll @@ -1,5 +1,4 @@ ; RUN: llc < %s -verify-machineinstrs -verify-coalescing -; UNSUPPORTED: target=evm{{.*}} ; ; This function has a PHI with one undefined input. Verify that PHIElimination ; inserts an IMPLICIT_DEF instruction in the predecessor so all paths to the use diff --git a/llvm/test/MC/EVM/datasize_relocation.ll b/llvm/test/MC/EVM/datasize_relocation.ll new file mode 100644 index 000000000000..906aee7bc15a --- /dev/null +++ b/llvm/test/MC/EVM/datasize_relocation.ll @@ -0,0 +1,51 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump -r - | FileCheck %s + +; CHECK: RELOCATION RECORDS FOR [.text]: +; CHECK-NEXT: OFFSET TYPE VALUE +; CHECK-NEXT: {{\d*}} R_EVM_DATA __datasize_D_105_deployed +; CHECK-NEXT: {{\d*}} R_EVM_DATA __dataoffset_D_105_deployed + +; TODO: CRP-1575. Rewrite the test in assembly. +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +declare void @llvm.memcpy.p1i256.p4i256.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +; Function Attrs: noreturn nounwind +declare void @llvm.evm.return(ptr addrspace(1), i256) #1 + +; Function Attrs: noreturn nounwind +declare void @llvm.evm.revert(ptr addrspace(1), i256) #1 + +; Function Attrs: nofree noinline noreturn null_pointer_is_valid +define void @__entry() local_unnamed_addr #2 { +entry: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %callvalue = tail call i256 @llvm.evm.callvalue() + %if_condition_compared.not = icmp eq i256 %callvalue, 0 + br i1 %if_condition_compared.not, label %if_join, label %if_main + +if_main: ; preds = %entry + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +if_join: ; preds = %entry + %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) + %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) + %rt_ptr = inttoptr i256 %deployed_off to ptr addrspace(4) + call void @llvm.memcpy.p1i256.p4i256.i256(ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), ptr addrspace(4) %rt_ptr, i256 %deployed_size, i1 false) + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 %deployed_size) + unreachable +} + +attributes #0 = { nounwind } +attributes #1 = { noreturn nounwind } +attributes #2 = { nofree noinline noreturn null_pointer_is_valid } + +!1 = !{!"D_105_deployed"} diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index f8156e20028e..714bb10de398 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1261,6 +1261,9 @@ const EnumEntry ElfMachineType[] = { // EraVM local begin ENUM_ENT(EM_ERAVM, "EraVM"), // EraVM local end + // EVM local begin + ENUM_ENT(EM_EVM, "EVM"), + // EVM local end }; const EnumEntry ElfSymbolBindings[] = {