From d8aae40fcc71abc839fac7109928df52e3688c35 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 2 Sep 2024 09:48:54 +0200 Subject: [PATCH 001/203] [APInt] Add default-disabled assertion to APInt constructor (#106524) If the uint64_t constructor is used, assert that the value is actually a signed or unsigned N-bit integer depending on whether the isSigned flag is set. Provide an implicitTrunc flag to restore the previous behavior, where the argument is silently truncated instead. In this commit, implicitTrunc is enabled by default, which means that the new assertions are disabled and no actual change in behavior occurs. The plan is to flip the default once all places violating the assertion have been fixed. See #80309 for the scope of the necessary changes. The primary motivation for this change is to avoid incorrectly specified isSigned flags. A recurring problem we have is that people write something like `APInt(BW, -1)` and this works perfectly fine -- until the code path is hit with `BW > 64`. Most of our i128 specific miscompilations are caused by variants of this issue. The cost of the change is that we have to specify the correct isSigned flag (and make sure there are no excess bits) for uses where BW is always <= 64 as well. --- llvm/include/llvm/ADT/APInt.h | 19 +++++++++++++++++-- llvm/lib/Support/APInt.cpp | 14 +++++++++----- llvm/unittests/ADT/APIntTest.cpp | 5 +++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 108df7e0eaea..6804230a2d36 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -108,11 +108,26 @@ class [[nodiscard]] APInt { /// \param numBits the bit width of the constructed APInt /// \param val the initial value of the APInt /// \param isSigned how to treat signedness of val - APInt(unsigned numBits, uint64_t val, bool isSigned = false) + /// \param implicitTrunc allow implicit truncation of non-zero/sign bits of + /// val beyond the range of numBits + APInt(unsigned numBits, uint64_t val, bool isSigned = false, + bool implicitTrunc = true) : BitWidth(numBits) { + if (!implicitTrunc) { + if (BitWidth == 0) { + assert(val == 0 && "Value must be zero for 0-bit APInt"); + } else if (isSigned) { + assert(llvm::isIntN(BitWidth, val) && + "Value is not an N-bit signed value"); + } else { + assert(llvm::isUIntN(BitWidth, val) && + "Value is not an N-bit unsigned value"); + } + } if (isSingleWord()) { U.VAL = val; - clearUnusedBits(); + if (implicitTrunc || isSigned) + clearUnusedBits(); } else { initSlowCase(val, isSigned); } diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp index 24e136fcb9c7..a5b1e8535418 100644 --- a/llvm/lib/Support/APInt.cpp +++ b/llvm/lib/Support/APInt.cpp @@ -234,7 +234,8 @@ APInt& APInt::operator-=(uint64_t RHS) { APInt APInt::operator*(const APInt& RHS) const { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) - return APInt(BitWidth, U.VAL * RHS.U.VAL); + return APInt(BitWidth, U.VAL * RHS.U.VAL, /*isSigned=*/false, + /*implicitTrunc=*/true); APInt Result(getMemory(getNumWords()), getBitWidth()); tcMultiply(Result.U.pVal, U.pVal, RHS.U.pVal, getNumWords()); @@ -455,7 +456,8 @@ APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const { "Illegal bit extraction"); if (isSingleWord()) - return APInt(numBits, U.VAL >> bitPosition); + return APInt(numBits, U.VAL >> bitPosition, /*isSigned=*/false, + /*implicitTrunc=*/true); unsigned loBit = whichBit(bitPosition); unsigned loWord = whichWord(bitPosition); @@ -463,7 +465,8 @@ APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const { // Single word result extracting bits from a single word source. if (loWord == hiWord) - return APInt(numBits, U.pVal[loWord] >> loBit); + return APInt(numBits, U.pVal[loWord] >> loBit, /*isSigned=*/false, + /*implicitTrunc=*/true); // Extracting bits that start on a source word boundary can be done // as a fast memory copy. @@ -907,7 +910,8 @@ APInt APInt::trunc(unsigned width) const { assert(width <= BitWidth && "Invalid APInt Truncate request"); if (width <= APINT_BITS_PER_WORD) - return APInt(width, getRawData()[0]); + return APInt(width, getRawData()[0], /*isSigned=*/false, + /*implicitTrunc=*/true); if (width == BitWidth) return *this; @@ -955,7 +959,7 @@ APInt APInt::sext(unsigned Width) const { assert(Width >= BitWidth && "Invalid APInt SignExtend request"); if (Width <= APINT_BITS_PER_WORD) - return APInt(Width, SignExtend64(U.VAL, BitWidth)); + return APInt(Width, SignExtend64(U.VAL, BitWidth), /*isSigned=*/true); if (Width == BitWidth) return *this; diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index eb4b847185f5..fff29d24a052 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -220,11 +220,12 @@ TEST(APIntTest, i256) { } TEST(APIntTest, i1) { - const APInt neg_two(1, static_cast(-2), true); + const APInt neg_two(1, static_cast(-2), true, + /*implicitTrunc=*/true); const APInt neg_one(1, static_cast(-1), true); const APInt zero(1, 0); const APInt one(1, 1); - const APInt two(1, 2); + const APInt two(1, 2, false, /*implicitTrunc=*/true); EXPECT_EQ(0, neg_two.getSExtValue()); EXPECT_EQ(-1, neg_one.getSExtValue()); From c97aade532bbd9ecec3b8f5fbcac149da14861ce Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 15 Oct 2024 09:33:44 +0200 Subject: [PATCH 002/203] [APInt] Fix getAllOnes() with zero width (#112227) This makes sure that APInt::getAllOnes() keeps working after the APInt constructor assertions are enabled. I'm relaxing the requirement for the signed case to either an all zeros or all ones integer. This is basically saying that we can interpret the zero-width integer as either positive or negative. --- llvm/include/llvm/ADT/APInt.h | 21 ++++++++++++++------- llvm/unittests/ADT/APIntTest.cpp | 1 + 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 6804230a2d36..3a6bad335b43 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -114,14 +114,21 @@ class [[nodiscard]] APInt { bool implicitTrunc = true) : BitWidth(numBits) { if (!implicitTrunc) { - if (BitWidth == 0) { - assert(val == 0 && "Value must be zero for 0-bit APInt"); - } else if (isSigned) { - assert(llvm::isIntN(BitWidth, val) && - "Value is not an N-bit signed value"); + if (isSigned) { + if (BitWidth == 0) { + assert((val == 0 || val == uint64_t(-1)) && + "Value must be 0 or -1 for signed 0-bit APInt"); + } else { + assert(llvm::isIntN(BitWidth, val) && + "Value is not an N-bit signed value"); + } } else { - assert(llvm::isUIntN(BitWidth, val) && - "Value is not an N-bit unsigned value"); + if (BitWidth == 0) { + assert(val == 0 && "Value must be zero for unsigned 0-bit APInt"); + } else { + assert(llvm::isUIntN(BitWidth, val) && + "Value is not an N-bit unsigned value"); + } } } if (isSingleWord()) { diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index fff29d24a052..95d3ed569ea4 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -3421,6 +3421,7 @@ TEST(APIntTest, ZeroWidth) { EXPECT_EQ(0U, ZW.getBitWidth()); EXPECT_EQ(0U, APInt(0, ArrayRef({0, 1, 2})).getBitWidth()); EXPECT_EQ(0U, APInt(0, "0", 10).getBitWidth()); + EXPECT_EQ(0U, APInt::getAllOnes(0).getBitWidth()); // Default constructor is single bit wide. EXPECT_EQ(1U, APInt().getBitWidth()); From b207a0b8161a9dc1719a0fc7151d4d8938ed9d19 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 17 Oct 2024 08:48:08 +0200 Subject: [PATCH 003/203] [APInt] Fix APInt constructions where value does not fit bitwidth (NFCI) (#80309) This fixes all the places that hit the new assertion added in https://github.com/llvm/llvm-project/pull/106524 in tests. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, set the implicitTrunc flag, or perform more calculations inside APInt. Note that the assertion is currently still disabled by default, so this patch is mostly NFC. --- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/AST/Interp/IntegralAP.h | 7 ++-- clang/lib/CodeGen/CGVTT.cpp | 5 +-- clang/lib/CodeGen/ItaniumCXXABI.cpp | 5 +-- clang/lib/Parse/ParseInit.cpp | 6 ++-- clang/lib/Sema/SemaExpr.cpp | 5 +-- clang/lib/Sema/SemaOpenMP.cpp | 4 ++- lldb/source/Expression/DWARFExpression.cpp | 8 +++-- llvm/include/llvm/ADT/APFixedPoint.h | 4 ++- llvm/lib/Analysis/ConstantFolding.cpp | 3 +- llvm/lib/Analysis/Loads.cpp | 6 ++-- llvm/lib/Analysis/MemoryBuiltins.cpp | 2 ++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 3 +- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 6 +++- .../SelectionDAG/SelectionDAGBuilder.cpp | 3 +- .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 10 ++++-- .../CodeGen/SelectionDAG/TargetLowering.cpp | 8 +++-- llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp | 2 +- llvm/lib/IR/Constants.cpp | 5 ++- .../Target/AArch64/AArch64ISelLowering.cpp | 32 +++++++++---------- llvm/lib/Target/AMDGPU/SIInstrInfo.cpp | 3 +- .../Target/AMDGPU/SIShrinkInstructions.cpp | 4 +-- .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 3 +- .../Hexagon/HexagonConstPropagation.cpp | 3 +- llvm/lib/Target/Hexagon/HexagonGenExtract.cpp | 2 +- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 3 +- llvm/lib/Target/X86/X86ISelLowering.cpp | 6 ++-- llvm/lib/Transforms/IPO/ArgumentPromotion.cpp | 3 +- llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +- llvm/unittests/ADT/APFixedPointTest.cpp | 9 +++--- 30 files changed, 101 insertions(+), 63 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7bfdaaae45a9..fe1927ac8a32 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6792,7 +6792,7 @@ class Sema final : public SemaBase { ExprResult BuildPredefinedExpr(SourceLocation Loc, PredefinedIdentKind IK); ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); - ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); + ExprResult ActOnIntegerConstant(SourceLocation Loc, int64_t Val); bool CheckLoopHintExpr(Expr *E, SourceLocation Loc, bool AllowZero); diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h index b8aa21038256..8f6e9b7bfc0f 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -61,7 +61,7 @@ template class IntegralAP final { IntegralAP(APInt V) : V(V) {} /// Arbitrary value for uninitialized variables. - IntegralAP() : IntegralAP(-1, 3) {} + IntegralAP() : IntegralAP(Signed ? -1 : 7, 3) {} IntegralAP operator-() const { return IntegralAP(-V); } IntegralAP operator-(const IntegralAP &Other) const { @@ -112,7 +112,10 @@ template class IntegralAP final { template static IntegralAP from(Integral I, unsigned BitWidth) { - APInt Copy = APInt(BitWidth, static_cast(I), InputSigned); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + APInt Copy = APInt(BitWidth, static_cast(I), InputSigned, + /*implicitTrunc=*/true); return IntegralAP(Copy); } diff --git a/clang/lib/CodeGen/CGVTT.cpp b/clang/lib/CodeGen/CGVTT.cpp index 20bd2c2fc2c6..989a07d09d50 100644 --- a/clang/lib/CodeGen/CGVTT.cpp +++ b/clang/lib/CodeGen/CGVTT.cpp @@ -85,8 +85,9 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT, cast(VTable->getValueType()) ->getElementType(AddressPoint.VTableIndex)); unsigned Offset = ComponentSize * AddressPoint.AddressPointIndex; - llvm::ConstantRange InRange(llvm::APInt(32, -Offset, true), - llvm::APInt(32, VTableSize - Offset, true)); + llvm::ConstantRange InRange( + llvm::APInt(32, (int)-Offset, true), + llvm::APInt(32, (int)(VTableSize - Offset), true)); llvm::Constant *Init = llvm::ConstantExpr::getGetElementPtr( VTable->getValueType(), VTable, Idxs, /*InBounds=*/true, InRange); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 0be92fb2e275..e9853116ddab 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -2101,8 +2101,9 @@ ItaniumCXXABI::getVTableAddressPoint(BaseSubobject Base, unsigned VTableSize = ComponentSize * Layout.getVTableSize(AddressPoint.VTableIndex); unsigned Offset = ComponentSize * AddressPoint.AddressPointIndex; - llvm::ConstantRange InRange(llvm::APInt(32, -Offset, true), - llvm::APInt(32, VTableSize - Offset, true)); + llvm::ConstantRange InRange( + llvm::APInt(32, (int)-Offset, true), + llvm::APInt(32, (int)(VTableSize - Offset), true)); return llvm::ConstantExpr::getGetElementPtr( VTable->getValueType(), VTable, Indices, /*InBounds=*/true, InRange); } diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index 0a9a359cdaf9..dd59cb23236d 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -436,9 +436,9 @@ ExprResult Parser::createEmbedExpr() { ASTContext &Context = Actions.getASTContext(); SourceLocation StartLoc = ConsumeAnnotationToken(); if (Data->BinaryData.size() == 1) { - Res = IntegerLiteral::Create(Context, - llvm::APInt(CHAR_BIT, Data->BinaryData.back()), - Context.UnsignedCharTy, StartLoc); + Res = IntegerLiteral::Create( + Context, llvm::APInt(CHAR_BIT, (unsigned char)Data->BinaryData.back()), + Context.UnsignedCharTy, StartLoc); } else { auto CreateStringLiteralFromStringRef = [&](StringRef Str, QualType Ty) { llvm::APSInt ArraySize = diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 687b1be94592..b1a2718dee39 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3575,9 +3575,10 @@ ExprResult Sema::ActOnCharacterConstant(const Token &Tok, Scope *UDLScope) { Lit, Tok.getLocation()); } -ExprResult Sema::ActOnIntegerConstant(SourceLocation Loc, uint64_t Val) { +ExprResult Sema::ActOnIntegerConstant(SourceLocation Loc, int64_t Val) { unsigned IntSize = Context.getTargetInfo().getIntWidth(); - return IntegerLiteral::Create(Context, llvm::APInt(IntSize, Val), + return IntegerLiteral::Create(Context, + llvm::APInt(IntSize, Val, /*isSigned=*/true), Context.IntTy, Loc); } diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 6cbc075302eb..9b9115c9f2fe 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -5712,7 +5712,9 @@ StmtResult SemaOpenMP::ActOnOpenMPCanonicalLoop(Stmt *AStmt) { llvm_unreachable("unhandled unary increment operator"); } Step = IntegerLiteral::Create( - Ctx, llvm::APInt(Ctx.getIntWidth(LogicalTy), Direction), LogicalTy, {}); + Ctx, + llvm::APInt(Ctx.getIntWidth(LogicalTy), Direction, /*isSigned=*/true), + LogicalTy, {}); } else if (auto *IncBin = dyn_cast(Inc)) { if (IncBin->getOpcode() == BO_AddAssign) { Step = IncBin->getRHS(); diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 444e44b39289..dc6211d233f4 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -859,10 +859,12 @@ llvm::Expected DWARFExpression::Evaluate( // TODO: Implement a real typed stack, and store the genericness of the value // there. auto to_generic = [&](auto v) { + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. bool is_signed = std::is_signed::value; - return Scalar(llvm::APSInt( - llvm::APInt(8 * opcodes.GetAddressByteSize(), v, is_signed), - !is_signed)); + return Scalar(llvm::APSInt(llvm::APInt(8 * opcodes.GetAddressByteSize(), v, + is_signed, /*implicitTrunc=*/true), + !is_signed)); }; // The default kind is a memory location. This is updated by any diff --git a/llvm/include/llvm/ADT/APFixedPoint.h b/llvm/include/llvm/ADT/APFixedPoint.h index 0c014e76aa71..9c21b1a055b5 100644 --- a/llvm/include/llvm/ADT/APFixedPoint.h +++ b/llvm/include/llvm/ADT/APFixedPoint.h @@ -160,7 +160,9 @@ class APFixedPoint { } APFixedPoint(uint64_t Val, const FixedPointSemantics &Sema) - : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned()), Sema) {} + : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned(), + /*implicitTrunc=*/true), + Sema) {} // Zero initialization. APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {} diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index ff30fece5fce..633ab8b329b0 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -889,7 +889,8 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, APInt Offset = APInt( BitWidth, DL.getIndexedOffsetInType( - SrcElemTy, ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1))); + SrcElemTy, ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1)), + /*isSigned=*/true, /*implicitTrunc=*/true); std::optional InRange = GEP->getInRange(); if (InRange) diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp index 61c6aa5e5a3e..8644c2148ebb 100644 --- a/llvm/lib/Analysis/Loads.cpp +++ b/llvm/lib/Analysis/Loads.cpp @@ -94,10 +94,8 @@ static bool isDereferenceableAndAlignedPointer( } bool CheckForNonNull, CheckForFreed; - APInt KnownDerefBytes(Size.getBitWidth(), - V->getPointerDereferenceableBytes(DL, CheckForNonNull, - CheckForFreed)); - if (KnownDerefBytes.getBoolValue() && KnownDerefBytes.uge(Size) && + if (Size.ule(V->getPointerDereferenceableBytes(DL, CheckForNonNull, + CheckForFreed)) && !CheckForFreed) if (!CheckForNonNull || isKnownNonZero(V, SimplifyQuery(DL, DT, AC, CtxI))) { diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 1edc51e9ce5d..e68effb71091 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -781,6 +781,8 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) { TypeSize ElemSize = DL.getTypeAllocSize(I.getAllocatedType()); if (ElemSize.isScalable() && Options.EvalMode != ObjectSizeOpts::Mode::Min) return ObjectSizeOffsetVisitor::unknown(); + if (!isUIntN(IntTyBits, ElemSize.getKnownMinValue())) + return ObjectSizeOffsetVisitor::unknown(); APInt Size(IntTyBits, ElemSize.getKnownMinValue()); if (!I.isArrayAllocation()) return SizeOffsetAPInt(align(Size, I.getAlign()), Zero); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 84d624f6cf8f..cb6874acfef3 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -858,7 +858,8 @@ class BitcodeReader : public BitcodeReaderBase, public GVMaterializer { } else { int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[OpNum++]); int64_t End = BitcodeReader::decodeSignRotatedValue(Record[OpNum++]); - return ConstantRange(APInt(BitWidth, Start), APInt(BitWidth, End)); + return ConstantRange(APInt(BitWidth, Start, true), + APInt(BitWidth, End, true)); } } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 02d44cd36ae5..c6143820aec9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1627,7 +1627,11 @@ SDValue SelectionDAG::getConstant(uint64_t Val, const SDLoc &DL, EVT VT, assert((EltVT.getSizeInBits() >= 64 || (uint64_t)((int64_t)Val >> EltVT.getSizeInBits()) + 1 < 2) && "getConstant with a uint64_t value that doesn't fit in the type!"); - return getConstant(APInt(EltVT.getSizeInBits(), Val), DL, VT, isT, isO); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + return getConstant(APInt(EltVT.getSizeInBits(), Val, /*isSigned=*/false, + /*implicitTrunc=*/true), + DL, VT, isT, isO); } SDValue SelectionDAG::getConstant(const APInt &Val, const SDLoc &DL, EVT VT, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 7fa3b8a73a41..f92100a74141 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4334,7 +4334,8 @@ void SelectionDAGBuilder::visitGetElementPtr(const User &I) { GTI.getSequentialElementStride(DAG.getDataLayout()); // We intentionally mask away the high bits here; ElementSize may not // fit in IdxTy. - APInt ElementMul(IdxSize, ElementSize.getKnownMinValue()); + APInt ElementMul(IdxSize, ElementSize.getKnownMinValue(), + /*isSigned=*/false, /*implicitTrunc=*/true); bool ElementScalable = ElementSize.isScalable(); // If this is a scalar constant or a splat vector of constants, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index b961d3bb1fec..d7260fc4cbf9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -2166,7 +2166,10 @@ ScheduleDAGSDNodes *SelectionDAGISel::CreateScheduler() { bool SelectionDAGISel::CheckAndMask(SDValue LHS, ConstantSDNode *RHS, int64_t DesiredMaskS) const { const APInt &ActualMask = RHS->getAPIntValue(); - const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS, + /*isSigned=*/false, /*implicitTrunc=*/true); // If the actual mask exactly matches, success! if (ActualMask == DesiredMask) @@ -2195,7 +2198,10 @@ bool SelectionDAGISel::CheckAndMask(SDValue LHS, ConstantSDNode *RHS, bool SelectionDAGISel::CheckOrMask(SDValue LHS, ConstantSDNode *RHS, int64_t DesiredMaskS) const { const APInt &ActualMask = RHS->getAPIntValue(); - const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + const APInt &DesiredMask = APInt(LHS.getValueSizeInBits(), DesiredMaskS, + /*isSigned=*/false, /*implicitTrunc=*/true); // If the actual mask exactly matches, success! if (ActualMask == DesiredMask) diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 140c97ccd90b..2cb79a1446c8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -6789,7 +6789,9 @@ TargetLowering::prepareUREMEqFold(EVT SETCCVT, SDValue REMNode, PAmts.push_back(DAG.getConstant(P, DL, SVT)); KAmts.push_back( - DAG.getConstant(APInt(ShSVT.getSizeInBits(), K), DL, ShSVT)); + DAG.getConstant(APInt(ShSVT.getSizeInBits(), K, /*isSigned=*/false, + /*implicitTrunc=*/true), + DL, ShSVT)); QAmts.push_back(DAG.getConstant(Q, DL, SVT)); return true; }; @@ -7060,7 +7062,9 @@ TargetLowering::prepareSREMEqFold(EVT SETCCVT, SDValue REMNode, PAmts.push_back(DAG.getConstant(P, DL, SVT)); AAmts.push_back(DAG.getConstant(A, DL, SVT)); KAmts.push_back( - DAG.getConstant(APInt(ShSVT.getSizeInBits(), K), DL, ShSVT)); + DAG.getConstant(APInt(ShSVT.getSizeInBits(), K, /*isSigned=*/false, + /*implicitTrunc=*/true), + DL, ShSVT)); QAmts.push_back(DAG.getConstant(Q, DL, SVT)); return true; }; diff --git a/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp index 869b383dd064..f8f344ec38f5 100644 --- a/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ b/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -590,7 +590,7 @@ GenericValue MCJIT::runFunction(Function *F, ArrayRef ArgValues) { return rv; } case Type::VoidTyID: - rv.IntVal = APInt(32, ((int(*)())(intptr_t)FPtr)()); + rv.IntVal = APInt(32, ((int (*)())(intptr_t)FPtr)(), true); return rv; case Type::FloatTyID: rv.FloatVal = ((float(*)())(intptr_t)FPtr)(); diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index 70803c153d8c..510e15f348f8 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -933,7 +933,10 @@ Constant *ConstantInt::get(Type *Ty, uint64_t V, bool isSigned) { } ConstantInt *ConstantInt::get(IntegerType *Ty, uint64_t V, bool isSigned) { - return get(Ty->getContext(), APInt(Ty->getBitWidth(), V, isSigned)); + // TODO: Avoid implicit trunc? + // See https://github.com/llvm/llvm-project/issues/112510. + return get(Ty->getContext(), + APInt(Ty->getBitWidth(), V, isSigned, /*implicitTrunc=*/true)); } Constant *ConstantInt::get(Type *Ty, const APInt& V) { diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index ef2789e96213..9c57d8895ab5 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -2329,10 +2329,11 @@ void AArch64TargetLowering::computeKnownBitsForTargetNode( } case AArch64ISD::BICi: { // Compute the bit cleared value. - uint64_t Mask = - ~(Op->getConstantOperandVal(1) << Op->getConstantOperandVal(2)); + APInt Mask = + ~(Op->getConstantOperandAPInt(1) << Op->getConstantOperandAPInt(2)) + .trunc(Known.getBitWidth()); Known = DAG.computeKnownBits(Op->getOperand(0), Depth + 1); - Known &= KnownBits::makeConstant(APInt(Known.getBitWidth(), Mask)); + Known &= KnownBits::makeConstant(Mask); break; } case AArch64ISD::VLSHR: { @@ -12504,7 +12505,8 @@ static bool isEXTMask(ArrayRef M, EVT VT, bool &ReverseEXT, // Benefit form APInt to handle overflow when calculating expected element. unsigned NumElts = VT.getVectorNumElements(); unsigned MaskBits = APInt(32, NumElts * 2).logBase2(); - APInt ExpectedElt = APInt(MaskBits, *FirstRealElt + 1); + APInt ExpectedElt = APInt(MaskBits, *FirstRealElt + 1, /*isSigned=*/false, + /*implicitTrunc=*/true); // The following shuffle indices must be the successive elements after the // first real element. bool FoundWrongElt = std::any_of(FirstRealElt + 1, M.end(), [&](int Elt) { @@ -13851,9 +13853,9 @@ static SDValue NormalizeBuildVector(SDValue Op, // (with operands cast to integers), then the only possibilities // are constants and UNDEFs. if (auto *CstLane = dyn_cast(Lane)) { - APInt LowBits(EltTy.getSizeInBits(), - CstLane->getZExtValue()); - Lane = DAG.getConstant(LowBits.getZExtValue(), dl, MVT::i32); + Lane = DAG.getConstant( + CstLane->getAPIntValue().trunc(EltTy.getSizeInBits()).getZExtValue(), + dl, MVT::i32); } else if (Lane.getNode()->isUndef()) { Lane = DAG.getUNDEF(MVT::i32); } else { @@ -23140,7 +23142,7 @@ static bool findMoreOptimalIndexType(const MaskedGatherScatterSDNode *N, EVT NewIndexVT = IndexVT.changeVectorElementType(MVT::i32); // Stride does not scale explicitly by 'Scale', because it happens in // the gather/scatter addressing mode. - Index = DAG.getStepVector(SDLoc(N), NewIndexVT, APInt(32, Stride)); + Index = DAG.getStepVector(SDLoc(N), NewIndexVT, APInt(32, Stride, true)); return true; } @@ -28084,7 +28086,7 @@ static SDValue GenerateFixedLengthSVETBL(SDValue Op, SDValue Op1, SDValue Op2, unsigned BitsPerElt = VTOp1.getVectorElementType().getSizeInBits(); unsigned IndexLen = MinSVESize / BitsPerElt; unsigned ElementsPerVectorReg = VTOp1.getVectorNumElements(); - uint64_t MaxOffset = APInt(BitsPerElt, -1, false).getZExtValue(); + uint64_t MaxOffset = maxUIntN(BitsPerElt); EVT MaskEltType = VTOp1.getVectorElementType().changeTypeToInteger(); EVT MaskType = EVT::getVectorVT(*DAG.getContext(), MaskEltType, IndexLen); bool MinMaxEqual = (MinSVESize == MaxSVESize); @@ -28427,16 +28429,14 @@ bool AArch64TargetLowering::SimplifyDemandedBitsForTargetNode( KnownBits KnownOp0 = TLO.DAG.computeKnownBits(Op0, OriginalDemandedElts, Depth + 1); // Op0 &= ~(ConstantOperandVal(1) << ConstantOperandVal(2)) - uint64_t BitsToClear = Op->getConstantOperandVal(1) - << Op->getConstantOperandVal(2); + APInt BitsToClear = + (Op->getConstantOperandAPInt(1) << Op->getConstantOperandAPInt(2)) + .trunc(KnownOp0.getBitWidth()); APInt AlreadyZeroedBitsToClear = BitsToClear & KnownOp0.Zero; - if (APInt(Known.getBitWidth(), BitsToClear) - .isSubsetOf(AlreadyZeroedBitsToClear)) + if (BitsToClear.isSubsetOf(AlreadyZeroedBitsToClear)) return TLO.CombineTo(Op, Op0); - Known = KnownOp0 & - KnownBits::makeConstant(APInt(Known.getBitWidth(), ~BitsToClear)); - + Known = KnownOp0 & KnownBits::makeConstant(~BitsToClear); return false; } case ISD::INTRINSIC_WO_CHAIN: { diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp index 27b8c1b17422..d781354bce16 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp @@ -3461,7 +3461,8 @@ bool SIInstrInfo::foldImmediate(MachineInstr &UseMI, MachineInstr &DefMI, : AMDGPU::V_MOV_B32_e32 : Is64Bit ? AMDGPU::S_MOV_B64_IMM_PSEUDO : AMDGPU::S_MOV_B32; - APInt Imm(Is64Bit ? 64 : 32, getImmFor(UseMI.getOperand(1))); + APInt Imm(Is64Bit ? 64 : 32, getImmFor(UseMI.getOperand(1)), + /*isSigned=*/true, /*implicitTrunc=*/true); if (RI.isAGPR(*MRI, DstReg)) { if (Is64Bit || !isInlineConstant(Imm)) diff --git a/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp b/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp index 79bcf5e8cd30..610226cdd49b 100644 --- a/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp +++ b/llvm/lib/Target/AMDGPU/SIShrinkInstructions.cpp @@ -203,12 +203,12 @@ static unsigned canModifyToInlineImmOp32(const SIInstrInfo *TII, // that SCC is not live as S_NOT_B32 clobbers it. It's probably not worth // it, as the reasonable values are already covered by s_movk_i32. ModifiedImm = ~SrcImm; - if (TII->isInlineConstant(APInt(32, ModifiedImm))) + if (TII->isInlineConstant(APInt(32, ModifiedImm, true))) return AMDGPU::V_NOT_B32_e32; } ModifiedImm = reverseBits(SrcImm); - if (TII->isInlineConstant(APInt(32, ModifiedImm))) + if (TII->isInlineConstant(APInt(32, ModifiedImm, true))) return Scalar ? AMDGPU::S_BREV_B32 : AMDGPU::V_BFREV_B32_e32; return 0; diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index e54314cc7d00..aadbdeb88344 100644 --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -1158,7 +1158,8 @@ class ARMOperand : public MCParsedAsmOperand { bool isFPImm() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); - if (!CE) return false; + if (!CE || !isUInt<32>(CE->getValue())) + return false; int Val = ARM_AM::getFP32Imm(APInt(32, CE->getValue())); return Val != -1; } diff --git a/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp b/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp index dae316ccb5e9..f68444c0b8d4 100644 --- a/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp +++ b/llvm/lib/Target/Hexagon/HexagonConstPropagation.cpp @@ -2503,7 +2503,8 @@ APInt HexagonConstEvaluator::getCmpImm(unsigned Opc, unsigned OpX, } uint64_t Val = MO.getImm(); - return APInt(32, Val, Signed); + // TODO: Is implicitTrunc correct here? + return APInt(32, Val, Signed, /*implicitTrunc=*/true); } void HexagonConstEvaluator::replaceWithNop(MachineInstr &MI) { diff --git a/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp b/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp index 3274f9162b54..2ed5150ec64f 100644 --- a/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp +++ b/llvm/lib/Target/Hexagon/HexagonGenExtract.cpp @@ -171,7 +171,7 @@ bool HexagonGenExtract::convert(Instruction *In) { // this value. if (!LogicalSR && (SR > SL)) return false; - APInt A = APInt(BW, ~0ULL).lshr(SR).shl(SL); + APInt A = APInt(BW, ~0ULL, true).lshr(SR).shl(SL); CM = ConstantInt::get(Ctx, A); } diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 823fb428472e..752e58493e9a 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -3452,7 +3452,8 @@ static std::optional isSimpleVIDSequence(SDValue Op, if (!Elt) continue; uint64_t ExpectedVal = - (int64_t)(Idx * (uint64_t)*SeqStepNum) / *SeqStepDenom; + (APInt(EltSizeInBits, Idx, /*isSigned=*/false, /*implicitTrunc=*/true) * + *SeqStepNum) / *SeqStepDenom; int64_t Addend = SignExtend64(*Elt - ExpectedVal, EltSizeInBits); if (!SeqAddend) SeqAddend = Addend; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 4e3a181f9a3a..62e4d6399497 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -52179,8 +52179,8 @@ static SDValue combineFMulcFCMulc(SDNode *N, SelectionDAG &DAG, if (XOR->getOpcode() == ISD::XOR && XOR.hasOneUse()) { KnownBits XORRHS = DAG.computeKnownBits(XOR.getOperand(1)); if (XORRHS.isConstant()) { - APInt ConjugationInt32 = APInt(32, 0x80000000, true); - APInt ConjugationInt64 = APInt(64, 0x8000000080000000ULL, true); + APInt ConjugationInt32 = APInt(32, 0x80000000); + APInt ConjugationInt64 = APInt(64, 0x8000000080000000ULL); if ((XORRHS.getBitWidth() == 32 && XORRHS.getConstant() == ConjugationInt32) || (XORRHS.getBitWidth() == 64 && @@ -52219,7 +52219,7 @@ static SDValue combineFaddCFmul(SDNode *N, SelectionDAG &DAG, Flags.hasNoSignedZeros(); }; auto IsVectorAllNegativeZero = [&DAG](SDValue Op) { - APInt AI = APInt(32, 0x80008000, true); + APInt AI = APInt(32, 0x80008000); KnownBits Bits = DAG.computeKnownBits(Op); return Bits.getBitWidth() == 32 && Bits.isConstant() && Bits.getConstant() == AI; diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index 99ec50aa4775..bac713021a99 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -100,7 +100,8 @@ using OffsetAndArgPart = std::pair; static Value *createByteGEP(IRBuilderBase &IRB, const DataLayout &DL, Value *Ptr, Type *ResElemTy, int64_t Offset) { if (Offset != 0) { - APInt APOffset(DL.getIndexTypeSizeInBits(Ptr->getType()), Offset); + APInt APOffset(DL.getIndexTypeSizeInBits(Ptr->getType()), Offset, + /*isSigned=*/true); Ptr = IRB.CreatePtrAdd(Ptr, IRB.getInt(APOffset)); } return Ptr; diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index f23e28888931..dd82832e2b80 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7026,7 +7026,7 @@ static bool ReduceSwitchRange(SwitchInst *SI, IRBuilder<> &Builder, for (auto Case : SI->cases()) { auto *Orig = Case.getCaseValue(); - auto Sub = Orig->getValue() - APInt(Ty->getBitWidth(), Base); + auto Sub = Orig->getValue() - APInt(Ty->getBitWidth(), Base, true); Case.setValue(cast(ConstantInt::get(Ty, Sub.lshr(Shift)))); } return true; diff --git a/llvm/unittests/ADT/APFixedPointTest.cpp b/llvm/unittests/ADT/APFixedPointTest.cpp index ecb89fbf76c8..a0f43fdfcd7e 100644 --- a/llvm/unittests/ADT/APFixedPointTest.cpp +++ b/llvm/unittests/ADT/APFixedPointTest.cpp @@ -240,19 +240,20 @@ void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) { APFixedPoint ValWithFract( APInt(Sema.getWidth(), relativeShr(IntPart, Sema.getLsbWeight()) + FullFactPart, - Sema.isSigned()), + Sema.isSigned(), /*implicitTrunc=*/true), Sema); ASSERT_EQ(ValWithFract.getIntPart(), IntPart); // Just fraction - APFixedPoint JustFract(APInt(Sema.getWidth(), FullFactPart, Sema.isSigned()), + APFixedPoint JustFract(APInt(Sema.getWidth(), FullFactPart, Sema.isSigned(), + /*implicitTrunc=*/true), Sema); ASSERT_EQ(JustFract.getIntPart(), 0); // Whole number APFixedPoint WholeNum(APInt(Sema.getWidth(), relativeShr(IntPart, Sema.getLsbWeight()), - Sema.isSigned()), + Sema.isSigned(), /*implicitTrunc=*/true), Sema); ASSERT_EQ(WholeNum.getIntPart(), IntPart); @@ -260,7 +261,7 @@ void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) { if (Sema.isSigned()) { APFixedPoint Negative(APInt(Sema.getWidth(), relativeShr(IntPart, Sema.getLsbWeight()), - Sema.isSigned()), + Sema.isSigned(), /*implicitTrunc=*/true), Sema); ASSERT_EQ(Negative.getIntPart(), IntPart); } From c32484d39ecbb4228dd834207b9fa2e086e35270 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 2 Aug 2024 13:43:02 +0200 Subject: [PATCH 004/203] [SCEV] Handle more adds in computeConstantDifference() (#101339) Currently it only deals with the case where we're subtracting adds with at most one non-constant operand. This patch extends it to cancel out common operands for the subtraction of arbitrary add expressions. The background here is that I want to replace a getMinusSCEV() call in LAA with computeConstantDifference(): https://github.com/llvm/llvm-project/blob/93fecc2577ece0329f3bbe2719bbc5b4b9b30010/llvm/lib/Analysis/LoopAccessAnalysis.cpp#L1602-L1603 This particular call is very expensive in some cases (e.g. lencod with LTO) and computeConstantDifference() could achieve this much more cheaply, because it does not need to construct new SCEV expressions. However, the current computeConstantDifference() implementation is too weak for this and misses many basic cases. This is a step towards making it more powerful while still keeping it pretty fast. --- llvm/lib/Analysis/ScalarEvolution.cpp | 51 +++++++++---------- .../Analysis/ScalarEvolutionTest.cpp | 8 ++- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 412cfe73d3e5..ed7c4fb49e9d 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -11947,8 +11947,9 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) { // fairly deep in the call stack (i.e. is called many times). // X - X = 0. + unsigned BW = getTypeSizeInBits(More->getType()); if (More == Less) - return APInt(getTypeSizeInBits(More->getType()), 0); + return APInt(BW, 0); if (isa(Less) && isa(More)) { const auto *LAR = cast(Less); @@ -11971,33 +11972,31 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) { // fall through } - if (isa(Less) && isa(More)) { - const auto &M = cast(More)->getAPInt(); - const auto &L = cast(Less)->getAPInt(); - return M - L; - } - - SCEV::NoWrapFlags Flags; - const SCEV *LLess = nullptr, *RLess = nullptr; - const SCEV *LMore = nullptr, *RMore = nullptr; - const SCEVConstant *C1 = nullptr, *C2 = nullptr; - // Compare (X + C1) vs X. - if (splitBinaryAdd(Less, LLess, RLess, Flags)) - if ((C1 = dyn_cast(LLess))) - if (RLess == More) - return -(C1->getAPInt()); - - // Compare X vs (X + C2). - if (splitBinaryAdd(More, LMore, RMore, Flags)) - if ((C2 = dyn_cast(LMore))) - if (RMore == Less) - return C2->getAPInt(); + // Try to cancel out common factors in two add expressions. + SmallDenseMap Multiplicity; + APInt Diff(BW, 0); + auto Add = [&](const SCEV *S, int Mul) { + if (auto *C = dyn_cast(S)) + Diff += C->getAPInt() * Mul; + else + Multiplicity[S] += Mul; + }; + auto Decompose = [&](const SCEV *S, int Mul) { + if (isa(S)) { + for (const SCEV *Op : S->operands()) + Add(Op, Mul); + } else + Add(S, Mul); + }; + Decompose(More, 1); + Decompose(Less, -1); - // Compare (X + C1) vs (X + C2). - if (C1 && C2 && RLess == RMore) - return C2->getAPInt() - C1->getAPInt(); + // Check whether all the non-constants cancel out. + for (const auto [_, Mul] : Multiplicity) + if (Mul != 0) + return std::nullopt; - return std::nullopt; + return Diff; } bool ScalarEvolution::isImpliedCondOperandsViaAddRecStart( diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp index a6a5ffda3cb7..93b53c466818 100644 --- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp @@ -1117,10 +1117,12 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { LLVMContext C; SMDiagnostic Err; std::unique_ptr M = parseAssemblyString( - "define void @foo(i32 %sz, i32 %pp) { " + "define void @foo(i32 %sz, i32 %pp, i32 %x) { " "entry: " " %v0 = add i32 %pp, 0 " " %v3 = add i32 %pp, 3 " + " %vx = add i32 %pp, %x " + " %vx3 = add i32 %vx, 3 " " br label %loop.body " "loop.body: " " %iv = phi i32 [ %iv.next, %loop.body ], [ 0, %entry ] " @@ -1141,6 +1143,9 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) { auto *ScevV0 = SE.getSCEV(getInstructionByName(F, "v0")); // %pp auto *ScevV3 = SE.getSCEV(getInstructionByName(F, "v3")); // (3 + %pp) + auto *ScevVX = SE.getSCEV(getInstructionByName(F, "vx")); // (%pp + %x) + // (%pp + %x + 3) + auto *ScevVX3 = SE.getSCEV(getInstructionByName(F, "vx3")); auto *ScevIV = SE.getSCEV(getInstructionByName(F, "iv")); // {0,+,1} auto *ScevXA = SE.getSCEV(getInstructionByName(F, "xa")); // {%pp,+,1} auto *ScevYY = SE.getSCEV(getInstructionByName(F, "yy")); // {(3 + %pp),+,1} @@ -1162,6 +1167,7 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { EXPECT_EQ(diff(ScevV0, ScevV3), -3); EXPECT_EQ(diff(ScevV0, ScevV0), 0); EXPECT_EQ(diff(ScevV3, ScevV3), 0); + EXPECT_EQ(diff(ScevVX3, ScevVX), 3); EXPECT_EQ(diff(ScevIV, ScevIV), 0); EXPECT_EQ(diff(ScevXA, ScevXB), 0); EXPECT_EQ(diff(ScevXA, ScevYY), -3); From 50fcf1e9ba41b560dad05fb594fb3590b38502e6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 2 Aug 2024 14:48:02 +0200 Subject: [PATCH 005/203] [SCEV] Fix warning (NFC) Produces -Wrange-loop-construct on some buildbots. --- llvm/lib/Analysis/ScalarEvolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index ed7c4fb49e9d..eef10a7b7267 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -11992,7 +11992,7 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) { Decompose(Less, -1); // Check whether all the non-constants cancel out. - for (const auto [_, Mul] : Multiplicity) + for (const auto &[_, Mul] : Multiplicity) if (Mul != 0) return std::nullopt; From f149d74db2a96ad2da94aa03039e1a38188fc980 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 15:03:54 +0200 Subject: [PATCH 006/203] [IndVars] Add test for #102597 (NFC) --- .../Transforms/IndVarSimplify/pr102597.ll | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 llvm/test/Transforms/IndVarSimplify/pr102597.ll diff --git a/llvm/test/Transforms/IndVarSimplify/pr102597.ll b/llvm/test/Transforms/IndVarSimplify/pr102597.ll new file mode 100644 index 000000000000..2bb00da0aaf1 --- /dev/null +++ b/llvm/test/Transforms/IndVarSimplify/pr102597.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=indvars < %s | FileCheck %s + +; FIXME: This is a miscompile. +define void @test() { +; CHECK-LABEL: define void @test() { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[IV:%.*]] = phi i128 [ 3, %[[ENTRY]] ], [ [[IV_DEC:%.*]], %[[LOOP_LATCH:.*]] ] +; CHECK-NEXT: br i1 true, label %[[LOOP_LATCH]], label %[[IF:.*]] +; CHECK: [[IF]]: +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: br label %[[LOOP_LATCH]] +; CHECK: [[LOOP_LATCH]]: +; CHECK-NEXT: [[IV_DEC]] = add nsw i128 [[IV]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i128 [[IV]], 0 +; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: + %iv = phi i128 [ 3, %entry ], [ %iv.dec, %loop.latch ] + %tobool = icmp ne i128 %iv, 0 + br i1 %tobool, label %loop.latch, label %if + +if: + call void @foo() + br label %loop.latch + +loop.latch: + %iv.dec = add nsw i128 %iv, -1 + %cmp = icmp sgt i128 %iv, 0 + br i1 %cmp, label %loop, label %exit + +exit: + ret void +} + +declare void @foo() From 6d0178995dd48e5e36f755ddc5f4a81691fd7bf6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 15:18:32 +0200 Subject: [PATCH 007/203] [SCEV] Fix incorrect extension in computeConstantDifference() The Mul factor was zero-extended here, resulting in incorrect results for integers larger than 64-bit. As we currently only multiply by 1 or -1, just split this into two cases -- there's no need for a full multiplication here. Fixes https://github.com/llvm/llvm-project/issues/102597. --- llvm/lib/Analysis/ScalarEvolution.cpp | 11 ++++++++--- llvm/test/Transforms/IndVarSimplify/pr102597.ll | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index eef10a7b7267..c57e12336256 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -11976,9 +11976,14 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) { SmallDenseMap Multiplicity; APInt Diff(BW, 0); auto Add = [&](const SCEV *S, int Mul) { - if (auto *C = dyn_cast(S)) - Diff += C->getAPInt() * Mul; - else + if (auto *C = dyn_cast(S)) { + if (Mul == 1) { + Diff += C->getAPInt(); + } else { + assert(Mul == -1); + Diff -= C->getAPInt(); + } + } else Multiplicity[S] += Mul; }; auto Decompose = [&](const SCEV *S, int Mul) { diff --git a/llvm/test/Transforms/IndVarSimplify/pr102597.ll b/llvm/test/Transforms/IndVarSimplify/pr102597.ll index 2bb00da0aaf1..9de614524444 100644 --- a/llvm/test/Transforms/IndVarSimplify/pr102597.ll +++ b/llvm/test/Transforms/IndVarSimplify/pr102597.ll @@ -1,14 +1,15 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -S -passes=indvars < %s | FileCheck %s -; FIXME: This is a miscompile. +; The %tobool condition should not be optimized away. define void @test() { ; CHECK-LABEL: define void @test() { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV:%.*]] = phi i128 [ 3, %[[ENTRY]] ], [ [[IV_DEC:%.*]], %[[LOOP_LATCH:.*]] ] -; CHECK-NEXT: br i1 true, label %[[LOOP_LATCH]], label %[[IF:.*]] +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i128 [[IV]], 0 +; CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOOP_LATCH]], label %[[IF:.*]] ; CHECK: [[IF]]: ; CHECK-NEXT: call void @foo() ; CHECK-NEXT: br label %[[LOOP_LATCH]] From d1cbfdeebe49fcbbeefaef8db6af24f176cfcdc4 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 16:23:16 +0200 Subject: [PATCH 008/203] [OMPIRBuilder] Use getAllOnesValue() Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp index ab2482b91213..89509d2d8e7a 100644 --- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp +++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp @@ -5905,7 +5905,7 @@ CallInst *OpenMPIRBuilder::createOMPInteropInit( Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize); Value *ThreadId = getOrCreateThreadID(Ident); if (Device == nullptr) - Device = ConstantInt::get(Int32, -1); + Device = Constant::getAllOnesValue(Int32); Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType); if (NumDependences == nullptr) { NumDependences = ConstantInt::get(Int32, 0); @@ -5933,7 +5933,7 @@ CallInst *OpenMPIRBuilder::createOMPInteropDestroy( Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize); Value *ThreadId = getOrCreateThreadID(Ident); if (Device == nullptr) - Device = ConstantInt::get(Int32, -1); + Device = Constant::getAllOnesValue(Int32); if (NumDependences == nullptr) { NumDependences = ConstantInt::get(Int32, 0); PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext()); @@ -5961,7 +5961,7 @@ CallInst *OpenMPIRBuilder::createOMPInteropUse(const LocationDescription &Loc, Value *Ident = getOrCreateIdent(SrcLocStr, SrcLocStrSize); Value *ThreadId = getOrCreateThreadID(Ident); if (Device == nullptr) - Device = ConstantInt::get(Int32, -1); + Device = Constant::getAllOnesValue(Int32); if (NumDependences == nullptr) { NumDependences = ConstantInt::get(Int32, 0); PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext()); @@ -6098,7 +6098,7 @@ OpenMPIRBuilder::createTargetInit(const LocationDescription &Loc, bool IsSPMD, Builder.CreateCall(Fn, {KernelEnvironment, KernelLaunchEnvironment}); Value *ExecUserCode = Builder.CreateICmpEQ( - ThreadKind, ConstantInt::get(ThreadKind->getType(), -1), + ThreadKind, Constant::getAllOnesValue(ThreadKind->getType()), "exec_user_code"); // ThreadKind = __kmpc_target_init(...) From 7f67e4c2bed853c7f01d15589f5ce7ac7b954670 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 16:26:28 +0200 Subject: [PATCH 009/203] [ConstantFolding] Use getSigned() Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Analysis/ConstantFolding.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 633ab8b329b0..69e1985c0c2b 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -3403,8 +3403,9 @@ ConstantFoldScalarFrexpCall(Constant *Op, Type *IntTy) { // The exponent is an "unspecified value" for inf/nan. We use zero to avoid // using undef. - Constant *Result1 = FrexpMant.isFinite() ? ConstantInt::get(IntTy, FrexpExp) - : ConstantInt::getNullValue(IntTy); + Constant *Result1 = FrexpMant.isFinite() + ? ConstantInt::getSigned(IntTy, FrexpExp) + : ConstantInt::getNullValue(IntTy); return {Result0, Result1}; } From 99b6705a476cc7166610fc9486d625943858e654 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 16:28:55 +0200 Subject: [PATCH 010/203] [CGP] Use getAllOnesValue() Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/CodeGen/CodeGenPrepare.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp index 22d0708f5478..1fb37fb8406e 100644 --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -1643,7 +1643,7 @@ static bool matchUAddWithOverflowConstantEdgeCases(CmpInst *Cmp, if (Pred == ICmpInst::ICMP_EQ && match(B, m_AllOnes())) B = ConstantInt::get(B->getType(), 1); else if (Pred == ICmpInst::ICMP_NE && match(B, m_ZeroInt())) - B = ConstantInt::get(B->getType(), -1); + B = Constant::getAllOnesValue(B->getType()); else return false; From e99d343aab8f0474154125b9a494d1f97245ead6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 16:30:48 +0200 Subject: [PATCH 011/203] [ExpandMemCmp] Use getAllOnesValue() Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/CodeGen/ExpandMemCmp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/ExpandMemCmp.cpp b/llvm/lib/CodeGen/ExpandMemCmp.cpp index 2758f7be4d50..04222d5b4afd 100644 --- a/llvm/lib/CodeGen/ExpandMemCmp.cpp +++ b/llvm/lib/CodeGen/ExpandMemCmp.cpp @@ -590,7 +590,7 @@ void MemCmpExpansion::emitMemCmpResultBlock() { ResBlock.PhiSrc2); Value *Res = - Builder.CreateSelect(Cmp, ConstantInt::get(Builder.getInt32Ty(), -1), + Builder.CreateSelect(Cmp, Constant::getAllOnesValue(Builder.getInt32Ty()), ConstantInt::get(Builder.getInt32Ty(), 1)); PhiRes->addIncoming(Res, ResBlock.BB); From d607f58abb878da826db43642d1383e74c4f807e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 12 Aug 2024 16:32:20 +0200 Subject: [PATCH 012/203] [ConstraintElimination] Use getAllOnesValue() Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Transforms/Scalar/ConstraintElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index d1c80aa67124..5bb4dc8c9e23 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -875,7 +875,7 @@ void ConstraintInfo::transferToOtherSystem( addFact(CmpInst::ICMP_ULT, A, B, NumIn, NumOut, DFSInStack); break; case CmpInst::ICMP_SGT: { - if (doesHold(CmpInst::ICMP_SGE, B, ConstantInt::get(B->getType(), -1))) + if (doesHold(CmpInst::ICMP_SGE, B, Constant::getAllOnesValue(B->getType()))) addFact(CmpInst::ICMP_UGE, A, ConstantInt::get(B->getType(), 0), NumIn, NumOut, DFSInStack); if (IsKnownNonNegative(B)) From 76686ed18cfd84f1726261d93eda4f581435feab Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 13 Aug 2024 14:53:54 +0200 Subject: [PATCH 013/203] [MemoryBuiltins] Use getAllOnesValue() Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Analysis/MemoryBuiltins.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index e68effb71091..50b8a774cb30 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -675,7 +675,8 @@ Value *llvm::lowerObjectSizeCall( if (!MustSucceed) return nullptr; - return ConstantInt::get(ResultType, MaxVal ? -1ULL : 0); + return MaxVal ? Constant::getAllOnesValue(ResultType) + : Constant::getNullValue(ResultType); } STATISTIC(ObjectVisitorArgument, From 93c6ad1d16375a9baa235cd46851d7f0063d36e7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 13 Aug 2024 14:57:01 +0200 Subject: [PATCH 014/203] [IndVars] Use getSigned() in FP transform This transform is working on signed integer, so this is the logically correct API. Split off from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp index 5e2131b0b180..ed7e76e9c10e 100644 --- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -357,19 +357,19 @@ bool IndVarSimplify::handleFloatingPointIV(Loop *L, PHINode *PN) { // Insert new integer induction variable. PHINode *NewPHI = PHINode::Create(Int32Ty, 2, PN->getName() + ".int", PN->getIterator()); - NewPHI->addIncoming(ConstantInt::get(Int32Ty, InitValue), + NewPHI->addIncoming(ConstantInt::getSigned(Int32Ty, InitValue), PN->getIncomingBlock(IncomingEdge)); NewPHI->setDebugLoc(PN->getDebugLoc()); - Instruction *NewAdd = - BinaryOperator::CreateAdd(NewPHI, ConstantInt::get(Int32Ty, IncValue), - Incr->getName() + ".int", Incr->getIterator()); + Instruction *NewAdd = BinaryOperator::CreateAdd( + NewPHI, ConstantInt::getSigned(Int32Ty, IncValue), + Incr->getName() + ".int", Incr->getIterator()); NewAdd->setDebugLoc(Incr->getDebugLoc()); NewPHI->addIncoming(NewAdd, PN->getIncomingBlock(BackEdge)); - ICmpInst *NewCompare = - new ICmpInst(TheBr->getIterator(), NewPred, NewAdd, - ConstantInt::get(Int32Ty, ExitValue), Compare->getName()); + ICmpInst *NewCompare = new ICmpInst( + TheBr->getIterator(), NewPred, NewAdd, + ConstantInt::getSigned(Int32Ty, ExitValue), Compare->getName()); NewCompare->setDebugLoc(Compare->getDebugLoc()); // In the following deletions, PN may become dead and may be deleted. From 1bc925f359cdb84090bbaa57545cc0aa77854f4b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 13 Aug 2024 15:00:32 +0200 Subject: [PATCH 015/203] [InstCombine] Use getAllOnesValue() Split off from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index abadf54a9676..626903bf1b94 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -312,7 +312,7 @@ Instruction *InstCombinerImpl::foldCmpLoadFromIndexedGlobal( DL.getTypeAllocSize(Init->getType()->getArrayElementType()); auto MaskIdx = [&](Value *Idx) { if (!GEP->isInBounds() && llvm::countr_zero(ElementSize) != 0) { - Value *Mask = ConstantInt::get(Idx->getType(), -1); + Value *Mask = Constant::getAllOnesValue(Idx->getType()); Mask = Builder.CreateLShr(Mask, llvm::countr_zero(ElementSize)); Idx = Builder.CreateAnd(Idx, Mask); } From 4889b9e60e836e5359748272d521202034c9a59d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 13 Aug 2024 14:55:35 +0200 Subject: [PATCH 016/203] [InstCombine] Use APInt::getSplat() Split off from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 3223fccbcf49..ed981989fbc2 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -260,13 +260,11 @@ Instruction *InstCombinerImpl::SimplifyAnyMemSet(AnyMemSetInst *MI) { // memset(s,c,n) -> store s, c (for n=1,2,4,8) if (Len <= 8 && isPowerOf2_32((uint32_t)Len)) { - Type *ITy = IntegerType::get(MI->getContext(), Len*8); // n=1 -> i8. - Value *Dest = MI->getDest(); // Extract the fill value and store. - const uint64_t Fill = FillC->getZExtValue()*0x0101010101010101ULL; - Constant *FillVal = ConstantInt::get(ITy, Fill); + Constant *FillVal = ConstantInt::get( + MI->getContext(), APInt::getSplat(Len * 8, FillC->getValue())); StoreInst *S = Builder.CreateStore(FillVal, Dest, MI->isVolatile()); S->copyMetadata(*MI, LLVMContext::MD_DIAssignID); auto replaceOpForAssignmentMarkers = [FillC, FillVal](auto *DbgAssign) { From c816f9cbdbc2ac5514c8e75ebd58bbd180b76d8f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 16 Aug 2024 15:00:08 +0200 Subject: [PATCH 017/203] [InstCombine] Add i128 test for select of lshr/ashr transform (NFC) --- llvm/test/Transforms/InstCombine/ashr-lshr.ll | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/ashr-lshr.ll b/llvm/test/Transforms/InstCombine/ashr-lshr.ll index c2a4f3541267..4a3f836e95c3 100644 --- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll +++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll @@ -61,6 +61,21 @@ define i32 @ashr_lshr2(i32 %x, i32 %y) { ret i32 %ret } +define i128 @ashr_lshr2_i128(i128 %x, i128 %y) { +; CHECK-LABEL: @ashr_lshr2_i128( +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i128 [[X:%.*]], 5 +; CHECK-NEXT: [[L:%.*]] = lshr i128 [[X]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = ashr exact i128 [[X]], [[Y]] +; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i128 [[L]], i128 [[R]] +; CHECK-NEXT: ret i128 [[RET]] +; + %cmp = icmp sgt i128 %x, 5 + %l = lshr i128 %x, %y + %r = ashr exact i128 %x, %y + %ret = select i1 %cmp, i128 %l, i128 %r + ret i128 %ret +} + define <2 x i32> @ashr_lshr_splat_vec(<2 x i32> %x, <2 x i32> %y) { ; CHECK-LABEL: @ashr_lshr_splat_vec( ; CHECK-NEXT: [[CMP12:%.*]] = ashr <2 x i32> [[X:%.*]], [[Y:%.*]] From 9e480b07ab8d7642e6e20470514d1d4ab004a13e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 16 Aug 2024 15:01:16 +0200 Subject: [PATCH 018/203] [InstCombine] Fix incorrect zero ext in select of lshr/ashr fold The -1 constant should be sign extended, not zero extended. Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 8 ++++---- llvm/test/Transforms/InstCombine/ashr-lshr.ll | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index aaf4ece3249a..da31846a723b 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -681,11 +681,11 @@ static Value *foldSelectICmpLshrAshr(const ICmpInst *IC, Value *TrueVal, Value *X, *Y; unsigned Bitwidth = CmpRHS->getType()->getScalarSizeInBits(); if ((Pred != ICmpInst::ICMP_SGT || - !match(CmpRHS, - m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, APInt(Bitwidth, -1)))) && + !match(CmpRHS, m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, + APInt::getAllOnes(Bitwidth)))) && (Pred != ICmpInst::ICMP_SLT || - !match(CmpRHS, - m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, APInt(Bitwidth, 0))))) + !match(CmpRHS, m_SpecificInt_ICMP(ICmpInst::ICMP_SGE, + APInt::getZero(Bitwidth))))) return nullptr; // Canonicalize so that ashr is in FalseVal. diff --git a/llvm/test/Transforms/InstCombine/ashr-lshr.ll b/llvm/test/Transforms/InstCombine/ashr-lshr.ll index 4a3f836e95c3..a81cd47b1cd4 100644 --- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll +++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll @@ -63,11 +63,8 @@ define i32 @ashr_lshr2(i32 %x, i32 %y) { define i128 @ashr_lshr2_i128(i128 %x, i128 %y) { ; CHECK-LABEL: @ashr_lshr2_i128( -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i128 [[X:%.*]], 5 -; CHECK-NEXT: [[L:%.*]] = lshr i128 [[X]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = ashr exact i128 [[X]], [[Y]] -; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i128 [[L]], i128 [[R]] -; CHECK-NEXT: ret i128 [[RET]] +; CHECK-NEXT: [[CMP1:%.*]] = ashr i128 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i128 [[CMP1]] ; %cmp = icmp sgt i128 %x, 5 %l = lshr i128 %x, %y From 29f2604ef3ff8b73d0b157ffcb38d1efcfa8b5b6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 15 Aug 2024 18:07:18 +0200 Subject: [PATCH 019/203] [InstSimplify] Add tests for f16 to i128 range (NFC) --- .../Transforms/InstSimplify/fptoi-range.ll | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/llvm/test/Transforms/InstSimplify/fptoi-range.ll b/llvm/test/Transforms/InstSimplify/fptoi-range.ll index cc9605259aa8..e0689c26c719 100644 --- a/llvm/test/Transforms/InstSimplify/fptoi-range.ll +++ b/llvm/test/Transforms/InstSimplify/fptoi-range.ll @@ -32,6 +32,17 @@ define i1 @f16_si16_max2(half %f) { ret i1 %c } +define i1 @f16_si128_max2(half %f) { +; CHECK-LABEL: @f16_si128_max2( +; CHECK-NEXT: [[I:%.*]] = fptosi half [[F:%.*]] to i128 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i128 [[I]], 65504 +; CHECK-NEXT: ret i1 [[C]] +; + %i = fptosi half %f to i128 + %c = icmp sgt i128 %i, 65504 + ret i1 %c +} + define i1 @f16_si_min1(half %f) { ; CHECK-LABEL: @f16_si_min1( ; CHECK-NEXT: ret i1 true @@ -41,6 +52,17 @@ define i1 @f16_si_min1(half %f) { ret i1 %c } +define i1 @f16_si128_min1(half %f) { +; CHECK-LABEL: @f16_si128_min1( +; CHECK-NEXT: [[I:%.*]] = fptosi half [[F:%.*]] to i128 +; CHECK-NEXT: [[C:%.*]] = icmp sge i128 [[I]], -65504 +; CHECK-NEXT: ret i1 [[C]] +; + %i = fptosi half %f to i128 + %c = icmp sge i128 %i, -65504 + ret i1 %c +} + define i1 @f16_si16_min1(half %f) { ; CHECK-LABEL: @f16_si16_min1( ; CHECK-NEXT: [[I:%.*]] = fptosi half [[F:%.*]] to i16 From b233ab51c9c15a619d0355c403ab6a08b71544e9 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 15 Aug 2024 18:08:27 +0200 Subject: [PATCH 020/203] [ValueTracking] Fix f16 fptosi range for large integers We were missing the signed flag on the negative value, so the range was incorrectly interpreted for integers larger than 64-bit. Split out from https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Analysis/ValueTracking.cpp | 2 +- .../CodeGen/Thumb2/mve-fpclamptosat_vec.ll | 194 ++++-------------- .../Transforms/InstSimplify/fptoi-range.ll | 8 +- 3 files changed, 43 insertions(+), 161 deletions(-) diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 4b77c0046cc7..24cafbcebc75 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -9673,7 +9673,7 @@ static void setLimitForFPToI(const Instruction *I, APInt &Lower, APInt &Upper) { if (!I->getOperand(0)->getType()->getScalarType()->isHalfTy()) return; if (isa(I) && BitWidth >= 17) { - Lower = APInt(BitWidth, -65504); + Lower = APInt(BitWidth, -65504, true); Upper = APInt(BitWidth, 65505); } diff --git a/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll b/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll index 94210d795867..f2ac52689218 100644 --- a/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll +++ b/llvm/test/CodeGen/Thumb2/mve-fpclamptosat_vec.ll @@ -1012,60 +1012,21 @@ entry: define arm_aapcs_vfpcc <2 x i64> @stest_f16i64(<2 x half> %x) { ; CHECK-LABEL: stest_f16i64: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, r8, r9, r10, lr} -; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, lr} +; CHECK-NEXT: .save {r4, r5, r7, lr} +; CHECK-NEXT: push {r4, r5, r7, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r7, r0, #-1 -; CHECK-NEXT: mvn r9, #-2147483648 -; CHECK-NEXT: sbcs.w r7, r1, r9 -; CHECK-NEXT: mov.w r10, #-2147483648 -; CHECK-NEXT: sbcs r7, r2, #0 -; CHECK-NEXT: sbcs r7, r3, #0 -; CHECK-NEXT: cset r7, lt -; CHECK-NEXT: cmp r7, #0 -; CHECK-NEXT: csel r3, r3, r7, ne -; CHECK-NEXT: csel r2, r2, r7, ne -; CHECK-NEXT: mov.w r7, #-1 -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r4, r0, r7, ne -; CHECK-NEXT: rsbs r0, r4, #0 -; CHECK-NEXT: sbcs.w r0, r10, r1 -; CHECK-NEXT: sbcs.w r0, r7, r2 -; CHECK-NEXT: sbcs.w r0, r7, r3 -; CHECK-NEXT: cset r5, lt +; CHECK-NEXT: mov r4, r0 ; CHECK-NEXT: vmov.u16 r0, q4[0] -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r8, r1, r10, ne +; CHECK-NEXT: mov r5, r1 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r6, r0, #-1 -; CHECK-NEXT: sbcs.w r6, r1, r9 -; CHECK-NEXT: sbcs r6, r2, #0 -; CHECK-NEXT: sbcs r6, r3, #0 -; CHECK-NEXT: cset r6, lt -; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r0, r0, r7, ne -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r3, r3, r6, ne -; CHECK-NEXT: csel r2, r2, r6, ne -; CHECK-NEXT: rsbs r6, r0, #0 -; CHECK-NEXT: sbcs.w r6, r10, r1 -; CHECK-NEXT: sbcs.w r2, r7, r2 -; CHECK-NEXT: sbcs.w r2, r7, r3 -; CHECK-NEXT: cset r2, lt -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r1, r1, r10, ne -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r3, r4, r5, ne -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r0, r0, r2, ne -; CHECK-NEXT: vmov q0[2], q0[0], r0, r3 -; CHECK-NEXT: vmov q0[3], q0[1], r1, r8 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r4 +; CHECK-NEXT: vmov q0[3], q0[1], r1, r5 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, pc} +; CHECK-NEXT: pop {r4, r5, r7, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %0 = icmp slt <2 x i128> %conv, @@ -1105,46 +1066,28 @@ entry: define arm_aapcs_vfpcc <2 x i64> @ustest_f16i64(<2 x half> %x) { ; CHECK-LABEL: ustest_f16i64: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, r8, r9, lr} -; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, lr} -; CHECK-NEXT: .pad #4 -; CHECK-NEXT: sub sp, #4 +; CHECK-NEXT: .save {r4, r5, r6, r7, r8, lr} +; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs r5, r2, #1 -; CHECK-NEXT: mov.w r8, #1 -; CHECK-NEXT: sbcs r5, r3, #0 -; CHECK-NEXT: mov.w r7, #0 -; CHECK-NEXT: cset r5, lt -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r0, r0, r5, ne -; CHECK-NEXT: csel r3, r3, r5, ne -; CHECK-NEXT: csel r2, r2, r8, ne -; CHECK-NEXT: csel r4, r1, r5, ne +; CHECK-NEXT: mov r4, r1 ; CHECK-NEXT: rsbs r1, r0, #0 -; CHECK-NEXT: sbcs.w r1, r7, r4 -; CHECK-NEXT: sbcs.w r1, r7, r2 -; CHECK-NEXT: sbcs.w r1, r7, r3 +; CHECK-NEXT: mov.w r5, #0 +; CHECK-NEXT: sbcs.w r1, r5, r4 +; CHECK-NEXT: sbcs.w r1, r5, r2 +; CHECK-NEXT: sbcs.w r1, r5, r3 ; CHECK-NEXT: cset r6, lt ; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r9, r0, r6, ne +; CHECK-NEXT: csel r8, r0, r6, ne ; CHECK-NEXT: vmov.u16 r0, q4[0] ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs r5, r2, #1 -; CHECK-NEXT: sbcs r5, r3, #0 -; CHECK-NEXT: cset r5, lt -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r0, r0, r5, ne -; CHECK-NEXT: csel r2, r2, r8, ne -; CHECK-NEXT: csel r3, r3, r5, ne -; CHECK-NEXT: csel r1, r1, r5, ne -; CHECK-NEXT: rsbs r5, r0, #0 -; CHECK-NEXT: sbcs.w r5, r7, r1 -; CHECK-NEXT: sbcs.w r2, r7, r2 -; CHECK-NEXT: sbcs.w r2, r7, r3 +; CHECK-NEXT: rsbs r7, r0, #0 +; CHECK-NEXT: sbcs.w r7, r5, r1 +; CHECK-NEXT: sbcs.w r2, r5, r2 +; CHECK-NEXT: sbcs.w r2, r5, r3 ; CHECK-NEXT: cset r2, lt ; CHECK-NEXT: cmp r2, #0 ; CHECK-NEXT: csel r0, r0, r2, ne @@ -1152,11 +1095,10 @@ define arm_aapcs_vfpcc <2 x i64> @ustest_f16i64(<2 x half> %x) { ; CHECK-NEXT: csel r3, r4, r6, ne ; CHECK-NEXT: cmp r2, #0 ; CHECK-NEXT: csel r1, r1, r2, ne -; CHECK-NEXT: vmov q0[2], q0[0], r0, r9 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r8 ; CHECK-NEXT: vmov q0[3], q0[1], r1, r3 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: add sp, #4 -; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, r9, pc} +; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %0 = icmp slt <2 x i128> %conv, @@ -2119,60 +2061,21 @@ entry: define arm_aapcs_vfpcc <2 x i64> @stest_f16i64_mm(<2 x half> %x) { ; CHECK-LABEL: stest_f16i64_mm: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, r8, r9, r10, lr} -; CHECK-NEXT: push.w {r4, r5, r6, r7, r8, r9, r10, lr} +; CHECK-NEXT: .save {r4, r5, r7, lr} +; CHECK-NEXT: push {r4, r5, r7, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r7, r0, #-1 -; CHECK-NEXT: mvn r9, #-2147483648 -; CHECK-NEXT: sbcs.w r7, r1, r9 -; CHECK-NEXT: mov.w r10, #-2147483648 -; CHECK-NEXT: sbcs r7, r2, #0 -; CHECK-NEXT: sbcs r7, r3, #0 -; CHECK-NEXT: cset r7, lt -; CHECK-NEXT: cmp r7, #0 -; CHECK-NEXT: csel r3, r3, r7, ne -; CHECK-NEXT: csel r2, r2, r7, ne -; CHECK-NEXT: mov.w r7, #-1 -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r4, r0, r7, ne -; CHECK-NEXT: rsbs r0, r4, #0 -; CHECK-NEXT: sbcs.w r0, r10, r1 -; CHECK-NEXT: sbcs.w r0, r7, r2 -; CHECK-NEXT: sbcs.w r0, r7, r3 -; CHECK-NEXT: cset r5, lt +; CHECK-NEXT: mov r4, r0 ; CHECK-NEXT: vmov.u16 r0, q4[0] -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r8, r1, r10, ne +; CHECK-NEXT: mov r5, r1 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs.w r6, r0, #-1 -; CHECK-NEXT: sbcs.w r6, r1, r9 -; CHECK-NEXT: sbcs r6, r2, #0 -; CHECK-NEXT: sbcs r6, r3, #0 -; CHECK-NEXT: cset r6, lt -; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r0, r0, r7, ne -; CHECK-NEXT: csel r1, r1, r9, ne -; CHECK-NEXT: csel r3, r3, r6, ne -; CHECK-NEXT: csel r2, r2, r6, ne -; CHECK-NEXT: rsbs r6, r0, #0 -; CHECK-NEXT: sbcs.w r6, r10, r1 -; CHECK-NEXT: sbcs.w r2, r7, r2 -; CHECK-NEXT: sbcs.w r2, r7, r3 -; CHECK-NEXT: cset r2, lt -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r1, r1, r10, ne -; CHECK-NEXT: cmp r5, #0 -; CHECK-NEXT: csel r3, r4, r5, ne -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r0, r0, r2, ne -; CHECK-NEXT: vmov q0[2], q0[0], r0, r3 -; CHECK-NEXT: vmov q0[3], q0[1], r1, r8 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r4 +; CHECK-NEXT: vmov q0[3], q0[1], r1, r5 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: pop.w {r4, r5, r6, r7, r8, r9, r10, pc} +; CHECK-NEXT: pop {r4, r5, r7, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %spec.store.select = call <2 x i128> @llvm.smin.v2i128(<2 x i128> %conv, <2 x i128> ) @@ -2209,51 +2112,34 @@ entry: define arm_aapcs_vfpcc <2 x i64> @ustest_f16i64_mm(<2 x half> %x) { ; CHECK-LABEL: ustest_f16i64_mm: ; CHECK: @ %bb.0: @ %entry -; CHECK-NEXT: .save {r4, r5, r6, r7, lr} -; CHECK-NEXT: push {r4, r5, r6, r7, lr} -; CHECK-NEXT: .pad #4 -; CHECK-NEXT: sub sp, #4 +; CHECK-NEXT: .save {r4, r5, r6, lr} +; CHECK-NEXT: push {r4, r5, r6, lr} ; CHECK-NEXT: .vsave {d8, d9} ; CHECK-NEXT: vpush {d8, d9} ; CHECK-NEXT: vmov.u16 r0, q0[1] ; CHECK-NEXT: vmov q4, q0 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: mov r4, r1 -; CHECK-NEXT: subs r1, r2, #1 -; CHECK-NEXT: sbcs r1, r3, #0 -; CHECK-NEXT: cset r6, lt -; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: csel r5, r0, r6, ne -; CHECK-NEXT: csel r7, r3, r6, ne +; CHECK-NEXT: mov r4, r0 ; CHECK-NEXT: vmov.u16 r0, q4[0] -; CHECK-NEXT: cmp r7, #0 -; CHECK-NEXT: it mi -; CHECK-NEXT: movmi r5, #0 +; CHECK-NEXT: mov r5, r1 +; CHECK-NEXT: mov r6, r3 ; CHECK-NEXT: bl __fixhfti -; CHECK-NEXT: subs r2, #1 -; CHECK-NEXT: sbcs r2, r3, #0 -; CHECK-NEXT: cset r2, lt -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r3, r3, r2, ne -; CHECK-NEXT: csel r0, r0, r2, ne +; CHECK-NEXT: cmp r6, #0 +; CHECK-NEXT: it mi +; CHECK-NEXT: movmi r4, #0 ; CHECK-NEXT: cmp r3, #0 ; CHECK-NEXT: it mi ; CHECK-NEXT: movmi r0, #0 ; CHECK-NEXT: cmp r6, #0 -; CHECK-NEXT: vmov q0[2], q0[0], r0, r5 -; CHECK-NEXT: csel r6, r4, r6, ne -; CHECK-NEXT: cmp r7, #0 +; CHECK-NEXT: vmov q0[2], q0[0], r0, r4 ; CHECK-NEXT: it mi -; CHECK-NEXT: movmi r6, #0 -; CHECK-NEXT: cmp r2, #0 -; CHECK-NEXT: csel r1, r1, r2, ne +; CHECK-NEXT: movmi r5, #0 ; CHECK-NEXT: cmp r3, #0 ; CHECK-NEXT: it mi ; CHECK-NEXT: movmi r1, #0 -; CHECK-NEXT: vmov q0[3], q0[1], r1, r6 +; CHECK-NEXT: vmov q0[3], q0[1], r1, r5 ; CHECK-NEXT: vpop {d8, d9} -; CHECK-NEXT: add sp, #4 -; CHECK-NEXT: pop {r4, r5, r6, r7, pc} +; CHECK-NEXT: pop {r4, r5, r6, pc} entry: %conv = fptosi <2 x half> %x to <2 x i128> %spec.store.select = call <2 x i128> @llvm.smin.v2i128(<2 x i128> %conv, <2 x i128> ) diff --git a/llvm/test/Transforms/InstSimplify/fptoi-range.ll b/llvm/test/Transforms/InstSimplify/fptoi-range.ll index e0689c26c719..95f2a9d50793 100644 --- a/llvm/test/Transforms/InstSimplify/fptoi-range.ll +++ b/llvm/test/Transforms/InstSimplify/fptoi-range.ll @@ -34,9 +34,7 @@ define i1 @f16_si16_max2(half %f) { define i1 @f16_si128_max2(half %f) { ; CHECK-LABEL: @f16_si128_max2( -; CHECK-NEXT: [[I:%.*]] = fptosi half [[F:%.*]] to i128 -; CHECK-NEXT: [[C:%.*]] = icmp sgt i128 [[I]], 65504 -; CHECK-NEXT: ret i1 [[C]] +; CHECK-NEXT: ret i1 false ; %i = fptosi half %f to i128 %c = icmp sgt i128 %i, 65504 @@ -54,9 +52,7 @@ define i1 @f16_si_min1(half %f) { define i1 @f16_si128_min1(half %f) { ; CHECK-LABEL: @f16_si128_min1( -; CHECK-NEXT: [[I:%.*]] = fptosi half [[F:%.*]] to i128 -; CHECK-NEXT: [[C:%.*]] = icmp sge i128 [[I]], -65504 -; CHECK-NEXT: ret i1 [[C]] +; CHECK-NEXT: ret i1 true ; %i = fptosi half %f to i128 %c = icmp sge i128 %i, -65504 From 267d86cb7480a52a6cff1a5233b68b43d93e7a10 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Fri, 16 Aug 2024 09:21:11 -0700 Subject: [PATCH 021/203] [SelectionDAG][X86] Add SelectionDAG::getSignedConstant and use it in a few places. (#104555) PR #80309 proposes to have users of APInt's uint64_t constructor opt-in to implicit truncation. Currently, that patch requires SelectionDAG::getConstant to opt-in. This patch adds getSignedConstant so we can start fixing some of the cases that require implicit truncation. --- llvm/include/llvm/CodeGen/SelectionDAG.h | 3 +++ llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 2 +- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 9 +++++++++ .../SelectionDAG/SelectionDAGBuilder.cpp | 2 +- .../CodeGen/SelectionDAG/TargetLowering.cpp | 4 ++-- llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 10 ++++++---- llvm/lib/Target/X86/X86ISelLowering.cpp | 19 ++++++++++--------- 7 files changed, 32 insertions(+), 17 deletions(-) diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 16ec65f2e7da..508685518338 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -670,6 +670,9 @@ class SelectionDAG { SDValue getConstant(const APInt &Val, const SDLoc &DL, EVT VT, bool isTarget = false, bool isOpaque = false); + SDValue getSignedConstant(int64_t Val, const SDLoc &DL, EVT VT, + bool isTarget = false, bool isOpaque = false); + SDValue getAllOnesConstant(const SDLoc &DL, EVT VT, bool IsTarget = false, bool IsOpaque = false) { return getConstant(APInt::getAllOnes(VT.getScalarSizeInBits()), DL, VT, diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index 4b25f553ffae..ee7c146cdf1f 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -2592,7 +2592,7 @@ SDValue SelectionDAGLegalize::expandFrexp(SDNode *Node) const { SDValue IsDenormal = DAG.getSetCC(dl, SetCCVT, Abs, SmallestNormalizedInt, ISD::SETULT); - SDValue MinExp = DAG.getConstant(MinExpVal, dl, ExpVT); + SDValue MinExp = DAG.getSignedConstant(MinExpVal, dl, ExpVT); SDValue Zero = DAG.getConstant(0, dl, ExpVT); SDValue ScaledAsInt = DAG.getNode(ISD::BITCAST, dl, AsIntVT, ScaleUp); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index c6143820aec9..8d69f3195798 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1751,6 +1751,15 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, return Result; } +SDValue SelectionDAG::getSignedConstant(int64_t Val, const SDLoc &DL, EVT VT, + bool isT, bool isO) { + unsigned Size = VT.getScalarSizeInBits(); + assert( + isIntN(Size, Val) && + "getSignedConstant with a int64_t value that doesn't fit in the type!"); + return getConstant(APInt(Size, Val, true), DL, VT, isT, isO); +} + SDValue SelectionDAG::getIntPtrConstant(uint64_t Val, const SDLoc &DL, bool isTarget) { return getConstant(Val, DL, TLI->getPointerTy(getDataLayout()), isTarget); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index f92100a74141..ac64d046bf89 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4475,7 +4475,7 @@ void SelectionDAGBuilder::visitAlloca(const AllocaInst &I) { // Mask out the low bits for alignment purposes. AllocSize = DAG.getNode(ISD::AND, dl, AllocSize.getValueType(), AllocSize, - DAG.getConstant(~StackAlignMask, dl, IntPtr)); + DAG.getSignedConstant(~StackAlignMask, dl, IntPtr)); SDValue Ops[] = { getRoot(), AllocSize, diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 2cb79a1446c8..55a1d25b4cc6 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -6342,9 +6342,9 @@ SDValue TargetLowering::BuildSDIV(SDNode *N, SelectionDAG &DAG, } MagicFactors.push_back(DAG.getConstant(magics.Magic, dl, SVT)); - Factors.push_back(DAG.getConstant(NumeratorFactor, dl, SVT)); + Factors.push_back(DAG.getSignedConstant(NumeratorFactor, dl, SVT)); Shifts.push_back(DAG.getConstant(magics.ShiftAmount, dl, ShSVT)); - ShiftMasks.push_back(DAG.getConstant(ShiftMask, dl, SVT)); + ShiftMasks.push_back(DAG.getSignedConstant(ShiftMask, dl, SVT)); return true; }; diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp index 74804e5c9783..d0a54ab8993c 100644 --- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -314,7 +314,8 @@ namespace { Disp = CurDAG->getTargetBlockAddress(AM.BlockAddr, MVT::i32, AM.Disp, AM.SymbolFlags); else - Disp = CurDAG->getTargetConstant(AM.Disp, DL, MVT::i32); + Disp = + CurDAG->getSignedConstant(AM.Disp, DL, MVT::i32, /*isTarget=*/true); if (AM.Segment.getNode()) Segment = AM.Segment; @@ -2130,7 +2131,7 @@ static bool foldMaskedShiftToScaledMask(SelectionDAG &DAG, SDValue N, X = NewX; } - SDValue NewMask = DAG.getConstant(Mask >> ShiftAmt, DL, VT); + SDValue NewMask = DAG.getSignedConstant(Mask >> ShiftAmt, DL, VT); SDValue NewAnd = DAG.getNode(ISD::AND, DL, VT, X, NewMask); SDValue NewShift = DAG.getNode(ISD::SHL, DL, VT, NewAnd, Shift.getOperand(1)); @@ -3733,7 +3734,8 @@ bool X86DAGToDAGISel::foldLoadStoreIntoMemOperand(SDNode *Node) { } if (MemVT != MVT::i64 || isInt<32>(OperandV)) { - Operand = CurDAG->getTargetConstant(OperandV, SDLoc(Node), MemVT); + Operand = CurDAG->getSignedConstant(OperandV, SDLoc(Node), MemVT, + /*isTarget=*/true); NewOpc = SelectImmOpcode(Opc); } } @@ -4507,7 +4509,7 @@ bool X86DAGToDAGISel::tryShrinkShlLogicImm(SDNode *N) { X = NewX; } - SDValue NewCst = CurDAG->getConstant(ShiftedVal, dl, NVT); + SDValue NewCst = CurDAG->getSignedConstant(ShiftedVal, dl, NVT); insertDAGNode(*CurDAG, SDValue(N, 0), NewCst); SDValue NewBinOp = CurDAG->getNode(Opcode, dl, NVT, X, NewCst); insertDAGNode(*CurDAG, SDValue(N, 0), NewBinOp); diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 62e4d6399497..640ef79f5629 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -10762,9 +10762,9 @@ static SDValue lowerShuffleAsBlend(const SDLoc &DL, MVT VT, SDValue V1, for (int i = 0, Size = Mask.size(); i < Size; ++i) for (int j = 0; j < Scale; ++j) VSELECTMask.push_back( - Mask[i] < 0 ? DAG.getUNDEF(MVT::i8) - : DAG.getConstant(Mask[i] < Size ? -1 : 0, DL, - MVT::i8)); + Mask[i] < 0 + ? DAG.getUNDEF(MVT::i8) + : DAG.getSignedConstant(Mask[i] < Size ? -1 : 0, DL, MVT::i8)); V1 = DAG.getBitcast(BlendVT, V1); V2 = DAG.getBitcast(BlendVT, V2); @@ -18649,7 +18649,7 @@ SDValue X86TargetLowering::LowerGlobalOrExternal(SDValue Op, SelectionDAG &DAG, // addition for it. if (Offset != 0) Result = DAG.getNode(ISD::ADD, dl, PtrVT, Result, - DAG.getConstant(Offset, dl, PtrVT)); + DAG.getSignedConstant(Offset, dl, PtrVT)); return Result; } @@ -24969,9 +24969,9 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, Result = DAG.getNode(ISD::SUB, dl, VT, SP, Size); // Value } if (Alignment && *Alignment > StackAlign) - Result = - DAG.getNode(ISD::AND, dl, VT, Result, - DAG.getConstant(~(Alignment->value() - 1ULL), dl, VT)); + Result = DAG.getNode( + ISD::AND, dl, VT, Result, + DAG.getSignedConstant(~(Alignment->value() - 1ULL), dl, VT)); Chain = DAG.getCopyToReg(Chain, dl, SPReg, Result); // Output chain } else if (SplitStack) { MachineRegisterInfo &MRI = MF.getRegInfo(); @@ -25003,8 +25003,9 @@ X86TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, Chain = SP.getValue(1); if (Alignment) { - SP = DAG.getNode(ISD::AND, dl, VT, SP.getValue(0), - DAG.getConstant(~(Alignment->value() - 1ULL), dl, VT)); + SP = DAG.getNode( + ISD::AND, dl, VT, SP.getValue(0), + DAG.getSignedConstant(~(Alignment->value() - 1ULL), dl, VT)); Chain = DAG.getCopyToReg(Chain, dl, SPReg, SP); } From 22b8209a56b5966885597da64cee5b4b3d2a79f1 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Sat, 17 Aug 2024 00:02:20 -0700 Subject: [PATCH 022/203] [SelectionDAG] Use getSignedConstant/getAllOnesConstant. --- llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 2 +- llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp | 2 +- llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 6 +++--- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 6 ++++-- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index 7b1f1dc40211..f6ac4b4ce217 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -9582,7 +9582,7 @@ SDValue DAGCombiner::visitXOR(SDNode *N) { // A rotate left of ~1 is a nice way of achieving the desired result. if (TLI.isOperationLegalOrCustom(ISD::ROTL, VT) && N0Opcode == ISD::SHL && isAllOnesConstant(N1) && isOneConstant(N0.getOperand(0))) { - return DAG.getNode(ISD::ROTL, DL, VT, DAG.getConstant(~1, DL, VT), + return DAG.getNode(ISD::ROTL, DL, VT, DAG.getSignedConstant(~1, DL, VT), N0.getOperand(1)); } diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp index af77b0070df0..3f11cbd0d07c 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -3289,7 +3289,7 @@ void DAGTypeLegalizer::ExpandIntRes_MINMAX(SDNode *N, SDValue HiNeg = DAG.getSetCC(DL, CCT, LHSH, DAG.getConstant(0, DL, NVT), ISD::SETLT); if (N->getOpcode() == ISD::SMIN) { - Lo = DAG.getSelect(DL, NVT, HiNeg, LHSL, DAG.getConstant(-1, DL, NVT)); + Lo = DAG.getSelect(DL, NVT, HiNeg, LHSL, DAG.getAllOnesConstant(DL, NVT)); } else { Lo = DAG.getSelect(DL, NVT, HiNeg, DAG.getConstant(0, DL, NVT), LHSL); } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 8d69f3195798..c1761cbe9e3a 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -2441,9 +2441,9 @@ SDValue SelectionDAG::expandVAArg(SDNode *Node) { VAList = getNode(ISD::ADD, dl, VAList.getValueType(), VAList, getConstant(MA->value() - 1, dl, VAList.getValueType())); - VAList = - getNode(ISD::AND, dl, VAList.getValueType(), VAList, - getConstant(-(int64_t)MA->value(), dl, VAList.getValueType())); + VAList = getNode( + ISD::AND, dl, VAList.getValueType(), VAList, + getSignedConstant(-(int64_t)MA->value(), dl, VAList.getValueType())); } // Increment the pointer, VAList, to the next vaarg diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index ac64d046bf89..a223927de4cd 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -12508,8 +12508,10 @@ void SelectionDAGBuilder::visitVectorSplice(const CallInst &I) { // VECTOR_SHUFFLE doesn't support a scalable mask so use a dedicated node. if (VT.isScalableVector()) { - setValue(&I, DAG.getNode(ISD::VECTOR_SPLICE, DL, VT, V1, V2, - DAG.getVectorIdxConstant(Imm, DL))); + setValue( + &I, DAG.getNode(ISD::VECTOR_SPLICE, DL, VT, V1, V2, + DAG.getSignedConstant( + Imm, DL, TLI.getVectorIdxTy(DAG.getDataLayout())))); return; } diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 55a1d25b4cc6..476b6c4957df 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -9049,8 +9049,8 @@ SDValue TargetLowering::expandVPCTLZ(SDNode *Node, SelectionDAG &DAG) const { DAG.getNode(ISD::VP_SRL, dl, VT, Op, Tmp, Mask, VL), Mask, VL); } - Op = DAG.getNode(ISD::VP_XOR, dl, VT, Op, DAG.getConstant(-1, dl, VT), Mask, - VL); + Op = DAG.getNode(ISD::VP_XOR, dl, VT, Op, DAG.getAllOnesConstant(dl, VT), + Mask, VL); return DAG.getNode(ISD::VP_CTPOP, dl, VT, Op, Mask, VL); } @@ -9163,7 +9163,7 @@ SDValue TargetLowering::expandVPCTTZ(SDNode *Node, SelectionDAG &DAG) const { // Same as the vector part of expandCTTZ, use: popcount(~x & (x - 1)) SDValue Not = DAG.getNode(ISD::VP_XOR, dl, VT, Op, - DAG.getConstant(-1, dl, VT), Mask, VL); + DAG.getAllOnesConstant(dl, VT), Mask, VL); SDValue MinusOne = DAG.getNode(ISD::VP_SUB, dl, VT, Op, DAG.getConstant(1, dl, VT), Mask, VL); SDValue Tmp = DAG.getNode(ISD::VP_AND, dl, VT, Not, MinusOne, Mask, VL); From dad91feb366d9d2dcd39133f96cf20d27b65e84d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 5 Sep 2024 16:04:52 +0200 Subject: [PATCH 023/203] [ConstantRangeTest] Set APInt signed flags where needed (NFC) Split out from https://github.com/llvm/llvm-project/pull/80309 to avoid assertion failures in the future. --- llvm/unittests/IR/ConstantRangeTest.cpp | 226 ++++++++++++------------ 1 file changed, 117 insertions(+), 109 deletions(-) diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index 0181e2ce6ac9..618b30999315 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -366,10 +366,10 @@ TEST_F(ConstantRangeTest, GetMinsAndMaxes) { EXPECT_EQ(Some.getSignedMax(), APInt(16, 0xaa9)); EXPECT_EQ(Wrap.getSignedMax(), APInt(16, INT16_MAX)); - EXPECT_EQ(Full.getSignedMin(), APInt(16, (uint64_t)INT16_MIN)); + EXPECT_EQ(Full.getSignedMin(), APInt(16, (uint16_t)INT16_MIN)); EXPECT_EQ(One.getSignedMin(), APInt(16, 0xa)); EXPECT_EQ(Some.getSignedMin(), APInt(16, 0xa)); - EXPECT_EQ(Wrap.getSignedMin(), APInt(16, (uint64_t)INT16_MIN)); + EXPECT_EQ(Wrap.getSignedMin(), APInt(16, (uint16_t)INT16_MIN)); // Found by Klee EXPECT_EQ(ConstantRange(APInt(4, 7), APInt(4, 0)).getSignedMax(), @@ -481,7 +481,7 @@ TEST_F(ConstantRangeTest, SExt) { APInt(20, INT16_MAX + 1, true))); EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, 140)).signExtend(16), - ConstantRange(APInt(16, -128), APInt(16, 128))); + ConstantRange(APInt(16, -128, true), APInt(16, 128))); EXPECT_EQ(ConstantRange(APInt(16, 0x0200), APInt(16, 0x8000)).signExtend(19), ConstantRange(APInt(19, 0x0200), APInt(19, 0x8000))); @@ -510,7 +510,7 @@ TEST_F(ConstantRangeTest, IntersectWith) { EXPECT_TRUE(LHS.intersectWith(RHS) == LHS); // previous bug: intersection of [min, 3) and [2, max) should be 2 - LHS = ConstantRange(APInt(32, -2147483646), APInt(32, 3)); + LHS = ConstantRange(APInt(32, (uint32_t)-2147483646), APInt(32, 3)); RHS = ConstantRange(APInt(32, 2), APInt(32, 2147483646)); EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 2))); @@ -738,45 +738,51 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_NE(Some.addWithNoWrap(Full, OBO::NoSignedWrap), Full); EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, 1), APInt(16, 2)), OBO::NoSignedWrap), - ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN))); + ConstantRange(APInt(16, INT16_MIN + 1, true), + APInt(16, INT16_MIN, true))); EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 2)) .addWithNoWrap(Full, OBO::NoSignedWrap), - ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN))); - EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, -1), APInt(16, 0)), + ConstantRange(APInt(16, INT16_MIN + 1, true), + APInt(16, INT16_MIN, true))); + EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, -1, true), APInt(16, 0)), OBO::NoSignedWrap), - ConstantRange(APInt(16, INT16_MIN), APInt(16, INT16_MAX))); + ConstantRange(APInt(16, INT16_MIN, true), APInt(16, INT16_MAX))); EXPECT_EQ(ConstantRange(APInt(8, 100), APInt(8, 120)) .addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, 123)), OBO::NoSignedWrap), ConstantRange(8, false)); - EXPECT_EQ(ConstantRange(APInt(8, -120), APInt(8, -100)) - .addWithNoWrap(ConstantRange(APInt(8, -110), APInt(8, -100)), - OBO::NoSignedWrap), + EXPECT_EQ(ConstantRange(APInt(8, -120, true), APInt(8, -100, true)) + .addWithNoWrap( + ConstantRange(APInt(8, -110, true), APInt(8, -100, true)), + OBO::NoSignedWrap), ConstantRange(8, false)); - EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)) - .addWithNoWrap(ConstantRange(APInt(8, -128), APInt(8, 28)), - OBO::NoSignedWrap), - ConstantRange(8, true)); - EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101)) - .addWithNoWrap(ConstantRange(APInt(8, -120), APInt(8, 29)), - OBO::NoSignedWrap), - ConstantRange(APInt(8, -120), APInt(8, -128))); - EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)) + EXPECT_EQ( + ConstantRange(APInt(8, 0), APInt(8, 101)) + .addWithNoWrap(ConstantRange(APInt(8, -128, true), APInt(8, 28)), + OBO::NoSignedWrap), + ConstantRange(8, true)); + EXPECT_EQ( + ConstantRange(APInt(8, 0), APInt(8, 101)) + .addWithNoWrap(ConstantRange(APInt(8, -120, true), APInt(8, 29)), + OBO::NoSignedWrap), + ConstantRange(APInt(8, -120, true), APInt(8, -128, true))); + EXPECT_EQ(ConstantRange(APInt(8, -50, true), APInt(8, 50)) .addWithNoWrap(ConstantRange(APInt(8, 10), APInt(8, 20)), OBO::NoSignedWrap), - ConstantRange(APInt(8, -40), APInt(8, 69))); + ConstantRange(APInt(8, -40, true), APInt(8, 69))); EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20)) - .addWithNoWrap(ConstantRange(APInt(8, -50), APInt(8, 50)), + .addWithNoWrap(ConstantRange(APInt(8, -50, true), APInt(8, 50)), OBO::NoSignedWrap), - ConstantRange(APInt(8, -40), APInt(8, 69))); - EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10)) + ConstantRange(APInt(8, -40, true), APInt(8, 69))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10, true)) .addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)), OBO::NoSignedWrap), ConstantRange(APInt(8, 125), APInt(8, 9))); - EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20)) - .addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, -10)), - OBO::NoSignedWrap), - ConstantRange(APInt(8, 125), APInt(8, 9))); + EXPECT_EQ( + ConstantRange(APInt(8, 5), APInt(8, 20)) + .addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, -10, true)), + OBO::NoSignedWrap), + ConstantRange(APInt(8, 125), APInt(8, 9))); TestBinaryOpExhaustive( [](const ConstantRange &CR1, const ConstantRange &CR2) { @@ -821,15 +827,15 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20)) .addWithNoWrap(ConstantRange(APInt(8, 50), APInt(8, 200)), OBO::NoUnsignedWrap), - ConstantRange(APInt(8, 60), APInt(8, -37))); - EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, -30)) + ConstantRange(APInt(8, 60), APInt(8, -37, true))); + EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, -30, true)) .addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)), OBO::NoUnsignedWrap), - ConstantRange(APInt(8, 25), APInt(8, -11))); + ConstantRange(APInt(8, 25), APInt(8, -11, true))); EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20)) - .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, -30)), + .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, -30, true)), OBO::NoUnsignedWrap), - ConstantRange(APInt(8, 25), APInt(8, -11))); + ConstantRange(APInt(8, 25), APInt(8, -11, true))); TestBinaryOpExhaustive( [](const ConstantRange &CR1, const ConstantRange &CR2) { @@ -847,7 +853,7 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)), OBO::NoSignedWrap), - ConstantRange(APInt(8, 70), APInt(8, -128))); + ConstantRange(APInt(8, 70), APInt(8, -128, true))); EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)), OBO::NoUnsignedWrap), @@ -855,17 +861,17 @@ TEST_F(ConstantRangeTest, AddWithNoWrap) { EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)), OBO::NoUnsignedWrap | OBO::NoSignedWrap), - ConstantRange(APInt(8, 70), APInt(8, -128))); + ConstantRange(APInt(8, 70), APInt(8, -128, true))); - EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)) + EXPECT_EQ(ConstantRange(APInt(8, -100, true), APInt(8, -50, true)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)), OBO::NoSignedWrap), - ConstantRange(APInt(8, -80), APInt(8, -21))); - EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)) + ConstantRange(APInt(8, -80, true), APInt(8, -21, true))); + EXPECT_EQ(ConstantRange(APInt(8, -100, true), APInt(8, -50, true)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)), OBO::NoUnsignedWrap), ConstantRange(APInt(8, 176), APInt(8, 235))); - EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50)) + EXPECT_EQ(ConstantRange(APInt(8, -100, true), APInt(8, -50, true)) .addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)), OBO::NoUnsignedWrap | OBO::NoSignedWrap), ConstantRange(APInt(8, 176), APInt(8, 235))); @@ -998,17 +1004,17 @@ TEST_F(ConstantRangeTest, Multiply) { ConstantRange(APInt(8, 250), APInt(8, 253))); // TODO: This should be return [-2, 0] - EXPECT_EQ(ConstantRange(APInt(8, -2)).multiply( - ConstantRange(APInt(8, 0), APInt(8, 2))), - ConstantRange(APInt(8, -2), APInt(8, 1))); + EXPECT_EQ(ConstantRange(APInt(8, -2, true)) + .multiply(ConstantRange(APInt(8, 0), APInt(8, 2))), + ConstantRange(APInt(8, -2, true), APInt(8, 1))); // Multiplication by -1 should give precise results. - EXPECT_EQ(ConstantRange(APInt(8, 3), APInt(8, -11)) - .multiply(ConstantRange(APInt(8, -1))), - ConstantRange(APInt(8, 12), APInt(8, -2))); - EXPECT_EQ(ConstantRange(APInt(8, -1)) - .multiply(ConstantRange(APInt(8, 3), APInt(8, -11))), - ConstantRange(APInt(8, 12), APInt(8, -2))); + EXPECT_EQ(ConstantRange(APInt(8, 3), APInt(8, -11, true)) + .multiply(ConstantRange(APInt(8, -1, true))), + ConstantRange(APInt(8, 12), APInt(8, -2, true))); + EXPECT_EQ(ConstantRange(APInt(8, -1, true)) + .multiply(ConstantRange(APInt(8, 3), APInt(8, -11, true))), + ConstantRange(APInt(8, 12), APInt(8, -2, true))); TestBinaryOpExhaustive( [](const ConstantRange &CR1, const ConstantRange &CR2) { @@ -1161,11 +1167,11 @@ TEST_F(ConstantRangeTest, SMax) { EXPECT_EQ(Empty.smax(Wrap), Empty); EXPECT_EQ(Empty.smax(One), Empty); EXPECT_EQ(Some.smax(Some), Some); - EXPECT_EQ(Some.smax(Wrap), ConstantRange(APInt(16, 0xa), - APInt(16, (uint64_t)INT16_MIN))); + EXPECT_EQ(Some.smax(Wrap), + ConstantRange(APInt(16, 0xa), APInt(16, (uint16_t)INT16_MIN))); EXPECT_EQ(Some.smax(One), Some); - EXPECT_EQ(Wrap.smax(One), ConstantRange(APInt(16, 0xa), - APInt(16, (uint64_t)INT16_MIN))); + EXPECT_EQ(Wrap.smax(One), + ConstantRange(APInt(16, 0xa), APInt(16, (uint16_t)INT16_MIN))); EXPECT_EQ(One.smax(One), One); TestBinaryOpExhaustive( @@ -1207,20 +1213,20 @@ TEST_F(ConstantRangeTest, UMin) { TEST_F(ConstantRangeTest, SMin) { EXPECT_EQ(Full.smin(Full), Full); EXPECT_EQ(Full.smin(Empty), Empty); - EXPECT_EQ(Full.smin(Some), ConstantRange(APInt(16, (uint64_t)INT16_MIN), - APInt(16, 0xaaa))); + EXPECT_EQ(Full.smin(Some), + ConstantRange(APInt(16, (uint16_t)INT16_MIN), APInt(16, 0xaaa))); EXPECT_EQ(Full.smin(Wrap), Full); EXPECT_EQ(Empty.smin(Empty), Empty); EXPECT_EQ(Empty.smin(Some), Empty); EXPECT_EQ(Empty.smin(Wrap), Empty); EXPECT_EQ(Empty.smin(One), Empty); EXPECT_EQ(Some.smin(Some), Some); - EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN), - APInt(16, 0xaaa))); + EXPECT_EQ(Some.smin(Wrap), + ConstantRange(APInt(16, (uint16_t)INT16_MIN), APInt(16, 0xaaa))); EXPECT_EQ(Some.smin(One), One); EXPECT_EQ(Wrap.smin(Wrap), Wrap); - EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN), - APInt(16, 0xb))); + EXPECT_EQ(Wrap.smin(One), + ConstantRange(APInt(16, (uint16_t)INT16_MIN), APInt(16, 0xb))); EXPECT_EQ(One.smin(One), One); TestBinaryOpExhaustive( @@ -1296,8 +1302,8 @@ TEST_F(ConstantRangeTest, SDiv) { } // If there is a non-full signed envelope, that should be the result. - APInt SMin(Bits, Results.find_first() - Bias); - APInt SMax(Bits, Results.find_last() - Bias); + APInt SMin(Bits, Results.find_first() - Bias, true); + APInt SMax(Bits, Results.find_last() - Bias, true); ConstantRange Envelope = ConstantRange::getNonEmpty(SMin, SMax + 1); if (!Envelope.isFullSet()) { EXPECT_EQ(Envelope, CR); @@ -1316,8 +1322,8 @@ TEST_F(ConstantRangeTest, SDiv) { --LastPos; } - APInt WMax(Bits, LastNeg); - APInt WMin(Bits, LastPos); + APInt WMax(Bits, LastNeg, true); + APInt WMin(Bits, LastPos, true); ConstantRange Wrapped = ConstantRange::getNonEmpty(WMin, WMax + 1); EXPECT_EQ(Wrapped, CR); }); @@ -1370,8 +1376,8 @@ TEST_F(ConstantRangeTest, SRem) { EXPECT_EQ(Full.srem(Full), ConstantRange(APInt::getSignedMinValue(16) + 1, APInt::getSignedMinValue(16))); - ConstantRange PosMod(APInt(16, 10), APInt(16, 21)); // [10, 20] - ConstantRange NegMod(APInt(16, -20), APInt(16, -9)); // [-20, -10] + ConstantRange PosMod(APInt(16, 10), APInt(16, 21)); // [10, 20] + ConstantRange NegMod(APInt(16, -20, true), APInt(16, -9, true)); // [-20, -10] ConstantRange IntMinMod(APInt::getSignedMinValue(16)); ConstantRange Expected(16, true); @@ -1381,12 +1387,12 @@ TEST_F(ConstantRangeTest, SRem) { Expected = ConstantRange(APInt(16, 0), APInt(16, 20)); EXPECT_EQ(PosLargeLHS.srem(PosMod), Expected); EXPECT_EQ(PosLargeLHS.srem(NegMod), Expected); - ConstantRange NegLargeLHS(APInt(16, -40), APInt(16, 1)); - Expected = ConstantRange(APInt(16, -19), APInt(16, 1)); + ConstantRange NegLargeLHS(APInt(16, -40, true), APInt(16, 1)); + Expected = ConstantRange(APInt(16, -19, true), APInt(16, 1)); EXPECT_EQ(NegLargeLHS.srem(PosMod), Expected); EXPECT_EQ(NegLargeLHS.srem(NegMod), Expected); - ConstantRange PosNegLargeLHS(APInt(16, -32), APInt(16, 38)); - Expected = ConstantRange(APInt(16, -19), APInt(16, 20)); + ConstantRange PosNegLargeLHS(APInt(16, -32, true), APInt(16, 38)); + Expected = ConstantRange(APInt(16, -19, true), APInt(16, 20)); EXPECT_EQ(PosNegLargeLHS.srem(PosMod), Expected); EXPECT_EQ(PosNegLargeLHS.srem(NegMod), Expected); @@ -1395,11 +1401,11 @@ TEST_F(ConstantRangeTest, SRem) { EXPECT_EQ(PosLHS.srem(PosMod), PosLHS); EXPECT_EQ(PosLHS.srem(NegMod), PosLHS); EXPECT_EQ(PosLHS.srem(IntMinMod), PosLHS); - ConstantRange NegLHS(APInt(16, -15), APInt(16, 1)); + ConstantRange NegLHS(APInt(16, -15, true), APInt(16, 1)); EXPECT_EQ(NegLHS.srem(PosMod), NegLHS); EXPECT_EQ(NegLHS.srem(NegMod), NegLHS); EXPECT_EQ(NegLHS.srem(IntMinMod), NegLHS); - ConstantRange PosNegLHS(APInt(16, -12), APInt(16, 18)); + ConstantRange PosNegLHS(APInt(16, -12, true), APInt(16, 18)); EXPECT_EQ(PosNegLHS.srem(PosMod), PosNegLHS); EXPECT_EQ(PosNegLHS.srem(NegMod), PosNegLHS); EXPECT_EQ(PosNegLHS.srem(IntMinMod), PosNegLHS); @@ -1409,11 +1415,11 @@ TEST_F(ConstantRangeTest, SRem) { EXPECT_EQ(PosSmallLHS.srem(PosMod), PosSmallLHS); EXPECT_EQ(PosSmallLHS.srem(NegMod), PosSmallLHS); EXPECT_EQ(PosSmallLHS.srem(IntMinMod), PosSmallLHS); - ConstantRange NegSmallLHS(APInt(16, -7), APInt(16, -2)); + ConstantRange NegSmallLHS(APInt(16, -7, true), APInt(16, -2, true)); EXPECT_EQ(NegSmallLHS.srem(PosMod), NegSmallLHS); EXPECT_EQ(NegSmallLHS.srem(NegMod), NegSmallLHS); EXPECT_EQ(NegSmallLHS.srem(IntMinMod), NegSmallLHS); - ConstantRange PosNegSmallLHS(APInt(16, -3), APInt(16, 8)); + ConstantRange PosNegSmallLHS(APInt(16, -3, true), APInt(16, 8)); EXPECT_EQ(PosNegSmallLHS.srem(PosMod), PosNegSmallLHS); EXPECT_EQ(PosNegSmallLHS.srem(NegMod), PosNegSmallLHS); EXPECT_EQ(PosNegSmallLHS.srem(IntMinMod), PosNegSmallLHS); @@ -1527,9 +1533,9 @@ TEST_F(ConstantRangeTest, Ashr) { APInt(16, (0xaaa >> 0xa) + 1))); EXPECT_EQ(Some.ashr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa))); EXPECT_EQ(Wrap.ashr(Wrap), Full); - ConstantRange Neg(APInt(16, 0xf3f0, true), APInt(16, 0xf7f8, true)); - EXPECT_EQ(Neg.ashr(Small), ConstantRange(APInt(16, 0xfffc, true), - APInt(16, 0xfffe, true))); + ConstantRange Neg(APInt(16, 0xf3f0), APInt(16, 0xf7f8)); + EXPECT_EQ(Neg.ashr(Small), + ConstantRange(APInt(16, 0xfffc), APInt(16, 0xfffe))); } TEST(ConstantRange, MakeAllowedICmpRegion) { @@ -1572,23 +1578,23 @@ TEST(ConstantRange, MakeSatisfyingICmpRegion) { UnsignedSample), ConstantRange(APInt(8, 199), APInt(8, 0))); - ConstantRange SignedSample(APInt(8, -5), APInt(8, 5)); + ConstantRange SignedSample(APInt(8, -5, true), APInt(8, 5)); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLT, SignedSample), - ConstantRange(APInt(8, -128), APInt(8, -5))); + ConstantRange(APInt(8, -128, true), APInt(8, -5, true))); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLE, SignedSample), - ConstantRange(APInt(8, -128), APInt(8, -4))); + ConstantRange(APInt(8, -128, true), APInt(8, -4, true))); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGT, SignedSample), - ConstantRange(APInt(8, 5), APInt(8, -128))); + ConstantRange(APInt(8, 5), APInt(8, -128, true))); EXPECT_EQ( ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGE, SignedSample), - ConstantRange(APInt(8, 4), APInt(8, -128))); + ConstantRange(APInt(8, 4), APInt(8, -128, true))); } void ICmpTestImpl(CmpInst::Predicate Pred) { @@ -1610,7 +1616,7 @@ TEST(ConstantRange, ICmp) { } TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { - const int IntMin4Bits = 8; + const int IntMin4Bits = -8; const int IntMax4Bits = 7; typedef OverflowingBinaryOperator OBO; @@ -1719,7 +1725,7 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { Instruction::Sub, OneToFive, OBO::NoUnsignedWrap), ConstantRange(APInt::getMinValue(32) + 5, APInt::getMinValue(32))); - ConstantRange MinusFiveToMinusTwo(APInt(32, -5), APInt(32, -1)); + ConstantRange MinusFiveToMinusTwo(APInt(32, -5, true), APInt(32, -1, true)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Add, MinusFiveToMinusTwo, OBO::NoSignedWrap), ConstantRange(APInt::getSignedMinValue(32) + 5, @@ -1733,10 +1739,9 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { APInt::getSignedMaxValue(32) - 4)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Sub, MinusFiveToMinusTwo, OBO::NoUnsignedWrap), - ConstantRange(APInt::getMaxValue(32) - 1, - APInt::getMinValue(32))); + ConstantRange(APInt::getMaxValue(32) - 1, APInt::getMinValue(32))); - ConstantRange MinusOneToOne(APInt(32, -1), APInt(32, 2)); + ConstantRange MinusOneToOne(APInt(32, -1, true), APInt(32, 2)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Add, MinusOneToOne, OBO::NoSignedWrap), ConstantRange(APInt::getSignedMinValue(32) + 1, @@ -1784,7 +1789,7 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { ConstantRange(APInt(32, 0), APInt(32, 1) + 1)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap), - ConstantRange(APInt(32, -1), APInt(32, 0) + 1)); + ConstantRange(APInt(32, -1, true), APInt(32, 0) + 1)); EXPECT_EQ( ConstantRange::makeGuaranteedNoWrapRegion( @@ -1805,34 +1810,36 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) { Instruction::Shl, IllegalShAmt, OBO::NoSignedWrap), ConstantRange::getFull(32)); - EXPECT_EQ( - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1), - OBO::NoUnsignedWrap), - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1), - OBO::NoUnsignedWrap)); - EXPECT_EQ( - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1), - OBO::NoSignedWrap), - ConstantRange::makeGuaranteedNoWrapRegion( - Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1), - OBO::NoSignedWrap)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), + OBO::NoUnsignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, 0), APInt(32, 16) + 1), + OBO::NoUnsignedWrap)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), + OBO::NoSignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, 0), APInt(32, 16) + 1), + OBO::NoSignedWrap)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Shl, - ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), OBO::NoUnsignedWrap), ConstantRange(APInt(32, 0), APInt(32, 65535) + 1)); EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Shl, - ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + ConstantRange(APInt(32, -32, true), APInt(32, 16) + 1), OBO::NoSignedWrap), - ConstantRange(APInt(32, -32768), APInt(32, 32767) + 1)); + ConstantRange(APInt(32, -32768, true), APInt(32, 32767) + 1)); } -template +template void TestNoWrapRegionExhaustive(Instruction::BinaryOps BinOp, unsigned NoWrapKind, Fn OverflowFn) { for (unsigned Bits : {1, 5}) { @@ -1997,14 +2004,15 @@ TEST(ConstantRange, GetEquivalentICmp) { EXPECT_EQ(Pred, CmpInst::ICMP_NE); EXPECT_EQ(RHS, APInt(32, 0)); - EXPECT_TRUE(ConstantRange(APInt(32, -1)).getEquivalentICmp(Pred, RHS)); + EXPECT_TRUE(ConstantRange(APInt(32, -1, true)).getEquivalentICmp(Pred, RHS)); EXPECT_EQ(Pred, CmpInst::ICMP_EQ); - EXPECT_EQ(RHS, APInt(32, -1)); + EXPECT_EQ(RHS, APInt(32, -1, true)); - EXPECT_TRUE( - ConstantRange(APInt(32, -1)).inverse().getEquivalentICmp(Pred, RHS)); + EXPECT_TRUE(ConstantRange(APInt(32, -1, true)) + .inverse() + .getEquivalentICmp(Pred, RHS)); EXPECT_EQ(Pred, CmpInst::ICMP_NE); - EXPECT_EQ(RHS, APInt(32, -1)); + EXPECT_EQ(RHS, APInt(32, -1, true)); EnumerateInterestingConstantRanges([](const ConstantRange &CR) { unsigned Bits = CR.getBitWidth(); From 356dc368c1f808baa40b3ed3eebd13b131ce0e5b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 5 Sep 2024 15:19:16 +0200 Subject: [PATCH 024/203] [PatternMatchTest] Use APInt::getAllOnes() (NFC) Split out from https://github.com/llvm/llvm-project/pull/80309 to avoid assertion failures in the future. --- llvm/unittests/IR/PatternMatch.cpp | 202 ++++++++++++++--------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/llvm/unittests/IR/PatternMatch.cpp b/llvm/unittests/IR/PatternMatch.cpp index 309fcc93996b..f2b15f411fcb 100644 --- a/llvm/unittests/IR/PatternMatch.cpp +++ b/llvm/unittests/IR/PatternMatch.cpp @@ -71,7 +71,7 @@ TEST_F(PatternMatchTest, SpecificIntEQ) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 0)) @@ -93,15 +93,15 @@ TEST_F(PatternMatchTest, SpecificIntEQ) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntNE) { @@ -110,7 +110,7 @@ TEST_F(PatternMatchTest, SpecificIntNE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 0)) @@ -132,15 +132,15 @@ TEST_F(PatternMatchTest, SpecificIntNE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_NE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntUGT) { @@ -149,7 +149,7 @@ TEST_F(PatternMatchTest, SpecificIntUGT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 0)) @@ -171,15 +171,15 @@ TEST_F(PatternMatchTest, SpecificIntUGT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SignbitZeroChecks) { @@ -187,7 +187,7 @@ TEST_F(PatternMatchTest, SignbitZeroChecks) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE(m_Negative().match(NegOne)); EXPECT_FALSE(m_NonNegative().match(NegOne)); @@ -211,7 +211,7 @@ TEST_F(PatternMatchTest, SpecificIntUGE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 0)) @@ -233,15 +233,15 @@ TEST_F(PatternMatchTest, SpecificIntUGE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_UGE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntULT) { @@ -250,7 +250,7 @@ TEST_F(PatternMatchTest, SpecificIntULT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 0)) @@ -272,15 +272,15 @@ TEST_F(PatternMatchTest, SpecificIntULT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntULE) { @@ -289,7 +289,7 @@ TEST_F(PatternMatchTest, SpecificIntULE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 0)) @@ -311,15 +311,15 @@ TEST_F(PatternMatchTest, SpecificIntULE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_ULE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSGT) { @@ -328,7 +328,7 @@ TEST_F(PatternMatchTest, SpecificIntSGT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 0)) @@ -350,15 +350,15 @@ TEST_F(PatternMatchTest, SpecificIntSGT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSGE) { @@ -367,7 +367,7 @@ TEST_F(PatternMatchTest, SpecificIntSGE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 0)) @@ -389,15 +389,15 @@ TEST_F(PatternMatchTest, SpecificIntSGE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SGE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSLT) { @@ -406,7 +406,7 @@ TEST_F(PatternMatchTest, SpecificIntSLT) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_FALSE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 0)) @@ -428,15 +428,15 @@ TEST_F(PatternMatchTest, SpecificIntSLT) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)) - .match(One)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLT, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, SpecificIntSLE) { @@ -445,7 +445,7 @@ TEST_F(PatternMatchTest, SpecificIntSLE) { Value *Zero = ConstantInt::get(IntTy, 0); Value *One = ConstantInt::get(IntTy, 1); - Value *NegOne = ConstantInt::get(IntTy, -1); + Value *NegOne = Constant::getAllOnesValue(IntTy); EXPECT_TRUE( m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 0)) @@ -467,15 +467,15 @@ TEST_F(PatternMatchTest, SpecificIntSLE) { m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, 1)) .match(NegOne)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)) - .match(Zero)); - EXPECT_FALSE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)) - .match(One)); - EXPECT_TRUE( - m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, APInt(BitWidth, -1)) - .match(NegOne)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, + APInt::getAllOnes(BitWidth)) + .match(Zero)); + EXPECT_FALSE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, + APInt::getAllOnes(BitWidth)) + .match(One)); + EXPECT_TRUE(m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_SLE, + APInt::getAllOnes(BitWidth)) + .match(NegOne)); } TEST_F(PatternMatchTest, Unless) { From 00794e628a75de4195bfb7171d6e86c29dba1e43 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 27 Aug 2024 15:48:59 +0200 Subject: [PATCH 025/203] [LoopUnrollAnalyzer] Store SimplifiedAddress offset as APInt (NFC) --- llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h | 2 +- llvm/lib/Analysis/LoopUnrollAnalyzer.cpp | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h b/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h index eada6a647763..12b906ec9dd5 100644 --- a/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h +++ b/llvm/include/llvm/Analysis/LoopUnrollAnalyzer.h @@ -44,7 +44,7 @@ class UnrolledInstAnalyzer : private InstVisitor { friend class InstVisitor; struct SimplifiedAddress { Value *Base = nullptr; - ConstantInt *Offset = nullptr; + APInt Offset; }; public: diff --git a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp index 7b6ca4d711fc..34fbab6a11d6 100644 --- a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp +++ b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp @@ -64,7 +64,7 @@ bool UnrolledInstAnalyzer::simplifyInstWithSCEV(Instruction *I) { return false; SimplifiedAddress Address; Address.Base = Base->getValue(); - Address.Offset = Offset->getValue(); + Address.Offset = Offset->getAPInt(); SimplifiedAddresses[I] = Address; return false; } @@ -105,7 +105,7 @@ bool UnrolledInstAnalyzer::visitLoad(LoadInst &I) { auto AddressIt = SimplifiedAddresses.find(AddrOp); if (AddressIt == SimplifiedAddresses.end()) return false; - ConstantInt *SimplifiedAddrOp = AddressIt->second.Offset; + const APInt &SimplifiedAddrOp = AddressIt->second.Offset; auto *GV = dyn_cast(AddressIt->second.Base); // We're only interested in loads that can be completely folded to a @@ -125,9 +125,9 @@ bool UnrolledInstAnalyzer::visitLoad(LoadInst &I) { return false; unsigned ElemSize = CDS->getElementType()->getPrimitiveSizeInBits() / 8U; - if (SimplifiedAddrOp->getValue().getActiveBits() > 64) + if (SimplifiedAddrOp.getActiveBits() > 64) return false; - int64_t SimplifiedAddrOpV = SimplifiedAddrOp->getSExtValue(); + int64_t SimplifiedAddrOpV = SimplifiedAddrOp.getSExtValue(); if (SimplifiedAddrOpV < 0) { // FIXME: For now we conservatively ignore out of bound accesses, but // we're allowed to perform the optimization in this case. @@ -186,10 +186,9 @@ bool UnrolledInstAnalyzer::visitCmpInst(CmpInst &I) { if (SimplifiedRHS != SimplifiedAddresses.end()) { SimplifiedAddress &LHSAddr = SimplifiedLHS->second; SimplifiedAddress &RHSAddr = SimplifiedRHS->second; - if (LHSAddr.Base == RHSAddr.Base) { - LHS = LHSAddr.Offset; - RHS = RHSAddr.Offset; - } + if (LHSAddr.Base == RHSAddr.Base) + return ICmpInst::compare(LHSAddr.Offset, RHSAddr.Offset, + I.getPredicate()); } } } From 09aa0e497cbbd03f93da6a223b1645c5b35d473b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 5 Sep 2024 16:02:50 +0200 Subject: [PATCH 026/203] [ConstantRange] Perform increment on APInt (NFC) This handles the edge case where BitWidth is 1 and doing the increment gets a value that's not valid in that width, while we just want wrap-around. Split out of https://github.com/llvm/llvm-project/pull/80309. --- llvm/lib/Analysis/ValueTracking.cpp | 2 +- llvm/lib/IR/ConstantRange.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 24cafbcebc75..16387b46569c 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -9518,7 +9518,7 @@ static ConstantRange getRangeForIntrinsic(const IntrinsicInst &II) { case Intrinsic::cttz: // Maximum of set/clear bits is the bit width. return ConstantRange::getNonEmpty(APInt::getZero(Width), - APInt(Width, Width + 1)); + APInt(Width, Width) + 1); case Intrinsic::uadd_sat: // uadd.sat(x, C) produces [C, UINT_MAX]. if (match(II.getOperand(0), m_APInt(C)) || diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index 0ead67742280..43cc8664866a 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -1836,7 +1836,7 @@ ConstantRange ConstantRange::ctlz(bool ZeroIsPoison) const { // Zero is either safe or not in the range. The output range is composed by // the result of countLeadingZero of the two extremes. return getNonEmpty(APInt(getBitWidth(), getUnsignedMax().countl_zero()), - APInt(getBitWidth(), getUnsignedMin().countl_zero() + 1)); + APInt(getBitWidth(), getUnsignedMin().countl_zero()) + 1); } static ConstantRange getUnsignedCountTrailingZerosRange(const APInt &Lower, @@ -1895,7 +1895,7 @@ ConstantRange ConstantRange::cttz(bool ZeroIsPoison) const { } if (isFullSet()) - return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1)); + return getNonEmpty(Zero, APInt(BitWidth, BitWidth) + 1); if (!isWrappedSet()) return getUnsignedCountTrailingZerosRange(Lower, Upper); // The range is wrapped. We decompose it into two ranges, [0, Upper) and @@ -1940,7 +1940,7 @@ ConstantRange ConstantRange::ctpop() const { unsigned BitWidth = getBitWidth(); APInt Zero = APInt::getZero(BitWidth); if (isFullSet()) - return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1)); + return getNonEmpty(Zero, APInt(BitWidth, BitWidth) + 1); if (!isWrappedSet()) return getUnsignedPopCountRange(Lower, Upper); // The range is wrapped. We decompose it into two ranges, [0, Upper) and From eeb1525a83880f437edf70f3fc34db274b571b8c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 4 Nov 2024 11:13:50 +0100 Subject: [PATCH 027/203] [ConstantHoist] Fix APInt ctor assertion The result here may require truncation. Fix this by removing the calculateOffsetDiff() helper entirely. As far as I can tell, this code does not actually have to deal with different bitwidths. findBaseConstants() will produce ranges of constants with equal types, which is what maximizeConstantsInRange() will then work on. Fixes assertion reported at: https://github.com/llvm/llvm-project/pull/114539#issuecomment-2453008679 --- .../Transforms/Scalar/ConstantHoisting.cpp | 36 ++++--------------- .../ConstantHoisting/ARM/apint-assert.ll | 18 ++++++++++ 2 files changed, 25 insertions(+), 29 deletions(-) create mode 100644 llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll diff --git a/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp b/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp index 4a6dedc93d30..61c6193bfd9f 100644 --- a/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp +++ b/llvm/lib/Transforms/Scalar/ConstantHoisting.cpp @@ -533,25 +533,6 @@ void ConstantHoistingPass::collectConstantCandidates(Function &Fn) { } } -// This helper function is necessary to deal with values that have different -// bit widths (APInt Operator- does not like that). If the value cannot be -// represented in uint64 we return an "empty" APInt. This is then interpreted -// as the value is not in range. -static std::optional calculateOffsetDiff(const APInt &V1, - const APInt &V2) { - std::optional Res; - unsigned BW = V1.getBitWidth() > V2.getBitWidth() ? - V1.getBitWidth() : V2.getBitWidth(); - uint64_t LimVal1 = V1.getLimitedValue(); - uint64_t LimVal2 = V2.getLimitedValue(); - - if (LimVal1 == ~0ULL || LimVal2 == ~0ULL) - return Res; - - uint64_t Diff = LimVal1 - LimVal2; - return APInt(BW, Diff, true); -} - // From a list of constants, one needs to picked as the base and the other // constants will be transformed into an offset from that base constant. The // question is which we can pick best? For example, consider these constants @@ -608,16 +589,13 @@ ConstantHoistingPass::maximizeConstantsInRange(ConstCandVecType::iterator S, LLVM_DEBUG(dbgs() << "Cost: " << Cost << "\n"); for (auto C2 = S; C2 != E; ++C2) { - std::optional Diff = calculateOffsetDiff( - C2->ConstInt->getValue(), ConstCand->ConstInt->getValue()); - if (Diff) { - const InstructionCost ImmCosts = - TTI->getIntImmCodeSizeCost(Opcode, OpndIdx, *Diff, Ty); - Cost -= ImmCosts; - LLVM_DEBUG(dbgs() << "Offset " << *Diff << " " - << "has penalty: " << ImmCosts << "\n" - << "Adjusted cost: " << Cost << "\n"); - } + APInt Diff = C2->ConstInt->getValue() - ConstCand->ConstInt->getValue(); + const InstructionCost ImmCosts = + TTI->getIntImmCodeSizeCost(Opcode, OpndIdx, Diff, Ty); + Cost -= ImmCosts; + LLVM_DEBUG(dbgs() << "Offset " << Diff << " " + << "has penalty: " << ImmCosts << "\n" + << "Adjusted cost: " << Cost << "\n"); } } LLVM_DEBUG(dbgs() << "Cumulative cost: " << Cost << "\n"); diff --git a/llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll b/llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll new file mode 100644 index 000000000000..6f165f53a57b --- /dev/null +++ b/llvm/test/Transforms/ConstantHoisting/ARM/apint-assert.ll @@ -0,0 +1,18 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=consthoist -mtriple=armv4t-unknown-linux-gnueabi < %s | FileCheck %s + +define i1 @test(i32 %arg) optsize { +; CHECK-LABEL: define i1 @test( +; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CONST:%.*]] = bitcast i32 380633088 to i32 +; CHECK-NEXT: [[CONST_MAT:%.*]] = add i32 [[CONST]], -381681664 +; CHECK-NEXT: [[SHR_MASK:%.*]] = and i32 [[ARG]], [[CONST_MAT]] +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SHR_MASK]], [[CONST]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %shr.mask = and i32 %arg, -1048576 + %cmp = icmp eq i32 %shr.mask, 380633088 + ret i1 %cmp +} From 0fd035886b51bdbced95437b014d8f0098fef202 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 5 Nov 2024 10:01:33 +0100 Subject: [PATCH 028/203] [CVP] Fix APInt ctor assertion with i1 urem This is creating an APInt with value 2, which may not be well-defined for i1 values. Fix this by replacing the Y.umul_sat(2) with Y.uadd_sat(Y). --- .../lib/Transforms/Scalar/CorrelatedValuePropagation.cpp | 4 +--- llvm/test/Transforms/CorrelatedValuePropagation/urem.ll | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp index 95de8eceb6be..4f8030b0dc79 100644 --- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp +++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @@ -832,9 +832,7 @@ static bool expandUDivOrURem(BinaryOperator *Instr, const ConstantRange &XCR, // Even if we don't know X's range, the divisor may be so large, X can't ever // be 2x larger than that. I.e. if divisor is always negative. - if (!XCR.icmp(ICmpInst::ICMP_ULT, - YCR.umul_sat(APInt(YCR.getBitWidth(), 2))) && - !YCR.isAllNegative()) + if (!XCR.icmp(ICmpInst::ICMP_ULT, YCR.uadd_sat(YCR)) && !YCR.isAllNegative()) return false; IRBuilder<> B(Instr); diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll b/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll index ec6461e29f19..e69deaa73d73 100644 --- a/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/urem.ll @@ -462,4 +462,13 @@ join: ret i8 %res } +define i1 @urem_i1() { +; CHECK-LABEL: @urem_i1( +; CHECK-NEXT: [[REM:%.*]] = urem i1 false, false +; CHECK-NEXT: ret i1 [[REM]] +; + %rem = urem i1 false, false + ret i1 %rem +} + declare void @use(i1) From fc86a978fd2dc27781398730e9b204529400e686 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 12 Nov 2024 17:41:08 +0100 Subject: [PATCH 029/203] [IR] Add helper for comparing KnownBits with IR predicate (NFC) (#115878) Add `ICmpInst::compare()` overload accepting `KnownBits`, similar to the existing one accepting `APInt`. This is not directly part of KnownBits (or APInt) for layering reasons. --- llvm/include/llvm/IR/Instructions.h | 6 ++++ .../lib/CodeGen/GlobalISel/CombinerHelper.cpp | 35 +------------------ llvm/lib/IR/Instructions.cpp | 30 ++++++++++++++++ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index c07fee58e4bd..c93e7fe665a7 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -48,6 +48,7 @@ class APInt; class BasicBlock; class ConstantInt; class DataLayout; +struct KnownBits; class StringRef; class Type; class Value; @@ -1279,6 +1280,11 @@ class ICmpInst: public CmpInst { static bool compare(const APInt &LHS, const APInt &RHS, ICmpInst::Predicate Pred); + /// Return result of `LHS Pred RHS`, if it can be determined from the + /// KnownBits. Otherwise return nullopt. + static std::optional compare(const KnownBits &LHS, const KnownBits &RHS, + ICmpInst::Predicate Pred); + // Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const Instruction *I) { return I->getOpcode() == Instruction::ICmp; diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp index e77ea3e76ad7..d70c2fe4f708 100644 --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -4369,40 +4369,7 @@ bool CombinerHelper::matchICmpToTrueFalseKnownBits(MachineInstr &MI, if (!KnownVal) { auto KnownLHS = KB->getKnownBits(MI.getOperand(2).getReg()); - switch (Pred) { - default: - llvm_unreachable("Unexpected G_ICMP predicate?"); - case CmpInst::ICMP_EQ: - KnownVal = KnownBits::eq(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_NE: - KnownVal = KnownBits::ne(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SGE: - KnownVal = KnownBits::sge(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SGT: - KnownVal = KnownBits::sgt(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SLE: - KnownVal = KnownBits::sle(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_SLT: - KnownVal = KnownBits::slt(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_UGE: - KnownVal = KnownBits::uge(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_UGT: - KnownVal = KnownBits::ugt(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_ULE: - KnownVal = KnownBits::ule(KnownLHS, KnownRHS); - break; - case CmpInst::ICMP_ULT: - KnownVal = KnownBits::ult(KnownLHS, KnownRHS); - break; - } + KnownVal = ICmpInst::compare(KnownLHS, KnownRHS, Pred); } if (!KnownVal) diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 7a8cf8c23049..f271063d6796 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/CheckedArithmetic.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/ModRef.h" #include "llvm/Support/TypeSize.h" @@ -3826,6 +3827,35 @@ bool FCmpInst::compare(const APFloat &LHS, const APFloat &RHS, } } +std::optional ICmpInst::compare(const KnownBits &LHS, + const KnownBits &RHS, + ICmpInst::Predicate Pred) { + switch (Pred) { + case ICmpInst::ICMP_EQ: + return KnownBits::eq(LHS, RHS); + case ICmpInst::ICMP_NE: + return KnownBits::ne(LHS, RHS); + case ICmpInst::ICMP_UGE: + return KnownBits::uge(LHS, RHS); + case ICmpInst::ICMP_UGT: + return KnownBits::ugt(LHS, RHS); + case ICmpInst::ICMP_ULE: + return KnownBits::ule(LHS, RHS); + case ICmpInst::ICMP_ULT: + return KnownBits::ult(LHS, RHS); + case ICmpInst::ICMP_SGE: + return KnownBits::sge(LHS, RHS); + case ICmpInst::ICMP_SGT: + return KnownBits::sgt(LHS, RHS); + case ICmpInst::ICMP_SLE: + return KnownBits::sle(LHS, RHS); + case ICmpInst::ICMP_SLT: + return KnownBits::slt(LHS, RHS); + default: + llvm_unreachable("Unexpected non-integer predicate."); + } +} + CmpInst::Predicate CmpInst::getFlippedSignednessPredicate(Predicate pred) { assert(CmpInst::isRelational(pred) && "Call only with non-equality predicates!"); From 7419030b9253fd9c382eca6dd371e3aafe172b80 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 19 Nov 2024 15:16:26 +0100 Subject: [PATCH 030/203] [InstCombine] Fix APInt ctor assertion The (extended) bit width might not fit into the (non-extended) type, resulting in an incorrect truncation of the compared value. Fix this by using m_SpecificInt(), which is both simpler and handles this correctly. Fixes the assertion failure reported in: https://github.com/llvm/llvm-project/pull/114539#issuecomment-2485799395 --- .../Transforms/InstCombine/InstCombineAddSub.cpp | 10 +++------- ...iable-length-signext-after-high-bit-extract.ll | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp index 0a55f4762fdf..977fb778cdfc 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1338,14 +1338,10 @@ Instruction *InstCombinerImpl:: // low bits to skip = shift bitwidth - high bits to extract // The shift amount itself may be extended, and we need to look past zero-ext // when matching NBits, that will matter for matching later. - Constant *C; Value *NBits; - if (!match( - LowBitsToSkip, - m_ZExtOrSelf(m_Sub(m_Constant(C), m_ZExtOrSelf(m_Value(NBits))))) || - !match(C, m_SpecificInt_ICMP(ICmpInst::Predicate::ICMP_EQ, - APInt(C->getType()->getScalarSizeInBits(), - X->getType()->getScalarSizeInBits())))) + if (!match(LowBitsToSkip, + m_ZExtOrSelf(m_Sub(m_SpecificInt(XTy->getScalarSizeInBits()), + m_ZExtOrSelf(m_Value(NBits)))))) return nullptr; // Sign-extending value can be zero-extended if we `sub`tract it, diff --git a/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll b/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll index 5569af452de4..b00b3a289de4 100644 --- a/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll +++ b/llvm/test/Transforms/InstCombine/conditional-variable-length-signext-after-high-bit-extract.ll @@ -1137,3 +1137,18 @@ define i32 @n290_or_with_wrong_magic(i32 %data, i32 %nbits) { %signextended = or i32 %high_bits_extracted, %magic ret i32 %signextended } + +define i32 @bitwidth_does_not_fit(i3 %arg) { +; CHECK-LABEL: @bitwidth_does_not_fit( +; CHECK-NEXT: [[NEG:%.*]] = sub i3 0, [[ARG:%.*]] +; CHECK-NEXT: [[NEG_EXT:%.*]] = zext i3 [[NEG]] to i32 +; CHECK-NEXT: [[SHR:%.*]] = lshr i32 1, [[NEG_EXT]] +; CHECK-NEXT: [[INC:%.*]] = add nuw nsw i32 [[SHR]], 1 +; CHECK-NEXT: ret i32 [[INC]] +; + %neg = sub i3 0, %arg + %neg.ext = zext i3 %neg to i32 + %shr = lshr i32 1, %neg.ext + %inc = add i32 %shr, 1 + ret i32 %inc +} From 90a15272573182c017ba33987922b1a8247c6dbb Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 7 Jan 2025 12:10:44 +0100 Subject: [PATCH 031/203] [InstCombine] Compute result directly on APInts If the bitwidth is 2 and we add two 1s, the result may overflow. This is fine in terms of correctness, but triggers the APInt ctor assertion. Fix this by performing the calculation directly on APInts. Fixes the issue reported in: https://github.com/llvm/llvm-project/pull/114539#issuecomment-2574845003 --- .../Transforms/InstCombine/InstCombineCompares.cpp | 8 ++++---- llvm/test/Transforms/InstCombine/icmp-add.ll | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 626903bf1b94..a8854d7eeb36 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -3035,12 +3035,12 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp, unsigned BW = C.getBitWidth(); std::bitset<4> Table; auto ComputeTable = [&](bool Op0Val, bool Op1Val) { - int Res = 0; + APInt Res(BW, 0); if (Op0Val) - Res += isa(Ext0) ? 1 : -1; + Res += APInt(BW, isa(Ext0) ? 1 : -1, /*isSigned=*/true); if (Op1Val) - Res += isa(Ext1) ? 1 : -1; - return ICmpInst::compare(APInt(BW, Res, true), C, Pred); + Res += APInt(BW, isa(Ext1) ? 1 : -1, /*isSigned=*/true); + return ICmpInst::compare(Res, C, Pred); }; Table[0] = ComputeTable(false, false); diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index baa6f3d51a40..b4256ff6ad55 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -79,6 +79,19 @@ bb: ret i1 %i4 } +define i1 @cvt_icmp_0_zext_plus_zext_eq_i2(i1 %a, i1 %b) { +; CHECK-LABEL: @cvt_icmp_0_zext_plus_zext_eq_i2( +; CHECK-NEXT: [[TMP1:%.*]] = or i1 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[TMP1]], true +; CHECK-NEXT: ret i1 [[CMP]] +; + %a.ext = zext i1 %a to i2 + %b.ext = zext i1 %b to i2 + %add = add i2 %a.ext, %b.ext + %cmp = icmp eq i2 %add, 0 + ret i1 %cmp +} + define i1 @cvt_icmp_1_zext_plus_zext_eq(i1 %arg, i1 %arg1) { ; CHECK-LABEL: @cvt_icmp_1_zext_plus_zext_eq( ; CHECK-NEXT: bb: From 3339f205a7fb5db56be37591c62e7ebbbb42a750 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 27 Aug 2024 16:36:41 +0200 Subject: [PATCH 032/203] [LoopUnrollAnalyzer] Fix icmp simplification Fix a bug I introduced in 721fdf1c9a73269280a504cbba847f4979512b66. --- llvm/lib/Analysis/LoopUnrollAnalyzer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp index 34fbab6a11d6..9cbdeb1994ed 100644 --- a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp +++ b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp @@ -186,9 +186,12 @@ bool UnrolledInstAnalyzer::visitCmpInst(CmpInst &I) { if (SimplifiedRHS != SimplifiedAddresses.end()) { SimplifiedAddress &LHSAddr = SimplifiedLHS->second; SimplifiedAddress &RHSAddr = SimplifiedRHS->second; - if (LHSAddr.Base == RHSAddr.Base) - return ICmpInst::compare(LHSAddr.Offset, RHSAddr.Offset, - I.getPredicate()); + if (LHSAddr.Base == RHSAddr.Base) { + bool Res = ICmpInst::compare(LHSAddr.Offset, RHSAddr.Offset, + I.getPredicate()); + SimplifiedValues[&I] = ConstantInt::getBool(I.getType(), Res); + return true; + } } } } From 4e86045bc7f6cefb42415201e0d6d557adad4138 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 28 Aug 2024 12:11:28 +0200 Subject: [PATCH 033/203] [LoopUnrollAnalyzer] Don't simplify signed pointer comparison We're generally not able to simplify signed pointer comparisons (because we don't have no-wrap flags that would permit it), so we shouldn't pretend that we can in the cost model. The unsigned comparison case is also not modelled correctly, as explained in the added comment. As this is a cost model inaccuracy at worst, I'm leaving it alone for now. --- llvm/lib/Analysis/LoopUnrollAnalyzer.cpp | 7 ++++++- llvm/unittests/Analysis/UnrollAnalyzerTest.cpp | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp index 9cbdeb1994ed..ff3bb0596c4a 100644 --- a/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp +++ b/llvm/lib/Analysis/LoopUnrollAnalyzer.cpp @@ -179,7 +179,7 @@ bool UnrolledInstAnalyzer::visitCmpInst(CmpInst &I) { if (Value *SimpleRHS = SimplifiedValues.lookup(RHS)) RHS = SimpleRHS; - if (!isa(LHS) && !isa(RHS)) { + if (!isa(LHS) && !isa(RHS) && !I.isSigned()) { auto SimplifiedLHS = SimplifiedAddresses.find(LHS); if (SimplifiedLHS != SimplifiedAddresses.end()) { auto SimplifiedRHS = SimplifiedAddresses.find(RHS); @@ -187,6 +187,11 @@ bool UnrolledInstAnalyzer::visitCmpInst(CmpInst &I) { SimplifiedAddress &LHSAddr = SimplifiedLHS->second; SimplifiedAddress &RHSAddr = SimplifiedRHS->second; if (LHSAddr.Base == RHSAddr.Base) { + // FIXME: This is only correct for equality predicates. For + // unsigned predicates, this only holds if we have nowrap flags, + // which we don't track (for nuw it's valid as-is, for nusw it + // requires converting the predicated to signed). As this is used only + // for cost modelling, this is not a correctness issue. bool Res = ICmpInst::compare(LHSAddr.Offset, RHSAddr.Offset, I.getPredicate()); SimplifiedValues[&I] = ConstantInt::getBool(I.getType(), Res); diff --git a/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp b/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp index 721d67f22f2f..0ff08d19957e 100644 --- a/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp +++ b/llvm/unittests/Analysis/UnrollAnalyzerTest.cpp @@ -221,6 +221,8 @@ TEST(UnrollAnalyzerTest, PtrCmpSimplifications) { " %iv.0 = phi i8* [ %a, %entry ], [ %iv.1, %loop.body ]\n" " %iv2.0 = phi i8* [ %start.iv2, %entry ], [ %iv2.1, %loop.body ]\n" " %cmp = icmp eq i8* %iv2.0, %iv.0\n" + " %cmp2 = icmp slt i8* %iv2.0, %iv.0\n" + " %cmp3 = icmp ult i8* %iv2.0, %iv.0\n" " %iv.1 = getelementptr inbounds i8, i8* %iv.0, i64 1\n" " %iv2.1 = getelementptr inbounds i8, i8* %iv2.0, i64 1\n" " %exitcond = icmp ne i8* %iv.1, %limit\n" @@ -242,12 +244,21 @@ TEST(UnrollAnalyzerTest, PtrCmpSimplifications) { BasicBlock::iterator BBI = Header->begin(); std::advance(BBI, 2); - Instruction *Y1 = &*BBI; + Instruction *Cmp1 = &*BBI++; + Instruction *Cmp2 = &*BBI++; + Instruction *Cmp3 = &*BBI++; // Check simplification expected on the 5th iteration. // Check that "%cmp = icmp eq i8* %iv2.0, %iv.0" is simplified to 0. - auto I1 = SimplifiedValuesVector[5].find(Y1); + auto I1 = SimplifiedValuesVector[5].find(Cmp1); EXPECT_TRUE(I1 != SimplifiedValuesVector[5].end()); EXPECT_EQ(cast((*I1).second)->getZExtValue(), 0U); + // Check that "%cmp2 = icmp slt i8* %iv2.0, %iv.0" does not simplify + auto I2 = SimplifiedValuesVector[5].find(Cmp2); + EXPECT_TRUE(I2 == SimplifiedValuesVector[5].end()); + // Check that "%cmp3 = icmp ult i8* %iv2.0, %iv.0" is simplified to 0. + auto I3 = SimplifiedValuesVector[5].find(Cmp3); + EXPECT_TRUE(I3 != SimplifiedValuesVector[5].end()); + EXPECT_EQ(cast((*I1).second)->getZExtValue(), 0U); } TEST(UnrollAnalyzerTest, CastSimplifications) { From 091ab03666cd576d15b38367b5f293c424b60610 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 18 Oct 2024 09:23:24 +0200 Subject: [PATCH 034/203] [APInt] Enable APInt ctor assertion by default (#112670) This enables the assertion introduced in https://github.com/llvm/llvm-project/pull/106524, which checks that the value passed to the APInt constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in https://github.com/llvm/llvm-project/pull/80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true. --- llvm/include/llvm/ADT/APInt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 3a6bad335b43..edb03523f1b7 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -111,7 +111,7 @@ class [[nodiscard]] APInt { /// \param implicitTrunc allow implicit truncation of non-zero/sign bits of /// val beyond the range of numBits APInt(unsigned numBits, uint64_t val, bool isSigned = false, - bool implicitTrunc = true) + bool implicitTrunc = false) : BitWidth(numBits) { if (!implicitTrunc) { if (isSigned) { From 6521a8ff39a1ead3bbc0563fa505f38d992ac631 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 4 Sep 2024 22:58:15 +0200 Subject: [PATCH 035/203] [APInt] Replace an assertion in constructor with fatal error The patch replaces assertions in APInt constructor with `report_fatal_error` calls. EraVM compiler is shipped to users w.o. assertions enabled, but zero extensions of negative numbers is a strong signal that a transformation which called the constructor has a bug. In that case we prefer to fail with diagnostic instead of miscompiling a module even if false positives could happen. --- llvm/include/llvm/ADT/APInt.h | 30 +++++++++++++++++++++++------- llvm/unittests/ADT/APIntTest.cpp | 9 +++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index edb03523f1b7..88f296c2dac3 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -16,6 +16,9 @@ #define LLVM_ADT_APINT_H #include "llvm/Support/Compiler.h" +// EVM local begin +#include "llvm/Support/ErrorHandling.h" +// EVM local end #include "llvm/Support/MathExtras.h" #include "llvm/Support/float128.h" #include @@ -116,18 +119,31 @@ class [[nodiscard]] APInt { if (!implicitTrunc) { if (isSigned) { if (BitWidth == 0) { - assert((val == 0 || val == uint64_t(-1)) && - "Value must be 0 or -1 for signed 0-bit APInt"); + // EVM local begin + if (val != 0 && val != uint64_t(-1)) + report_fatal_error( + "APInt error: Value must be 0 or -1 for signed 0-bit APInt"); + // EVM local end } else { - assert(llvm::isIntN(BitWidth, val) && - "Value is not an N-bit signed value"); + // EVM local begin + if (!llvm::isIntN(BitWidth, val)) + report_fatal_error( + "APInt error: Value is not an N-bit signed value"); + // EVM local end } } else { if (BitWidth == 0) { - assert(val == 0 && "Value must be zero for unsigned 0-bit APInt"); + // EVM local begin + if (val != 0) + report_fatal_error( + "APInt error: Value must be zero for unsigned 0-bit APInt"); + // EVM local end } else { - assert(llvm::isUIntN(BitWidth, val) && - "Value is not an N-bit unsigned value"); + // EVM local begin + if (!llvm::isUIntN(BitWidth, val)) + report_fatal_error( + "APInt error: Value is not an N-bit unsigned value"); + // EVM local begin } } } diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index 95d3ed569ea4..1d235e1f3a69 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -3555,4 +3555,13 @@ TEST(APIntTest, TryExt) { ASSERT_EQ(42, APInt(128, -1).trySExtValue().value_or(42)); } +// EVM local begin +#ifdef GTEST_HAS_DEATH_TEST +TEST(APIntTest, DiagnoseZExt) { + EXPECT_DEATH((void)APInt(32, -1, false), + "Value is not an N-bit unsigned value"); +} +#endif +// EVM local end + } // end anonymous namespace From d109ee8abf0f667d16b3f1e9633f9ca2d6142a0e Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Fri, 16 Aug 2024 21:46:39 -0700 Subject: [PATCH 036/203] [SelectionDAG][X86] Use getAllOnesConstant. NFC (#104640) Part of an effort to make getConstant stricter about implicit truncation when converting uint64_t to APInt. --- .../SelectionDAG/LegalizeIntegerTypes.cpp | 2 +- llvm/lib/Target/X86/X86ISelLowering.cpp | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp index 3f11cbd0d07c..30bf62955104 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -4372,7 +4372,7 @@ void DAGTypeLegalizer::ExpandIntRes_MULFIX(SDNode *N, SDValue &Lo, SDValue SatMax, SatMin; SDValue NVTZero = DAG.getConstant(0, dl, NVT); - SDValue NVTNeg1 = DAG.getConstant(-1, dl, NVT); + SDValue NVTNeg1 = DAG.getAllOnesConstant(dl, NVT); EVT BoolNVT = getSetCCResultType(NVT); if (!Signed) { diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 640ef79f5629..975f629c58af 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -23593,7 +23593,7 @@ static SDValue LowerVSETCC(SDValue Op, const X86Subtarget &Subtarget, if (!FlipSigns && !Invert && ISD::isBuildVectorAllOnes(Op1.getNode())) { Op0 = DAG.getBitcast(MVT::v4i32, Op0); - Op1 = DAG.getConstant(-1, dl, MVT::v4i32); + Op1 = DAG.getAllOnesConstant(dl, MVT::v4i32); SDValue GT = DAG.getNode(X86ISD::PCMPGT, dl, MVT::v4i32, Op0, Op1); static const int MaskHi[] = { 1, 1, 3, 3 }; @@ -24385,7 +24385,7 @@ static SDValue LowerSIGN_EXTEND_Mask(SDValue Op, const SDLoc &dl, (Subtarget.hasBWI() && WideEltVT.getSizeInBits() <= 16)) { V = DAG.getNode(Op.getOpcode(), dl, WideVT, In); } else { - SDValue NegOne = DAG.getConstant(-1, dl, WideVT); + SDValue NegOne = DAG.getAllOnesConstant(dl, WideVT); SDValue Zero = DAG.getConstant(0, dl, WideVT); V = DAG.getSelect(dl, WideVT, In, NegOne, Zero); } @@ -26089,7 +26089,7 @@ SDValue X86TargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, Op.getOperand(3)); } else { SDValue GenCF = DAG.getNode(X86ISD::ADD, dl, CFVTs, Op.getOperand(1), - DAG.getConstant(-1, dl, MVT::i8)); + DAG.getAllOnesConstant(dl, MVT::i8)); Res = DAG.getNode(IntrData->Opc0, dl, VTs, Op.getOperand(2), Op.getOperand(3), GenCF.getValue(1)); } @@ -29370,7 +29370,7 @@ static SDValue LowerShiftByScalarVariable(SDValue Op, SelectionDAG &DAG, // Create the mask using vXi16 shifts. For shift-rights we need to move // the upper byte down before splatting the vXi8 mask. - SDValue BitMask = DAG.getConstant(-1, dl, ExtVT); + SDValue BitMask = DAG.getAllOnesConstant(dl, ExtVT); BitMask = getTargetVShiftNode(LogicalX86Op, dl, ExtVT, BitMask, BaseShAmt, BaseShAmtIdx, Subtarget, DAG); if (Opcode != ISD::SHL) @@ -50520,7 +50520,7 @@ static SDValue combineAddOrSubToADCOrSBB(bool IsSub, const SDLoc &DL, EVT VT, // X - SETAE --> adc X, -1 return DAG.getNode(IsSub ? X86ISD::ADC : X86ISD::SBB, DL, DAG.getVTList(VT, MVT::i32), X, - DAG.getConstant(-1, DL, VT), EFLAGS); + DAG.getAllOnesConstant(DL, VT), EFLAGS); } if (CC == X86::COND_BE) { @@ -50541,7 +50541,7 @@ static SDValue combineAddOrSubToADCOrSBB(bool IsSub, const SDLoc &DL, EVT VT, SDValue NewEFLAGS = NewSub.getValue(EFLAGS.getResNo()); return DAG.getNode(IsSub ? X86ISD::ADC : X86ISD::SBB, DL, DAG.getVTList(VT, MVT::i32), X, - DAG.getConstant(-1, DL, VT), NewEFLAGS); + DAG.getAllOnesConstant(DL, VT), NewEFLAGS); } } @@ -50600,7 +50600,7 @@ static SDValue combineAddOrSubToADCOrSBB(bool IsSub, const SDLoc &DL, EVT VT, // X + (Z != 0) --> add X, (zext(setne Z, 0)) --> sbb X, -1, (cmp Z, 1) if (CC == X86::COND_NE) return DAG.getNode(IsSub ? X86ISD::ADC : X86ISD::SBB, DL, VTs, X, - DAG.getConstant(-1ULL, DL, VT), Cmp1.getValue(1)); + DAG.getAllOnesConstant(DL, VT), Cmp1.getValue(1)); // X - (Z == 0) --> sub X, (zext(sete Z, 0)) --> sbb X, 0, (cmp Z, 1) // X + (Z == 0) --> add X, (zext(sete Z, 0)) --> adc X, 0, (cmp Z, 1) @@ -50884,8 +50884,9 @@ static SDValue foldXorTruncShiftIntoCmp(SDNode *N, SelectionDAG &DAG) { const TargetLowering &TLI = DAG.getTargetLoweringInfo(); EVT SetCCResultType = TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), ResultType); - SDValue Cond = DAG.getSetCC(DL, SetCCResultType, ShiftOp, - DAG.getConstant(-1, DL, ShiftOpTy), ISD::SETGT); + SDValue Cond = + DAG.getSetCC(DL, SetCCResultType, ShiftOp, + DAG.getAllOnesConstant(DL, ShiftOpTy), ISD::SETGT); if (SetCCResultType != ResultType) Cond = DAG.getNode(ISD::ZERO_EXTEND, DL, ResultType, Cond); return Cond; From 8bb9e8de2f7b5e7a47f8f4027bcf82b7e56e3d7d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 20 Sep 2024 12:10:56 +0200 Subject: [PATCH 037/203] [UnitTests] Fix APInt signed flags (NFC) This makes unit tests compatible with the assertion added in https://github.com/llvm/llvm-project/pull/106524, by setting the isSigned flag to the correct value or changing how the value is constructed. --- llvm/unittests/ADT/APIntTest.cpp | 180 +++++++++--------- llvm/unittests/ADT/APSIntTest.cpp | 8 +- llvm/unittests/ADT/StringExtrasTest.cpp | 10 +- .../Analysis/ScalarEvolutionTest.cpp | 2 +- llvm/unittests/IR/MetadataTest.cpp | 22 ++- .../Support/DivisionByConstantTest.cpp | 2 +- 6 files changed, 114 insertions(+), 110 deletions(-) diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index 1d235e1f3a69..6058fb9c23dd 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -1111,11 +1111,11 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 3), APInt(32, "+11", 2)); EXPECT_EQ(APInt(32, 4), APInt(32, "+100", 2)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 2)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 2)); - EXPECT_EQ(APInt(32, uint64_t(-2LL)), APInt(32, "-10", 2)); - EXPECT_EQ(APInt(32, uint64_t(-3LL)), APInt(32, "-11", 2)); - EXPECT_EQ(APInt(32, uint64_t(-4LL)), APInt(32, "-100", 2)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 2)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 2)); + EXPECT_EQ(APInt(32, uint32_t(-2LL)), APInt(32, "-10", 2)); + EXPECT_EQ(APInt(32, uint32_t(-3LL)), APInt(32, "-11", 2)); + EXPECT_EQ(APInt(32, uint32_t(-4LL)), APInt(32, "-100", 2)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 8)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 8)); @@ -1131,12 +1131,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, +15), APInt(32, "+17", 8)); EXPECT_EQ(APInt(32, +16), APInt(32, "+20", 8)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 8)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 8)); - EXPECT_EQ(APInt(32, uint64_t(-7LL)), APInt(32, "-7", 8)); - EXPECT_EQ(APInt(32, uint64_t(-8LL)), APInt(32, "-10", 8)); - EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-17", 8)); - EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-20", 8)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 8)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 8)); + EXPECT_EQ(APInt(32, uint32_t(-7LL)), APInt(32, "-7", 8)); + EXPECT_EQ(APInt(32, uint32_t(-8LL)), APInt(32, "-10", 8)); + EXPECT_EQ(APInt(32, uint32_t(-15LL)), APInt(32, "-17", 8)); + EXPECT_EQ(APInt(32, uint32_t(-16LL)), APInt(32, "-20", 8)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 10)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 10)); @@ -1145,12 +1145,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 19), APInt(32, "19", 10)); EXPECT_EQ(APInt(32, 20), APInt(32, "20", 10)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 10)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 10)); - EXPECT_EQ(APInt(32, uint64_t(-9LL)), APInt(32, "-9", 10)); - EXPECT_EQ(APInt(32, uint64_t(-10LL)), APInt(32, "-10", 10)); - EXPECT_EQ(APInt(32, uint64_t(-19LL)), APInt(32, "-19", 10)); - EXPECT_EQ(APInt(32, uint64_t(-20LL)), APInt(32, "-20", 10)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 10)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 10)); + EXPECT_EQ(APInt(32, uint32_t(-9LL)), APInt(32, "-9", 10)); + EXPECT_EQ(APInt(32, uint32_t(-10LL)), APInt(32, "-10", 10)); + EXPECT_EQ(APInt(32, uint32_t(-19LL)), APInt(32, "-19", 10)); + EXPECT_EQ(APInt(32, uint32_t(-20LL)), APInt(32, "-20", 10)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 16)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 16)); @@ -1159,12 +1159,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 31), APInt(32, "1F", 16)); EXPECT_EQ(APInt(32, 32), APInt(32, "20", 16)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 16)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 16)); - EXPECT_EQ(APInt(32, uint64_t(-15LL)), APInt(32, "-F", 16)); - EXPECT_EQ(APInt(32, uint64_t(-16LL)), APInt(32, "-10", 16)); - EXPECT_EQ(APInt(32, uint64_t(-31LL)), APInt(32, "-1F", 16)); - EXPECT_EQ(APInt(32, uint64_t(-32LL)), APInt(32, "-20", 16)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 16)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 16)); + EXPECT_EQ(APInt(32, uint32_t(-15LL)), APInt(32, "-F", 16)); + EXPECT_EQ(APInt(32, uint32_t(-16LL)), APInt(32, "-10", 16)); + EXPECT_EQ(APInt(32, uint32_t(-31LL)), APInt(32, "-1F", 16)); + EXPECT_EQ(APInt(32, uint32_t(-32LL)), APInt(32, "-20", 16)); EXPECT_EQ(APInt(32, 0), APInt(32, "0", 36)); EXPECT_EQ(APInt(32, 1), APInt(32, "1", 36)); @@ -1173,12 +1173,12 @@ TEST(APIntTest, fromString) { EXPECT_EQ(APInt(32, 71), APInt(32, "1Z", 36)); EXPECT_EQ(APInt(32, 72), APInt(32, "20", 36)); - EXPECT_EQ(APInt(32, uint64_t(-0LL)), APInt(32, "-0", 36)); - EXPECT_EQ(APInt(32, uint64_t(-1LL)), APInt(32, "-1", 36)); - EXPECT_EQ(APInt(32, uint64_t(-35LL)), APInt(32, "-Z", 36)); - EXPECT_EQ(APInt(32, uint64_t(-36LL)), APInt(32, "-10", 36)); - EXPECT_EQ(APInt(32, uint64_t(-71LL)), APInt(32, "-1Z", 36)); - EXPECT_EQ(APInt(32, uint64_t(-72LL)), APInt(32, "-20", 36)); + EXPECT_EQ(APInt(32, uint32_t(-0LL)), APInt(32, "-0", 36)); + EXPECT_EQ(APInt(32, uint32_t(-1LL)), APInt(32, "-1", 36)); + EXPECT_EQ(APInt(32, uint32_t(-35LL)), APInt(32, "-Z", 36)); + EXPECT_EQ(APInt(32, uint32_t(-36LL)), APInt(32, "-10", 36)); + EXPECT_EQ(APInt(32, uint32_t(-71LL)), APInt(32, "-1Z", 36)); + EXPECT_EQ(APInt(32, uint32_t(-72LL)), APInt(32, "-20", 36)); } TEST(APIntTest, SaturatingMath) { @@ -1202,10 +1202,10 @@ TEST(APIntTest, SaturatingMath) { EXPECT_EQ(APInt(6, 31), AP_42.truncSSat(6)); EXPECT_EQ(APInt(5, 15), AP_42.truncSSat(5)); - EXPECT_EQ(APInt(8, -56), AP_200.truncSSat(8)); - EXPECT_EQ(APInt(7, -56), AP_200.truncSSat(7)); - EXPECT_EQ(APInt(6, -32), AP_200.truncSSat(6)); - EXPECT_EQ(APInt(5, -16), AP_200.truncSSat(5)); + EXPECT_EQ(APInt(8, -56, true), AP_200.truncSSat(8)); + EXPECT_EQ(APInt(7, -56, true), AP_200.truncSSat(7)); + EXPECT_EQ(APInt(6, -32, true), AP_200.truncSSat(6)); + EXPECT_EQ(APInt(5, -16, true), AP_200.truncSSat(5)); EXPECT_EQ(APInt(8, 200), AP_100.uadd_sat(AP_100)); EXPECT_EQ(APInt(8, 255), AP_100.uadd_sat(AP_200)); @@ -1213,52 +1213,53 @@ TEST(APIntTest, SaturatingMath) { EXPECT_EQ(APInt(8, 110), AP_10.sadd_sat(AP_100)); EXPECT_EQ(APInt(8, 127), AP_100.sadd_sat(AP_100)); - EXPECT_EQ(APInt(8, -128), (-AP_100).sadd_sat(-AP_100)); - EXPECT_EQ(APInt(8, -128), APInt(8, -128).sadd_sat(APInt(8, -128))); + EXPECT_EQ(APInt(8, -128, true), (-AP_100).sadd_sat(-AP_100)); + EXPECT_EQ(APInt(8, -128, true), + APInt(8, -128, true).sadd_sat(APInt(8, -128, true))); EXPECT_EQ(APInt(8, 90), AP_100.usub_sat(AP_10)); EXPECT_EQ(APInt(8, 0), AP_100.usub_sat(AP_200)); EXPECT_EQ(APInt(8, 0), APInt(8, 0).usub_sat(APInt(8, 255))); - EXPECT_EQ(APInt(8, -90), AP_10.ssub_sat(AP_100)); + EXPECT_EQ(APInt(8, -90, true), AP_10.ssub_sat(AP_100)); EXPECT_EQ(APInt(8, 127), AP_100.ssub_sat(-AP_100)); - EXPECT_EQ(APInt(8, -128), (-AP_100).ssub_sat(AP_100)); - EXPECT_EQ(APInt(8, -128), APInt(8, -128).ssub_sat(APInt(8, 127))); + EXPECT_EQ(APInt(8, -128, true), (-AP_100).ssub_sat(AP_100)); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -128, true).ssub_sat(APInt(8, 127))); EXPECT_EQ(APInt(8, 250), APInt(8, 50).umul_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 255), APInt(8, 50).umul_sat(APInt(8, 6))); - EXPECT_EQ(APInt(8, 255), APInt(8, -128).umul_sat(APInt(8, 3))); - EXPECT_EQ(APInt(8, 255), APInt(8, 3).umul_sat(APInt(8, -128))); - EXPECT_EQ(APInt(8, 255), APInt(8, -128).umul_sat(APInt(8, -128))); + EXPECT_EQ(APInt(8, 255), APInt(8, -128, true).umul_sat(APInt(8, 3))); + EXPECT_EQ(APInt(8, 255), APInt(8, 3).umul_sat(APInt(8, -128, true))); + EXPECT_EQ(APInt(8, 255), APInt(8, -128, true).umul_sat(APInt(8, -128, true))); EXPECT_EQ(APInt(8, 125), APInt(8, 25).smul_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 127), APInt(8, 25).smul_sat(APInt(8, 6))); EXPECT_EQ(APInt(8, 127), APInt(8, 127).smul_sat(APInt(8, 127))); - EXPECT_EQ(APInt(8, -125), APInt(8, -25).smul_sat(APInt(8, 5))); - EXPECT_EQ(APInt(8, -125), APInt(8, 25).smul_sat(APInt(8, -5))); - EXPECT_EQ(APInt(8, 125), APInt(8, -25).smul_sat(APInt(8, -5))); + EXPECT_EQ(APInt(8, -125, true), APInt(8, -25, true).smul_sat(APInt(8, 5))); + EXPECT_EQ(APInt(8, -125, true), APInt(8, 25).smul_sat(APInt(8, -5, true))); + EXPECT_EQ(APInt(8, 125), APInt(8, -25, true).smul_sat(APInt(8, -5, true))); EXPECT_EQ(APInt(8, 125), APInt(8, 25).smul_sat(APInt(8, 5))); - EXPECT_EQ(APInt(8, -128), APInt(8, -25).smul_sat(APInt(8, 6))); - EXPECT_EQ(APInt(8, -128), APInt(8, 25).smul_sat(APInt(8, -6))); - EXPECT_EQ(APInt(8, 127), APInt(8, -25).smul_sat(APInt(8, -6))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -25, true).smul_sat(APInt(8, 6))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, 25).smul_sat(APInt(8, -6, true))); + EXPECT_EQ(APInt(8, 127), APInt(8, -25, true).smul_sat(APInt(8, -6, true))); EXPECT_EQ(APInt(8, 127), APInt(8, 25).smul_sat(APInt(8, 6))); EXPECT_EQ(APInt(8, 128), APInt(8, 4).ushl_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 255), APInt(8, 4).ushl_sat(APInt(8, 6))); EXPECT_EQ(APInt(8, 128), APInt(8, 1).ushl_sat(APInt(8, 7))); EXPECT_EQ(APInt(8, 255), APInt(8, 1).ushl_sat(APInt(8, 8))); - EXPECT_EQ(APInt(8, 255), APInt(8, -128).ushl_sat(APInt(8, 2))); + EXPECT_EQ(APInt(8, 255), APInt(8, -128, true).ushl_sat(APInt(8, 2))); EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, 2))); - EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, -2))); + EXPECT_EQ(APInt(8, 255), APInt(8, 64).ushl_sat(APInt(8, -2, true))); EXPECT_EQ(APInt(8, 64), APInt(8, 4).sshl_sat(APInt(8, 4))); EXPECT_EQ(APInt(8, 127), APInt(8, 4).sshl_sat(APInt(8, 5))); EXPECT_EQ(APInt(8, 127), APInt(8, 1).sshl_sat(APInt(8, 8))); - EXPECT_EQ(APInt(8, -64), APInt(8, -4).sshl_sat(APInt(8, 4))); - EXPECT_EQ(APInt(8, -128), APInt(8, -4).sshl_sat(APInt(8, 5))); - EXPECT_EQ(APInt(8, -128), APInt(8, -4).sshl_sat(APInt(8, 6))); - EXPECT_EQ(APInt(8, -128), APInt(8, -1).sshl_sat(APInt(8, 7))); - EXPECT_EQ(APInt(8, -128), APInt(8, -1).sshl_sat(APInt(8, 8))); + EXPECT_EQ(APInt(8, -64, true), APInt(8, -4, true).sshl_sat(APInt(8, 4))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -4, true).sshl_sat(APInt(8, 5))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -4, true).sshl_sat(APInt(8, 6))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -1, true).sshl_sat(APInt(8, 7))); + EXPECT_EQ(APInt(8, -128, true), APInt(8, -1, true).sshl_sat(APInt(8, 8))); } TEST(APIntTest, FromArray) { @@ -1400,39 +1401,39 @@ TEST(APIntTest, toString) { S.clear(); isSigned = false; - APInt(8, 255, isSigned).toString(S, 2, isSigned, true); + APInt(8, 255).toString(S, 2, isSigned, true); EXPECT_EQ(std::string(S), "0b11111111"); S.clear(); - APInt(8, 255, isSigned).toString(S, 8, isSigned, true); + APInt(8, 255).toString(S, 8, isSigned, true); EXPECT_EQ(std::string(S), "0377"); S.clear(); - APInt(8, 255, isSigned).toString(S, 10, isSigned, true); + APInt(8, 255).toString(S, 10, isSigned, true); EXPECT_EQ(std::string(S), "255"); S.clear(); - APInt(8, 255, isSigned).toString(S, 16, isSigned, true, /*UpperCase=*/false); + APInt(8, 255).toString(S, 16, isSigned, true, /*UpperCase=*/false); EXPECT_EQ(std::string(S), "0xff"); S.clear(); - APInt(8, 255, isSigned).toString(S, 16, isSigned, true); + APInt(8, 255).toString(S, 16, isSigned, true); EXPECT_EQ(std::string(S), "0xFF"); S.clear(); - APInt(8, 255, isSigned).toString(S, 36, isSigned, false); + APInt(8, 255).toString(S, 36, isSigned, false); EXPECT_EQ(std::string(S), "73"); S.clear(); isSigned = true; - APInt(8, 255, isSigned).toString(S, 2, isSigned, true); + APInt(8, 255).toString(S, 2, isSigned, true); EXPECT_EQ(std::string(S), "-0b1"); S.clear(); - APInt(8, 255, isSigned).toString(S, 8, isSigned, true); + APInt(8, 255).toString(S, 8, isSigned, true); EXPECT_EQ(std::string(S), "-01"); S.clear(); - APInt(8, 255, isSigned).toString(S, 10, isSigned, true); + APInt(8, 255).toString(S, 10, isSigned, true); EXPECT_EQ(std::string(S), "-1"); S.clear(); - APInt(8, 255, isSigned).toString(S, 16, isSigned, true); + APInt(8, 255).toString(S, 16, isSigned, true); EXPECT_EQ(std::string(S), "-0x1"); S.clear(); - APInt(8, 255, isSigned).toString(S, 36, isSigned, false); + APInt(8, 255).toString(S, 36, isSigned, false); EXPECT_EQ(std::string(S), "-1"); S.clear(); @@ -1515,7 +1516,7 @@ TEST(APIntTest, Rotate) { EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(33, 33))); EXPECT_EQ(APInt(32, (1 << 8)), APInt(32, 1).rotl(APInt(32, 40))); EXPECT_EQ(APInt(32, (1 << 30)), APInt(32, 1).rotl(APInt(31, 30))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotl(APInt(31, 31))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotl(APInt(31, 31))); EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotl(APInt(1, 0))); EXPECT_EQ(APInt(32, 2), APInt(32, 1).rotl(APInt(1, 1))); @@ -1542,24 +1543,24 @@ TEST(APIntTest, Rotate) { EXPECT_EQ(APInt(8, 16), APInt(8, 1).rotr(4)); EXPECT_EQ(APInt(8, 1), APInt(8, 1).rotr(8)); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33)); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(33)); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(32, 33))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(33)); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(32, 33))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(33, 33))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(33)); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(32, 33))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(33, 33))); EXPECT_EQ(APInt(32, (1 << 24)), APInt(32, 1).rotr(APInt(32, 40))); EXPECT_EQ(APInt(32, (1 << 2)), APInt(32, 1).rotr(APInt(31, 30))); EXPECT_EQ(APInt(32, (1 << 1)), APInt(32, 1).rotr(APInt(31, 31))); EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(1, 0))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(1, 1))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(1, 1))); EXPECT_EQ(APInt(32, (1 << 28)), APInt(32, 1).rotr(APInt(3, 4))); EXPECT_EQ(APInt(32, 1), APInt(32, 1).rotr(APInt(64, 64))); - EXPECT_EQ(APInt(32, (1 << 31)), APInt(32, 1).rotr(APInt(64, 65))); + EXPECT_EQ(APInt(32, (1u << 31)), APInt(32, 1).rotr(APInt(64, 65))); EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 3))); EXPECT_EQ(APInt(7, 48), APInt(7, 3).rotr(APInt(7, 10))); @@ -1581,7 +1582,7 @@ TEST(APIntTest, Splat) { APInt ValB(3, 5); EXPECT_EQ(APInt(4, 0xD), APInt::getSplat(4, ValB)); - EXPECT_EQ(APInt(15, 0xDB6D), APInt::getSplat(15, ValB)); + EXPECT_EQ(APInt(15, 0x5B6D), APInt::getSplat(15, ValB)); } TEST(APIntTest, tcDecrement) { @@ -2858,7 +2859,7 @@ TEST(APIntTest, sext) { EXPECT_EQ(31U, i32_min.countr_zero()); EXPECT_EQ(32U, i32_min.popcount()); - APInt i32_neg1(APInt(32, ~uint64_t(0)).sext(63)); + APInt i32_neg1(APInt(32, ~uint32_t(0)).sext(63)); EXPECT_EQ(i32_neg1, i32_neg1.sext(63)); EXPECT_EQ(63U, i32_neg1.countl_one()); EXPECT_EQ(0U, i32_neg1.countr_zero()); @@ -2991,7 +2992,7 @@ TEST(APIntTest, RoundingUDiv) { TEST(APIntTest, RoundingSDiv) { for (int64_t Ai = -128; Ai <= 127; Ai++) { - APInt A(8, Ai); + APInt A(8, Ai, true); if (Ai != 0) { APInt Zero(8, 0); @@ -3004,7 +3005,7 @@ TEST(APIntTest, RoundingSDiv) { if (Bi == 0) continue; - APInt B(8, Bi); + APInt B(8, Bi, true); APInt QuoTowardZero = A.sdiv(B); { APInt Quo = APIntOps::RoundingSDiv(A, B, APInt::Rounding::UP); @@ -3063,10 +3064,10 @@ TEST(APIntTest, Average) { APInt Ap100(32, +100); APInt Ap101(32, +101); APInt Ap200(32, +200); - APInt Am1(32, -1); - APInt Am100(32, -100); - APInt Am101(32, -101); - APInt Am200(32, -200); + APInt Am1(32, -1, true); + APInt Am100(32, -100, true); + APInt Am101(32, -101, true); + APInt Am200(32, -200, true); APInt AmSMin = APInt::getSignedMinValue(32); APInt ApSMax = APInt::getSignedMaxValue(32); @@ -3076,7 +3077,7 @@ TEST(APIntTest, Average) { EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap200, A2, APInt::Rounding::UP), APIntOps::avgCeilS(Ap100, Ap200)); - EXPECT_EQ(APInt(32, -150), APIntOps::avgFloorS(Am100, Am200)); + EXPECT_EQ(APInt(32, -150, true), APIntOps::avgFloorS(Am100, Am200)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am200, A2, APInt::Rounding::DOWN), APIntOps::avgFloorS(Am100, Am200)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am200, A2, APInt::Rounding::UP), @@ -3089,10 +3090,10 @@ TEST(APIntTest, Average) { EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap101, A2, APInt::Rounding::UP), APIntOps::avgCeilS(Ap100, Ap101)); - EXPECT_EQ(APInt(32, -101), APIntOps::avgFloorS(Am100, Am101)); + EXPECT_EQ(APInt(32, -101, true), APIntOps::avgFloorS(Am100, Am101)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am101, A2, APInt::Rounding::DOWN), APIntOps::avgFloorS(Am100, Am101)); - EXPECT_EQ(APInt(32, -100), APIntOps::avgCeilS(Am100, Am101)); + EXPECT_EQ(APInt(32, -100, true), APIntOps::avgCeilS(Am100, Am101)); EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am101, A2, APInt::Rounding::UP), APIntOps::avgCeilS(Am100, Am101)); @@ -3304,7 +3305,8 @@ TEST(APIntTest, SolveQuadraticEquationWrap) { for (int B = Low; B != High; ++B) { for (int C = Low; C != High; ++C) { std::optional S = APIntOps::SolveQuadraticEquationWrap( - APInt(Width, A), APInt(Width, B), APInt(Width, C), Width); + APInt(Width, A, true), APInt(Width, B, true), + APInt(Width, C, true), Width); if (S) Validate(A, B, C, Width, S->getSExtValue()); } @@ -3399,10 +3401,10 @@ TEST(APIntTest, GetMostSignificantDifferentBitExaustive) { } TEST(APIntTest, SignbitZeroChecks) { - EXPECT_TRUE(APInt(8, -1).isNegative()); - EXPECT_FALSE(APInt(8, -1).isNonNegative()); - EXPECT_FALSE(APInt(8, -1).isStrictlyPositive()); - EXPECT_TRUE(APInt(8, -1).isNonPositive()); + EXPECT_TRUE(APInt(8, -1, true).isNegative()); + EXPECT_FALSE(APInt(8, -1, true).isNonNegative()); + EXPECT_FALSE(APInt(8, -1, true).isStrictlyPositive()); + EXPECT_TRUE(APInt(8, -1, true).isNonPositive()); EXPECT_FALSE(APInt(8, 0).isNegative()); EXPECT_TRUE(APInt(8, 0).isNonNegative()); diff --git a/llvm/unittests/ADT/APSIntTest.cpp b/llvm/unittests/ADT/APSIntTest.cpp index f804eba3ca83..2d2a64433da9 100644 --- a/llvm/unittests/ADT/APSIntTest.cpp +++ b/llvm/unittests/ADT/APSIntTest.cpp @@ -74,14 +74,14 @@ TEST(APSIntTest, getExtValue) { EXPECT_TRUE(APSInt(APInt(3, 7), false).isSigned()); EXPECT_TRUE(APSInt(APInt(4, 7), true).isUnsigned()); EXPECT_TRUE(APSInt(APInt(4, 7), false).isSigned()); - EXPECT_TRUE(APSInt(APInt(4, -7), true).isUnsigned()); - EXPECT_TRUE(APSInt(APInt(4, -7), false).isSigned()); + EXPECT_TRUE(APSInt(APInt(4, -7, true), true).isUnsigned()); + EXPECT_TRUE(APSInt(APInt(4, -7, true), false).isSigned()); EXPECT_EQ(7, APSInt(APInt(3, 7), true).getExtValue()); EXPECT_EQ(-1, APSInt(APInt(3, 7), false).getExtValue()); EXPECT_EQ(7, APSInt(APInt(4, 7), true).getExtValue()); EXPECT_EQ(7, APSInt(APInt(4, 7), false).getExtValue()); - EXPECT_EQ(9, APSInt(APInt(4, -7), true).getExtValue()); - EXPECT_EQ(-7, APSInt(APInt(4, -7), false).getExtValue()); + EXPECT_EQ(9, APSInt(APInt(4, -7, true), true).getExtValue()); + EXPECT_EQ(-7, APSInt(APInt(4, -7, true), false).getExtValue()); } TEST(APSIntTest, tryExtValue) { ASSERT_EQ(-7, APSInt(APInt(64, -7), false).tryExtValue().value_or(42)); diff --git a/llvm/unittests/ADT/StringExtrasTest.cpp b/llvm/unittests/ADT/StringExtrasTest.cpp index 1fb1fea65779..eb202bad865d 100644 --- a/llvm/unittests/ADT/StringExtrasTest.cpp +++ b/llvm/unittests/ADT/StringExtrasTest.cpp @@ -296,11 +296,11 @@ TEST(StringExtrasTest, toStringAPInt) { EXPECT_EQ(toString(APInt(8, 255, isSigned), 36, isSigned, false), "73"); isSigned = true; - EXPECT_EQ(toString(APInt(8, 255, isSigned), 2, isSigned, true), "-0b1"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 8, isSigned, true), "-01"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 10, isSigned, true), "-1"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 16, isSigned, true), "-0x1"); - EXPECT_EQ(toString(APInt(8, 255, isSigned), 36, isSigned, false), "-1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 2, isSigned, true), "-0b1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 8, isSigned, true), "-01"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 10, isSigned, true), "-1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 16, isSigned, true), "-0x1"); + EXPECT_EQ(toString(APInt(8, -1, isSigned), 36, isSigned, false), "-1"); } TEST(StringExtrasTest, toStringAPSInt) { diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp index 93b53c466818..7a76d2304ef6 100644 --- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp @@ -982,7 +982,7 @@ TEST_F(ScalarEvolutionsTest, SCEVAddRecFromPHIwithLargeConstantAccum) { // entry: BranchInst::Create(LoopBB, EntryBB); // loop: - auto *MinInt32 = ConstantInt::get(Context, APInt(32, 0x80000000U, true)); + auto *MinInt32 = ConstantInt::get(Context, APInt(32, 0x80000000U)); auto *Int32_16 = ConstantInt::get(Context, APInt(32, 16)); auto *Br = BranchInst::Create( LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB); diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp index 17573ca57e08..821d6f44e521 100644 --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -4224,16 +4224,18 @@ TEST_F(DIExpressionTest, foldConstant) { DIExpression *Expr; DIExpression *NewExpr; -#define EXPECT_FOLD_CONST(StartWidth, StartValue, EndWidth, EndValue, NumElts) \ - Int = ConstantInt::get(Context, APInt(StartWidth, StartValue)); \ - std::tie(NewExpr, NewInt) = Expr->constantFold(Int); \ - ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u); \ - EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue)); \ +#define EXPECT_FOLD_CONST(StartWidth, StartValue, StartIsSigned, EndWidth, \ + EndValue, EndIsSigned, NumElts) \ + Int = \ + ConstantInt::get(Context, APInt(StartWidth, StartValue, StartIsSigned)); \ + std::tie(NewExpr, NewInt) = Expr->constantFold(Int); \ + ASSERT_EQ(NewInt->getBitWidth(), EndWidth##u); \ + EXPECT_EQ(NewInt->getValue(), APInt(EndWidth, EndValue, EndIsSigned)); \ EXPECT_EQ(NewExpr->getNumElements(), NumElts##u) // Unfoldable expression should return the original unmodified Int/Expr. Expr = DIExpression::get(Context, {dwarf::DW_OP_deref}); - EXPECT_FOLD_CONST(32, 117, 32, 117, 1); + EXPECT_FOLD_CONST(32, 117, false, 32, 117, false, 1); EXPECT_EQ(NewExpr, Expr); EXPECT_EQ(NewInt, Int); EXPECT_TRUE(NewExpr->startsWithDeref()); @@ -4241,18 +4243,18 @@ TEST_F(DIExpressionTest, foldConstant) { // One unsigned bit-width conversion. Expr = DIExpression::get( Context, {dwarf::DW_OP_LLVM_convert, 72, dwarf::DW_ATE_unsigned}); - EXPECT_FOLD_CONST(8, 12, 72, 12, 0); + EXPECT_FOLD_CONST(8, 12, false, 72, 12, false, 0); // Two unsigned bit-width conversions (mask truncation). Expr = DIExpression::get( Context, {dwarf::DW_OP_LLVM_convert, 8, dwarf::DW_ATE_unsigned, dwarf::DW_OP_LLVM_convert, 16, dwarf::DW_ATE_unsigned}); - EXPECT_FOLD_CONST(32, -1, 16, 0xff, 0); + EXPECT_FOLD_CONST(32, -1, true, 16, 0xff, false, 0); // Sign extension. Expr = DIExpression::get( Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed}); - EXPECT_FOLD_CONST(16, -1, 32, -1, 0); + EXPECT_FOLD_CONST(16, -1, true, 32, -1, true, 0); // Get non-foldable operations back in the new Expr. uint64_t Elements[] = {dwarf::DW_OP_deref, dwarf::DW_OP_stack_value}; @@ -4261,7 +4263,7 @@ TEST_F(DIExpressionTest, foldConstant) { Context, {dwarf::DW_OP_LLVM_convert, 32, dwarf::DW_ATE_signed}); Expr = DIExpression::append(Expr, Expected); ASSERT_EQ(Expr->getNumElements(), 5u); - EXPECT_FOLD_CONST(16, -1, 32, -1, 2); + EXPECT_FOLD_CONST(16, -1, true, 32, -1, true, 2); EXPECT_EQ(NewExpr->getElements(), Expected); #undef EXPECT_FOLD_CONST diff --git a/llvm/unittests/Support/DivisionByConstantTest.cpp b/llvm/unittests/Support/DivisionByConstantTest.cpp index 2b17f98bb75b..715dded68ff0 100644 --- a/llvm/unittests/Support/DivisionByConstantTest.cpp +++ b/llvm/unittests/Support/DivisionByConstantTest.cpp @@ -32,7 +32,7 @@ APInt SignedDivideUsingMagic(APInt Numerator, APInt Divisor, unsigned Bits = Numerator.getBitWidth(); APInt Factor(Bits, 0); - APInt ShiftMask(Bits, -1); + APInt ShiftMask(Bits, -1, true); if (Divisor.isOne() || Divisor.isAllOnes()) { // If d is +1/-1, we just multiply the numerator by +1/-1. Factor = Divisor.getSExtValue(); From b19bea53768b0943392d6ec65e7bbf349522caa9 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 25 Jan 2024 17:02:27 +0200 Subject: [PATCH 038/203] [Tablegen] Fix SubtargetEmitter for featureless targets SubtargetEmitter emits `std::nullopt`, `None` as default values for `ArrayRef`. They don't type match. The patch changes them to `{}`. --- llvm/utils/TableGen/SubtargetEmitter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index 1adefea5b035..c7d2ada57faa 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -2033,11 +2033,11 @@ void SubtargetEmitter::run(raw_ostream &OS) { if (NumFeatures) OS << Target << "FeatureKV, "; else - OS << "std::nullopt, "; + OS << "{}, "; if (NumProcs) OS << Target << "SubTypeKV, "; else - OS << "std::nullopt, "; + OS << "{}, "; OS << '\n'; OS.indent(22); OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " @@ -2139,11 +2139,11 @@ void SubtargetEmitter::run(raw_ostream &OS) { if (NumFeatures) OS << "ArrayRef(" << Target << "FeatureKV, " << NumFeatures << "), "; else - OS << "std::nullopt, "; + OS << "{}, "; if (NumProcs) OS << "ArrayRef(" << Target << "SubTypeKV, " << NumProcs << "), "; else - OS << "std::nullopt, "; + OS << "{}, "; OS << '\n'; OS.indent(24); OS << Target << "WriteProcResTable, " << Target << "WriteLatencyTable, " From 9cf1c5598b0dc1a699251c58bcec0c03cc403cc3 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 3 Aug 2023 15:22:53 +0200 Subject: [PATCH 039/203] [NewGVN] Fix dangling pointer Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Transforms/Scalar/NewGVN.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp index fc0b31c43396..82b2c50b4583 100644 --- a/llvm/lib/Transforms/Scalar/NewGVN.cpp +++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp @@ -4098,7 +4098,9 @@ bool NewGVN::eliminateInstructions(Function &F) { U->set(DominatingLeader); // This is now a use of the dominating leader, which means if the // dominating leader was dead, it's now live! - auto &LeaderUseCount = UseCounts[DominatingLeader]; + // EVM local begin + auto LeaderUseCount = UseCounts[DominatingLeader]; + // EVM local end // It's about to be alive again. if (LeaderUseCount == 0 && isa(DominatingLeader)) ProbablyDead.erase(cast(DominatingLeader)); @@ -4112,7 +4114,9 @@ bool NewGVN::eliminateInstructions(Function &F) { ProbablyDead.insert(II); } } - ++LeaderUseCount; + // EVM local begin + ++UseCounts[DominatingLeader]; + // EVM local end AnythingReplaced = true; } } From e821541f70995bcc1284800d1d2b52ccfadcf06a Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 9 Aug 2023 14:23:23 +0200 Subject: [PATCH 040/203] Implementation of the Keccak-f[1600] permutation It is used for instantiation of the KECCAK-256 hash function. --- llvm/include/llvm/Support/KECCAK.h | 42 ++++ llvm/lib/Support/CMakeLists.txt | 1 + llvm/lib/Support/KECCAK.cpp | 287 ++++++++++++++++++++++++++ llvm/unittests/Support/CMakeLists.txt | 1 + llvm/unittests/Support/KECCAKTest.cpp | 69 +++++++ 5 files changed, 400 insertions(+) create mode 100644 llvm/include/llvm/Support/KECCAK.h create mode 100644 llvm/lib/Support/KECCAK.cpp create mode 100644 llvm/unittests/Support/KECCAKTest.cpp diff --git a/llvm/include/llvm/Support/KECCAK.h b/llvm/include/llvm/Support/KECCAK.h new file mode 100644 index 000000000000..42230cf31f35 --- /dev/null +++ b/llvm/include/llvm/Support/KECCAK.h @@ -0,0 +1,42 @@ +//===-- KECCAK.h - KECCAK implementation ------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Implementation the Keccak-f[1600] permutation approved in the FIPS 202 +// standard, which is used for instantiation of the KECCAK-256 hash function. +// +// [FIPS PUB 202] +// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf +// [Keccak Reference] +// https://keccak.team/files/Keccak-reference-3.0.pdf +// [Keccak Specifications Summary] +// https://keccak.team/keccak_specs_summary.html +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_KECCAK_H +#define LLVM_SUPPORT_KECCAK_H + +#include +#include + +namespace llvm { + +template class ArrayRef; +class StringRef; + +namespace KECCAK { +/// Returns a raw 256-bit KECCAK-256 hash for the given data. +std::array KECCAK_256(ArrayRef Data); + +/// Returns a raw 256-bit KECCAK-256 hash for the given string. +std::array KECCAK_256(StringRef Str); +} // namespace KECCAK + +} // namespace llvm + +#endif // LLVM_SUPPORT_KECCAK_H diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index f653379e3033..4240a981551b 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -194,6 +194,7 @@ add_llvm_component_library(LLVMSupport IntEqClasses.cpp IntervalMap.cpp JSON.cpp + KECCAK.cpp KnownBits.cpp LEB128.cpp LineIterator.cpp diff --git a/llvm/lib/Support/KECCAK.cpp b/llvm/lib/Support/KECCAK.cpp new file mode 100644 index 000000000000..a2714286976b --- /dev/null +++ b/llvm/lib/Support/KECCAK.cpp @@ -0,0 +1,287 @@ +//===-- KECCAK.cpp - Private copy of the KECCAK implementation --*- 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 code was taken from public domain +// (https://github.com/XKCP/XKCP/blob/master/Standalone/CompactFIPS202/ +// C/Keccak-readable-and-compact.c), and modified by wrapping it in a +// C++ interface for LLVM, changing formatting, and removing +// unnecessary comments. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/KECCAK.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/TargetParser/Host.h" +#include + +using namespace llvm; + +namespace { + +#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN +#define KECCAK_LITTLE_ENDIAN +#endif + +#ifndef KECCAK_LITTLE_ENDIAN +/// +/// Function to load a 64-bit value using the little-endian (LE) convention. +/// On a LE platform, this could be greatly simplified using a cast. +/// +uint64_t load64(const uint8_t *x) { + int i; + uint64_t u = 0; + for (i = 7; i >= 0; --i) { + u <<= 8; + u |= x[i]; + } + return u; +} + +/// +/// Function to store a 64-bit value using the little-endian (LE) convention. +/// On a LE platform, this could be greatly simplified using a cast. +/// +void store64(uint8_t *x, uint64_t u) { + unsigned int i; + for (i = 0; i < 8; ++i) { + x[i] = u; + u >>= 8; + } +} + +/// +/// Function to XOR into a 64-bit value using the little-endian (LE) convention. +/// On a LE platform, this could be greatly simplified using a cast. +/// +void xor64(uint8_t *x, uint64_t u) { + unsigned int i; + for (i = 0; i < 8; ++i) { + x[i] ^= u; + u >>= 8; + } +} +#endif + +typedef uint64_t tKeccakLane; + +/// +/// A readable and compact implementation of the Keccak-f[1600] permutation. +/// +#define ROL64(a, offset) \ + ((((uint64_t)a) << offset) ^ (((uint64_t)a) >> (64 - offset))) +#define i(x, y) ((x) + 5 * (y)) + +#ifdef KECCAK_LITTLE_ENDIAN +#define readLane(x, y) (((tKeccakLane *)state)[i(x, y)]) +#define writeLane(x, y, lane) (((tKeccakLane *)state)[i(x, y)]) = (lane) +#define XORLane(x, y, lane) (((tKeccakLane *)state)[i(x, y)]) ^= (lane) +#else +#define readLane(x, y) load64((uint8_t *)state + sizeof(tKeccakLane) * i(x, y)) +#define writeLane(x, y, lane) \ + store64((uint8_t *)state + sizeof(tKeccakLane) * i(x, y), lane) +#define XORLane(x, y, lane) \ + xor64((uint8_t *)state + sizeof(tKeccakLane) * i(x, y), lane) +#endif + +/// +/// Function that computes the linear feedback shift register (LFSR) used to +/// define the round constants (see [Keccak Reference, Section 1.2]). +/// +int LFSR86540(uint8_t *LFSR) { + const int result = ((*LFSR) & 0x01) != 0; + if (((*LFSR) & 0x80) != 0) + // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 + (*LFSR) = ((*LFSR) << 1) ^ 0x71; + else + (*LFSR) <<= 1; + return result; +} + +/// +/// Function that computes the Keccak-f[1600] permutation on the given state. +/// +void KeccakF1600_StatePermute(void *state) { + unsigned int round, x, y, j, t; + uint8_t LFSRstate = 0x01; + + for (round = 0; round < 24; round++) { + { + // θ step (see [Keccak Reference, Section 2.3.2]) + tKeccakLane C[5], D; + // Compute the parity of the columns + for (x = 0; x < 5; x++) + C[x] = readLane(x, 0) ^ readLane(x, 1) ^ readLane(x, 2) ^ + readLane(x, 3) ^ readLane(x, 4); + for (x = 0; x < 5; x++) { + /* Compute the θ effect for a given column */ + D = C[(x + 4) % 5] ^ ROL64(C[(x + 1) % 5], 1); + /* Add the θ effect to the whole column */ + for (y = 0; y < 5; y++) + XORLane(x, y, D); + } + } + + { + // ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) + tKeccakLane current, temp; + // Start at coordinates (1 0) + x = 1; + y = 0; + current = readLane(x, y); + // Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 + for (t = 0; t < 24; t++) { + // Compute the rotation constant r = (t+1)(t+2)/2 + const unsigned int r = ((t + 1) * (t + 2) / 2) % 64; + // Compute ((0 1)(2 3)) * (x y) + const unsigned int Y = (2 * x + 3 * y) % 5; + x = y; + y = Y; + // Swap current and state(x,y), and rotate + temp = readLane(x, y); + writeLane(x, y, ROL64(current, r)); + current = temp; + } + } + + { + // χ step (see [Keccak Reference, Section 2.3.1]) + tKeccakLane temp[5]; + for (y = 0; y < 5; y++) { + // Take a copy of the plane + for (x = 0; x < 5; x++) + temp[x] = readLane(x, y); + // Compute χ on the plane + for (x = 0; x < 5; x++) + writeLane(x, y, temp[x] ^ ((~temp[(x + 1) % 5]) & temp[(x + 2) % 5])); + } + } + + { + // ι step (see [Keccak Reference, Section 2.3.5]) + for (j = 0; j < 7; j++) { + const unsigned int bitPosition = (1 << j) - 1; // 2^j-1 + if (LFSR86540(&LFSRstate)) + XORLane(0, 0, (tKeccakLane)1 << bitPosition); + } + } + } +} + +/// +/// Function to compute the Keccak[r, c] sponge function over a given input +/// using the Keccak-f[1600] permutation. +/// +/// @param rate +/// The value of the rate r. +/// @param capacity +/// The value of the capacity c. +/// @param input +/// Pointer to the input message. +/// @param inputByteLen +/// The number of input bytes provided in the input message. +/// @param delimitedSuffix +/// Bits that will be automatically appended to the end of the input message, +/// as in domain separation. This is a byte containing from 0 to 7 bits. +/// These n bits must be in the least significant bit positions and +/// must be delimited with a bit 1 at position n +/// (counting from 0=LSB to 7=MSB) and followed by bits 0 from position +/// n+1 to position 7. +/// Some examples: +/// - If no bits are to be appended, then @a delimitedSuffix must be 0x01. +/// - If the 2-bit sequence 0,1 is to be appended (as for SHA3-*), +/// @a delimitedSuffix must be 0x06. +/// - If the 4-bit sequence 1,1,1,1 is to be appended (as for SHAKE*), +/// @a delimitedSuffix must be 0x1F. +/// - If the 7-bit sequence 1,1,0,1,0,0,0 is to be absorbed, +/// @a delimitedSuffix must be 0x8B. +/// @param output +/// Pointer to the buffer where to store the output. +/// @param outputByteLen +/// The number of output bytes desired. +/// @pre +/// One must have r+c=1600 and the rate a multiple of 8 bits in this +/// implementation. +/// +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +void KeccakSponge(unsigned int rate, unsigned int capacity, + const unsigned char *input, + unsigned long long int inputByteLen, + unsigned char delimitedSuffix, unsigned char *output, + unsigned long long int outputByteLen) { + uint8_t state[200]; + const unsigned int rateInBytes = rate / 8; + unsigned int blockSize = 0; + unsigned int i; + + if (((rate + capacity) != 1600) || ((rate % 8) != 0)) + return; + + // Initialize the state + std::memset(state, 0, sizeof(state)); + + // Absorb all the input blocks + while (inputByteLen > 0) { + blockSize = MIN(inputByteLen, rateInBytes); + for (i = 0; i < blockSize; i++) + state[i] ^= input[i]; + input += blockSize; + inputByteLen -= blockSize; + + if (blockSize == rateInBytes) { + KeccakF1600_StatePermute(state); + blockSize = 0; + } + } + + // Do the padding and switch to the squeezing phase + // Absorb the last few bits and add the first bit of padding + // (which coincides with the delimiter in delimitedSuffix) + state[blockSize] ^= delimitedSuffix; + // If the first bit of padding is at position rate-1, we need a whole new + // block for the second bit of padding + if (((delimitedSuffix & 0x80) != 0) && (blockSize == (rateInBytes - 1))) + KeccakF1600_StatePermute(state); + // Add the second bit of padding + state[rateInBytes - 1] ^= 0x80; + // Switch to the squeezing phase + KeccakF1600_StatePermute(state); + + // Squeeze out all the output blocks + while (outputByteLen > 0) { + blockSize = MIN(outputByteLen, rateInBytes); + std::memcpy(output, state, blockSize); + output += blockSize; + outputByteLen -= blockSize; + + if (outputByteLen > 0) + KeccakF1600_StatePermute(state); + } +} + +} // end anonymous namespace + +/// +/// Function to compute KECCAK-256 hash on the input message. The output length +/// is fixed to 32 bytes. +/// +std::array KECCAK::KECCAK_256(ArrayRef Data) { + std::array Result; + KeccakSponge(1088, 512, Data.data(), Data.size(), 0x01, Result.data(), 32); + return Result; +} + +/// +/// Function to compute KECCAK-256 hash on the input string message. The output +/// length is fixed to 32 bytes. +/// +std::array KECCAK::KECCAK_256(StringRef Str) { + return KECCAK_256( + ArrayRef(reinterpret_cast(Str.data()), Str.size())); +} diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 631f2e6bf00d..0800a247eff1 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -50,6 +50,7 @@ add_llvm_unittest(SupportTests InstructionCostTest.cpp JSONTest.cpp KnownBitsTest.cpp + KECCAKTest.cpp LEB128Test.cpp LineIteratorTest.cpp LockFileManagerTest.cpp diff --git a/llvm/unittests/Support/KECCAKTest.cpp b/llvm/unittests/Support/KECCAKTest.cpp new file mode 100644 index 000000000000..00527eca4a07 --- /dev/null +++ b/llvm/unittests/Support/KECCAKTest.cpp @@ -0,0 +1,69 @@ +//===-- KECCAKTest.cpp - KECCAK tests ---------------------------*- 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 implements unit tests for the KECCAK functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/KECCAK.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +static std::string toHex(ArrayRef Input) { + static const char *const LUT = "0123456789abcdef"; + const size_t Length = Input.size(); + + std::string Output; + Output.reserve(2 * Length); + for (size_t i = 0; i < Length; ++i) { + const unsigned char c = Input[i]; + Output.push_back(LUT[c >> 4]); + Output.push_back(LUT[c & 15]); + } + return Output; +} + +/// Tests an arbitrary set of bytes passed as \p Input. +void TestKECCAKSum(ArrayRef Input, StringRef Final) { + auto hash = KECCAK::KECCAK_256(Input); + auto hashStr = toHex(hash); + EXPECT_EQ(hashStr, Final); +} + +using KV = std::pair; + +TEST(KECCAKTest, KECCAK) { + const std::array testvectors{ + KV{"", + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"}, + KV{"a", + "3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb"}, + KV{"abc", + "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"}, + KV{"abcdbcdecdefdefgefghfghighijhijk", + "4b50e45e85ca4a0a9c089890faf83098c75b04fe0e0f9c5488effd1643711033"}, + KV{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "45d3b367a6904e6e8d502ee04999a7c27647f91fa845d456525fd352ae3d7371"}, + KV{"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklm" + "nopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67"}}; + + for (auto input_expected : testvectors) { + const auto *const str = std::get<0>(input_expected); + const auto *const expected = std::get<1>(input_expected); + TestKECCAKSum({reinterpret_cast(str), strlen(str)}, + expected); + } +} + +} // end anonymous namespace From 4a6ce30a179f126dd1d26bef5d80610cb5b01690 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 21 Dec 2023 12:00:24 +0100 Subject: [PATCH 041/203] [NewPM] Add PreInlinerOptimizations module extension point This patch adds new module extension point just before Inliner to use it e.g. for eliminating duplicated stdlib functions. We could use existing Peephole extension, but it is called more than once, and we need to register passes only before Inliner. Signed-off-by: Vladimir Radosavljevic --- llvm/include/llvm/Passes/PassBuilder.h | 42 ++++++++++++++++++++++++ llvm/lib/Passes/PassBuilderPipelines.cpp | 10 ++++++ 2 files changed, 52 insertions(+) diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h index 474a19531ff5..1841939d5b44 100644 --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -483,6 +483,44 @@ class PassBuilder { PipelineEarlySimplificationEPCallbacks.push_back(C); } + // EVM local begin + /// Register a callback for a default optimizer pipeline extension point + /// + /// This extension point allows adding optimizations before the inliner + /// and it differs from PipelineEarlySimplificationEPCallbacks, because some + /// passes are called in between, that in some cases can create opportunities + /// for CSE-like and memory based optimizations, since the input code is + /// better optimized. + /// EVM adds passes to remove repetitive computation of read only library + /// function calls (__sha3, __system_request, etc.). These passes must work + /// before the inliner, but to simplify inner pattern matching logic and make + /// it work more frequently, we found it better to put it right before the + /// inliner. Since InstCombinePass is called before this extension point, + /// it can simplify code and enable easier elimination of repetitive function + /// calls. + /// + /// Example that shows how InstCombinePass enables elimination of repetitive + /// function calls based on the input data that is passed: + /// Before InstCombinePass: + /// %_118 = load i256, ptr %_1, align 32 + /// store i256 %_118, ptr addrspace(1) %ptr1, align 1 + /// %_119 = load i256, ptr %_1, align 32 + /// store i256 %_119, ptr addrspace(1) %ptr2, align 1 + /// After InstCombinePass: + /// %_118 = load i256, ptr %_1, align 32 + /// store i256 %_118, ptr addrspace(1) %ptr1, align 4294967296 + /// store i256 %_118, ptr addrspace(1) %ptr2, align 32 + /// + /// In case there are two consecutive calls to the same library function, + /// but only different pointers are passed (in this case %ptr1 and %ptr2), + /// we can inspect these pointers and see that the same input data is written, + /// so in that case, it's possible to safely eliminate the second call. + void registerPreInlinerOptimizationsEPCallback( + const std::function &C) { + PreInlinerOptimizationsEPCallbacks.push_back(C); + } + // EVM local end + /// Register a callback for a default optimizer pipeline extension point /// /// This extension point allows adding optimizations before the function @@ -753,6 +791,10 @@ class PassBuilder { SmallVector, 2> VectorizerStartEPCallbacks; // Module callbacks + // EVM local begin + SmallVector, 2> + PreInlinerOptimizationsEPCallbacks; + // EVM local end SmallVector, 2> OptimizerEarlyEPCallbacks; SmallVector, 2> diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index 6f36bdad780a..d3074475f199 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -1207,6 +1207,11 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, if (EnablePGOForceFunctionAttrs && PGOOpt) MPM.addPass(PGOForceFunctionAttrsPass(PGOOpt->ColdOptType)); + // EVM local begin + for (auto &C : PreInlinerOptimizationsEPCallbacks) + C(MPM, Level); + // EVM local end + MPM.addPass(AlwaysInlinerPass(/*InsertLifetimeIntrinsics=*/true)); if (EnableModuleInliner) @@ -2094,6 +2099,11 @@ ModulePassManager PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, invokePipelineEarlySimplificationEPCallbacks(MPM, Level); + // EVM local begin + for (auto &C : PreInlinerOptimizationsEPCallbacks) + C(MPM, Level); + // EVM local end + // Build a minimal pipeline based on the semantics required by LLVM, // which is just that always inlining occurs. Further, disable generating // lifetime intrinsics to avoid enabling further optimizations during From 2727d14f18f6ad570a4b0cbce7d377b31844068e Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 17 Jan 2024 11:16:19 +0100 Subject: [PATCH 042/203] [InstCombine] Add pre-commit tests for simplify -X > ~X to X != 0 Signed-off-by: Vladimir Radosavljevic --- .../Transforms/InstCombine/neg-not-iugt.ll | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 llvm/test/Transforms/InstCombine/neg-not-iugt.ll diff --git a/llvm/test/Transforms/InstCombine/neg-not-iugt.ll b/llvm/test/Transforms/InstCombine/neg-not-iugt.ll new file mode 100644 index 000000000000..a45b0d085c29 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/neg-not-iugt.ll @@ -0,0 +1,146 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare void @use(i32) + +define i1 @test1(i32 %arg) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test1_nuw(i32 %arg) { +; CHECK-LABEL: @test1_nuw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test1_nsw(i32 %arg) { +; CHECK-LABEL: @test1_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 0, [[ARG:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub nsw i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test1_nuw_nsw(i32 %arg) { +; CHECK-LABEL: @test1_nuw_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw nsw i32 0, %arg + %xor = xor i32 %arg, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2(i32 %arg) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2_nuw(i32 %arg) { +; CHECK-LABEL: @test2_nuw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2_nsw(i32 %arg) { +; CHECK-LABEL: @test2_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 0, [[ARG:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub nsw i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test2_nuw_nsw(i32 %arg) { +; CHECK-LABEL: @test2_nuw_nsw( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i1 false +; +entry: + %sub = sub nuw nsw i32 0, %arg + %xor = xor i32 -1, %arg + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test3(i32 %arg) { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG:%.*]] +; CHECK-NEXT: call void @use(i32 [[SUB]]) +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 +; CHECK-NEXT: call void @use(i32 [[XOR]]) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg + call void @use(i32 %sub) + %xor = xor i32 -1, %arg + call void @use(i32 %xor) + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} + +define i1 @test_no(i32 %arg1, i32 %arg2) { +; CHECK-LABEL: @test_no( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG1:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG2:%.*]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: ret i1 [[CMP]] +; +entry: + %sub = sub i32 0, %arg1 + %xor = xor i32 %arg2, -1 + %cmp = icmp ugt i32 %sub, %xor + ret i1 %cmp +} From 07987b14d0195d4b5539d29327abe518c780861e Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 17 Jan 2024 11:22:48 +0100 Subject: [PATCH 043/203] [InstCombine] Simplify -X > ~X to X != 0 Proof: https://godbolt.org/z/r154e8efK This should be reverted when we update to LLVM 18. Signed-off-by: Vladimir Radosavljevic --- .../InstCombine/InstCombineCompares.cpp | 12 ++++++++++ .../Transforms/InstCombine/neg-not-iugt.ll | 23 ++++++------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index a8854d7eeb36..b0114e429904 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -7583,6 +7583,18 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) { !ACXI->isWeak()) return ExtractValueInst::Create(ACXI, 1); + + // EVM local begin + { + Value *X = nullptr; + // -X > ~X --> X != 0 + if (Pred == ICmpInst::ICMP_UGT && match(Op0, m_Neg(m_Value(X))) && + match(Op1, m_Not(m_Specific(X)))) + return new ICmpInst(ICmpInst::ICMP_NE, X, + ConstantInt::getNullValue(Op0->getType())); + } + // EVM local end + if (Instruction *Res = foldICmpWithHighBitMask(I, Builder)) return Res; diff --git a/llvm/test/Transforms/InstCombine/neg-not-iugt.ll b/llvm/test/Transforms/InstCombine/neg-not-iugt.ll index a45b0d085c29..c8091fad7edf 100644 --- a/llvm/test/Transforms/InstCombine/neg-not-iugt.ll +++ b/llvm/test/Transforms/InstCombine/neg-not-iugt.ll @@ -6,9 +6,7 @@ declare void @use(i32) define i1 @test1(i32 %arg) { ; CHECK-LABEL: @test1( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 -; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 ; CHECK-NEXT: ret i1 [[CMP]] ; entry: @@ -33,9 +31,7 @@ entry: define i1 @test1_nsw(i32 %arg) { ; CHECK-LABEL: @test1_nsw( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 0, [[ARG:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 -; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 ; CHECK-NEXT: ret i1 [[CMP]] ; entry: @@ -60,9 +56,7 @@ entry: define i1 @test2(i32 %arg) { ; CHECK-LABEL: @test2( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 -; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 ; CHECK-NEXT: ret i1 [[CMP]] ; entry: @@ -87,9 +81,7 @@ entry: define i1 @test2_nsw(i32 %arg) { ; CHECK-LABEL: @test2_nsw( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 0, [[ARG:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 -; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG:%.*]], 0 ; CHECK-NEXT: ret i1 [[CMP]] ; entry: @@ -118,7 +110,7 @@ define i1 @test3(i32 %arg) { ; CHECK-NEXT: call void @use(i32 [[SUB]]) ; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG]], -1 ; CHECK-NEXT: call void @use(i32 [[XOR]]) -; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[ARG]], 0 ; CHECK-NEXT: ret i1 [[CMP]] ; entry: @@ -133,9 +125,8 @@ entry: define i1 @test_no(i32 %arg1, i32 %arg2) { ; CHECK-LABEL: @test_no( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[ARG1:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[ARG2:%.*]], -1 -; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SUB]], [[XOR]] +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[ARG1:%.*]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ADD]], %arg2 ; CHECK-NEXT: ret i1 [[CMP]] ; entry: From 7bceee840cb3445ab1efb17b723bb7bcb52f3ee8 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 14 Feb 2024 11:50:25 +0100 Subject: [PATCH 044/203] [SeparateConstOffsetFromGEP] Fix constant generation for negative AccumulativeByteOffset Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp index 73e3ff296cf1..111508cfac47 100644 --- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp +++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp @@ -904,7 +904,9 @@ void SeparateConstOffsetFromGEP::lowerToSingleIndexGEPs( // Create a GEP with the constant offset index. if (AccumulativeByteOffset != 0) { - Value *Offset = ConstantInt::get(PtrIndexTy, AccumulativeByteOffset); + // EVM local begin + Value *Offset = ConstantInt::get(PtrIndexTy, AccumulativeByteOffset, true); + // EVM local end ResultPtr = Builder.CreatePtrAdd(ResultPtr, Offset, "uglygep"); } else isSwapCandidate = false; @@ -961,8 +963,10 @@ SeparateConstOffsetFromGEP::lowerToArithmetics(GetElementPtrInst *Variadic, // Create an ADD for the constant offset index. if (AccumulativeByteOffset != 0) { + // EVM local begin ResultPtr = Builder.CreateAdd( - ResultPtr, ConstantInt::get(IntPtrTy, AccumulativeByteOffset)); + ResultPtr, ConstantInt::get(IntPtrTy, AccumulativeByteOffset, true)); + // EVM local end } ResultPtr = Builder.CreateIntToPtr(ResultPtr, Variadic->getType()); From f2daab9981c7c73d992a6305987958a731208eab Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 19 Feb 2024 10:39:27 +0100 Subject: [PATCH 045/203] [ConstantFolding] Fix negative offset calculation in GEP evaluation Set that offset is signed to fix calculation for large BitWidth. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Analysis/ConstantFolding.cpp | 14 +++++++---- .../InstCombine/gep-large-negative-offset.ll | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 69e1985c0c2b..1c86e2948023 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -886,11 +886,15 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, return nullptr; unsigned BitWidth = DL.getTypeSizeInBits(IntIdxTy); - APInt Offset = APInt( - BitWidth, - DL.getIndexedOffsetInType( - SrcElemTy, ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1)), + // EVM local begin + APInt Offset = + APInt(BitWidth, + DL.getIndexedOffsetInType( + SrcElemTy, + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + ArrayRef((Value *const *)Ops.data() + 1, Ops.size() - 1)), /*isSigned=*/true, /*implicitTrunc=*/true); + // EVM local end std::optional InRange = GEP->getInRange(); if (InRange) @@ -925,7 +929,7 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, Ptr = cast(GEP->getOperand(0)); SrcElemTy = GEP->getSourceElementType(); Offset = Offset.sadd_ov( - APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps)), + APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps), true), // EVM local Overflow); } diff --git a/llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll b/llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll new file mode 100644 index 000000000000..fe79746f87e5 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/gep-large-negative-offset.ll @@ -0,0 +1,23 @@ +; RUN: opt -S < %s -passes=instcombine | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S32-a:256:256-ni:1" + +define ptr @test_neg() { +; CHECK-LABEL: @test_neg( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret ptr inttoptr (i256 192 to ptr) +; +entry: + %gep = getelementptr inbounds i8, ptr inttoptr (i256 224 to ptr), i256 -32 + ret ptr %gep +} + +define ptr addrspace(1) @test_inner_neg() { +; CHECK-LABEL: @test_inner_neg( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret ptr addrspace(1) getelementptr (i8, ptr addrspace(1) null, i256 -22) +; +entry: + %gep = getelementptr i8, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) null, i256 -32), i256 10 + ret ptr addrspace(1) %gep +} From 53d5afaeb3d1ed4acc1ecdff46ea550df1692210 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 28 Nov 2023 20:11:02 +0200 Subject: [PATCH 046/203] [LLVM] Suppress linter in APInt destructor --- llvm/include/llvm/ADT/APInt.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 88f296c2dac3..9c29eae68647 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -207,7 +207,10 @@ class [[nodiscard]] APInt { /// Destructor. ~APInt() { if (needsCleanup()) + // EVM local begin + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete) delete[] U.pVal; + // EVM local end } /// @} From d3ed1020d29b272fc1399304f7c8e5ffd3e4d857 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Mon, 23 Jan 2023 17:48:05 +0200 Subject: [PATCH 047/203] Track commit id as part of the version * Export commit ID getting API as LLVMPrintCommitIDTo. * Print commit id in LLVM tool --version message. --- llvm/include/llvm-c/Support.h | 8 ++++++++ llvm/lib/Support/CMakeLists.txt | 10 ++++++++++ llvm/lib/Support/CommandLine.cpp | 11 +++++++++++ 3 files changed, 29 insertions(+) diff --git a/llvm/include/llvm-c/Support.h b/llvm/include/llvm-c/Support.h index 17657861b32b..95a503190fab 100644 --- a/llvm/include/llvm-c/Support.h +++ b/llvm/include/llvm-c/Support.h @@ -63,6 +63,14 @@ void *LLVMSearchForAddressOfSymbol(const char *symbolName); */ void LLVMAddSymbol(const char *symbolName, void *symbolValue); +// EVM local begin +/** + * Print git commit id to Buf. + * Return sprintf exit code. + */ +int LLVMPrintCommitIDTo(char* Buf); +// EVM local end + /** * @} */ diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 4240a981551b..32887ce755f7 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -128,6 +128,16 @@ endif() add_subdirectory(BLAKE3) +# EVM local begin +find_package(Git REQUIRED) +execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD + WORKING_DIRECTORY "${LLVM_MAIN_SRC_DIR}" + OUTPUT_VARIABLE COMMIT_ID + RESULT_VARIABLE RES_ID + OUTPUT_STRIP_TRAILING_WHITESPACE) +set_source_files_properties(CommandLine.cpp PROPERTIES COMPILE_FLAGS -DEVM_LLVM_COMMIT_ID=\\"${COMMIT_ID}\\") +# EVM local end + add_llvm_component_library(LLVMSupport ABIBreak.cpp AMDGPUMetadata.cpp diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index ecc487a17ccc..b0993bc8eb8d 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -2537,6 +2537,11 @@ class VersionPrinter { OS << "LLVM (http://llvm.org/):\n "; #endif OS << PACKAGE_NAME << " version " << PACKAGE_VERSION << "\n "; +// EVM local begin +#ifdef EVM_LLVM_COMMIT_ID + OS << "Git: " << EVM_LLVM_COMMIT_ID << "\n "; +#endif +// EVM local end #if LLVM_IS_DEBUG_BUILD OS << "DEBUG build"; #else @@ -2842,3 +2847,9 @@ void LLVMParseCommandLineOptions(int argc, const char *const *argv, llvm::cl::ParseCommandLineOptions(argc, argv, StringRef(Overview), &llvm::nulls()); } + +// EVM local begin +int LLVMPrintCommitIDTo(char* Buf) { + return sprintf(Buf, "%s", EVM_LLVM_COMMIT_ID); +} +// EVM local end From f1333b5d522a85e9c75ffef125cdf8632a79d039 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Fri, 2 Feb 2024 13:58:03 +0200 Subject: [PATCH 048/203] [LIT] Disable tests that require non-root permission --- llvm/test/tools/llvm-ar/error-opening-permission.test | 4 ++-- llvm/test/tools/llvm-ifs/fail-file-write.test | 5 ++--- llvm/test/tools/llvm-ranlib/error-opening-permission.test | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/llvm/test/tools/llvm-ar/error-opening-permission.test b/llvm/test/tools/llvm-ar/error-opening-permission.test index b42f95329a3c..7b880b51b75f 100644 --- a/llvm/test/tools/llvm-ar/error-opening-permission.test +++ b/llvm/test/tools/llvm-ar/error-opening-permission.test @@ -1,7 +1,7 @@ ## Unsupported on windows as marking files "unreadable" ## is non-trivial on windows. -# UNSUPPORTED: system-windows -# REQUIRES: non-root-user +## Unsupported on evm as CI currently runs with root permission. +# UNSUPPORTED: system-windows, target=evm{{.*}} # RUN: rm -rf %t && mkdir -p %t # RUN: echo file1 > %t/1.txt diff --git a/llvm/test/tools/llvm-ifs/fail-file-write.test b/llvm/test/tools/llvm-ifs/fail-file-write.test index f13500f22620..3321f91e5347 100644 --- a/llvm/test/tools/llvm-ifs/fail-file-write.test +++ b/llvm/test/tools/llvm-ifs/fail-file-write.test @@ -1,7 +1,6 @@ ## Test failing to write output file on non-windows platforms. - -# UNSUPPORTED: system-windows -# REQUIRES: non-root-user +## Unsupported on evm as CI currently runs with root permission. +# UNSUPPORTED: system-windows, target=evm{{.*}} # RUN: rm -rf %t.TestDir # RUN: mkdir %t.TestDir # RUN: touch %t.TestDir/Output.TestFile diff --git a/llvm/test/tools/llvm-ranlib/error-opening-permission.test b/llvm/test/tools/llvm-ranlib/error-opening-permission.test index be56962112e6..7123742bdc11 100644 --- a/llvm/test/tools/llvm-ranlib/error-opening-permission.test +++ b/llvm/test/tools/llvm-ranlib/error-opening-permission.test @@ -1,6 +1,6 @@ ## Unsupported on windows as marking files "unreadable" is non-trivial on windows. -# UNSUPPORTED: system-windows -# REQUIRES: non-root-user +## Unsupported on evm as CI currently runs with root permission. +# UNSUPPORTED: system-windows, target=evm{{.*}} # RUN: rm -rf %t && split-file %s %t && cd %t # RUN: yaml2obj 1.yaml -o 1.o From 549306e7f572e08b036a4f08ad5ce0ea11658b55 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 24 Dec 2024 09:43:41 +0200 Subject: [PATCH 049/203] [LIT] Enable unexpectedly passes llvm-ar tests on darwin --- llvm/test/tools/llvm-ar/extract.test | 1 - llvm/test/tools/llvm-ar/print.test | 1 - 2 files changed, 2 deletions(-) diff --git a/llvm/test/tools/llvm-ar/extract.test b/llvm/test/tools/llvm-ar/extract.test index ccad703f3a14..ab22feb4c3ec 100644 --- a/llvm/test/tools/llvm-ar/extract.test +++ b/llvm/test/tools/llvm-ar/extract.test @@ -1,5 +1,4 @@ ## Test extract operation. -# XFAIL: system-darwin # RUN: rm -rf %t && mkdir -p %t/extracted/ diff --git a/llvm/test/tools/llvm-ar/print.test b/llvm/test/tools/llvm-ar/print.test index a27cab4a227e..7c2b40480c00 100644 --- a/llvm/test/tools/llvm-ar/print.test +++ b/llvm/test/tools/llvm-ar/print.test @@ -1,5 +1,4 @@ ## Test Print output -# XFAIL: system-darwin # RUN: rm -rf %t && mkdir -p %t # RUN: echo file1 > %t/1.txt From 981f750f96e270d432239400c953cc470197399c Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 8 Aug 2024 17:31:43 +0200 Subject: [PATCH 050/203] [cmake] Disable multi-threading for a Windows based OS. This is a workaround for suspicious crashes/hanging ups happening with lld-as-a-library. TODO: ##677, needs to figure out exact reason. --- llvm/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 12618966c4ad..9d946450b0b1 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -546,7 +546,10 @@ option(LLVM_ENABLE_LIBEDIT "Use libedit if available." ON) option(LLVM_ENABLE_LIBPFM "Use libpfm for performance counters if available." ON) # On z/OS, threads cannot be used because TLS is not supported. -if (CMAKE_SYSTEM_NAME MATCHES "OS390") +# EVM local begin +# TODO: #677, investigate hanging up of solidity-compiler tests on windows. +if (CMAKE_SYSTEM_NAME MATCHES "OS390" OR CMAKE_SYSTEM_NAME MATCHES "Windows") +# EVM local end option(LLVM_ENABLE_THREADS "Use threads if available." OFF) else() option(LLVM_ENABLE_THREADS "Use threads if available." ON) From 3c1a31d776ffa328706d5e7d3d6e78a05488a1cb Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 28 Mar 2024 13:11:57 +0200 Subject: [PATCH 051/203] [.gitignore] Extend the pattern for build directories --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 20c4f52cd378..e249915a801c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,8 +26,8 @@ # Ignore the user specified CMake presets in subproject directories. /*/CMakeUserPresets.json -# Nested build directory -/build* +# Nested build directories +/build*/ #==============================================================================# # Explicit files to ignore (only matches one). From 96b9a35e8ee874ac369261ac7ba254dee0563415 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 28 Mar 2024 13:05:11 +0200 Subject: [PATCH 052/203] Remove mainline's workflows and SECURITY.md --- .github/workflows/README.md | 1 - .github/workflows/build-ci-container.yml | 112 ---- .../workflows/ci-post-commit-analyzer-run.py | 34 -- .github/workflows/ci-post-commit-analyzer.yml | 95 ---- .github/workflows/clang-tests.yml | 38 -- .../github-action-ci/bootstrap.patch | 13 - .../github-action-ci/stage1.Dockerfile | 44 -- .../github-action-ci/stage2.Dockerfile | 29 -- .../containers/github-action-ci/storage.conf | 4 - .github/workflows/docs.yml | 169 ------ .github/workflows/email-check.yaml | 46 -- .github/workflows/get-llvm-version/action.yml | 26 - .github/workflows/issue-release-workflow.yml | 69 --- .github/workflows/issue-subscriber.yml | 37 -- .github/workflows/issue-write.yml | 157 ------ .github/workflows/libclang-abi-tests.yml | 168 ------ .github/workflows/libclang-python-tests.yml | 47 -- .github/workflows/libclc-tests.yml | 39 -- .github/workflows/libcxx-build-and-test.yaml | 234 --------- .../libcxx-check-generated-files.yml | 24 - .../libcxx-restart-preempted-jobs.yaml | 132 ----- .github/workflows/lld-tests.yml | 38 -- .github/workflows/lldb-tests.yml | 39 -- .github/workflows/llvm-bugs.yml | 63 --- .github/workflows/llvm-project-tests.yml | 148 ------ .../workflows/llvm-project-workflow-tests.yml | 32 -- .github/workflows/llvm-tests.yml | 192 ------- .github/workflows/merged-prs.yml | 41 -- .github/workflows/new-issues.yml | 23 - .github/workflows/new-prs.yml | 75 --- .github/workflows/pr-code-format.yml | 96 ---- .github/workflows/pr-request-release-note.yml | 49 -- .github/workflows/pr-subscriber.yml | 34 -- .github/workflows/release-binaries-all.yml | 98 ---- .../release-binaries-save-stage/action.yml | 44 -- .../release-binaries-setup-stage/action.yml | 59 --- .github/workflows/release-binaries.yml | 492 ------------------ .github/workflows/release-documentation.yml | 91 ---- .github/workflows/release-doxygen.yml | 72 --- .github/workflows/release-lit.yml | 79 --- .github/workflows/release-sources.yml | 108 ---- .github/workflows/release-tasks.yml | 123 ----- .github/workflows/scorecard.yml | 62 --- .../workflows/set-release-binary-outputs.sh | 34 -- .github/workflows/spirv-tests.yml | 29 -- .../unprivileged-download-artifact/action.yml | 81 --- .github/workflows/version-check.py | 36 -- .github/workflows/version-check.yml | 31 -- SECURITY.md | 5 - 49 files changed, 3792 deletions(-) delete mode 100644 .github/workflows/README.md delete mode 100644 .github/workflows/build-ci-container.yml delete mode 100644 .github/workflows/ci-post-commit-analyzer-run.py delete mode 100644 .github/workflows/ci-post-commit-analyzer.yml delete mode 100644 .github/workflows/clang-tests.yml delete mode 100644 .github/workflows/containers/github-action-ci/bootstrap.patch delete mode 100644 .github/workflows/containers/github-action-ci/stage1.Dockerfile delete mode 100644 .github/workflows/containers/github-action-ci/stage2.Dockerfile delete mode 100644 .github/workflows/containers/github-action-ci/storage.conf delete mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/email-check.yaml delete mode 100644 .github/workflows/get-llvm-version/action.yml delete mode 100644 .github/workflows/issue-release-workflow.yml delete mode 100644 .github/workflows/issue-subscriber.yml delete mode 100644 .github/workflows/issue-write.yml delete mode 100644 .github/workflows/libclang-abi-tests.yml delete mode 100644 .github/workflows/libclang-python-tests.yml delete mode 100644 .github/workflows/libclc-tests.yml delete mode 100644 .github/workflows/libcxx-build-and-test.yaml delete mode 100644 .github/workflows/libcxx-check-generated-files.yml delete mode 100644 .github/workflows/libcxx-restart-preempted-jobs.yaml delete mode 100644 .github/workflows/lld-tests.yml delete mode 100644 .github/workflows/lldb-tests.yml delete mode 100644 .github/workflows/llvm-bugs.yml delete mode 100644 .github/workflows/llvm-project-tests.yml delete mode 100644 .github/workflows/llvm-project-workflow-tests.yml delete mode 100644 .github/workflows/llvm-tests.yml delete mode 100644 .github/workflows/merged-prs.yml delete mode 100644 .github/workflows/new-issues.yml delete mode 100644 .github/workflows/new-prs.yml delete mode 100644 .github/workflows/pr-code-format.yml delete mode 100644 .github/workflows/pr-request-release-note.yml delete mode 100644 .github/workflows/pr-subscriber.yml delete mode 100644 .github/workflows/release-binaries-all.yml delete mode 100644 .github/workflows/release-binaries-save-stage/action.yml delete mode 100644 .github/workflows/release-binaries-setup-stage/action.yml delete mode 100644 .github/workflows/release-binaries.yml delete mode 100644 .github/workflows/release-documentation.yml delete mode 100644 .github/workflows/release-doxygen.yml delete mode 100644 .github/workflows/release-lit.yml delete mode 100644 .github/workflows/release-sources.yml delete mode 100644 .github/workflows/release-tasks.yml delete mode 100644 .github/workflows/scorecard.yml delete mode 100644 .github/workflows/set-release-binary-outputs.sh delete mode 100644 .github/workflows/spirv-tests.yml delete mode 100644 .github/workflows/unprivileged-download-artifact/action.yml delete mode 100755 .github/workflows/version-check.py delete mode 100644 .github/workflows/version-check.yml delete mode 100644 SECURITY.md diff --git a/.github/workflows/README.md b/.github/workflows/README.md deleted file mode 100644 index ce34d2337e9c..000000000000 --- a/.github/workflows/README.md +++ /dev/null @@ -1 +0,0 @@ -Github action workflows should be stored in this directory. diff --git a/.github/workflows/build-ci-container.yml b/.github/workflows/build-ci-container.yml deleted file mode 100644 index 28fc7de2ee06..000000000000 --- a/.github/workflows/build-ci-container.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: Build CI Container - -permissions: - contents: read - -on: - push: - branches: - - main - paths: - - .github/workflows/build-ci-container.yml - - '.github/workflows/containers/github-action-ci/**' - pull_request: - branches: - - main - paths: - - .github/workflows/build-ci-container.yml - - '.github/workflows/containers/github-action-ci/**' - -jobs: - # TODO(boomanaiden154): Switch this back to a single stage build when we can - # run this on the self-hosted runners and don't have to do it this way to - # avoid timeouts. - build-ci-container-stage1: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - steps: - - name: Checkout LLVM - uses: actions/checkout@v4 - with: - sparse-checkout: .github/workflows/containers/github-action-ci/ - - name: Change podman Root Direcotry - run: | - mkdir -p ~/.config/containers - sudo mkdir -p /mnt/podman - sudo chown `whoami`:`whoami` /mnt/podman - cp ./.github/workflows/containers/github-action-ci/storage.conf ~/.config/containers/storage.conf - podman info - - name: Build container stage1 - working-directory: ./.github/workflows/containers/github-action-ci/ - run: | - podman build -t stage1-toolchain --target stage1-toolchain -f stage1.Dockerfile . - - name: Save container image - run: | - podman save stage1-toolchain > stage1-toolchain.tar - - name: Upload container image - uses: actions/upload-artifact@v4 - with: - name: stage1-toolchain - path: stage1-toolchain.tar - retention-days: 1 - build-ci-container-stage2: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: build-ci-container-stage1 - permissions: - packages: write - steps: - - name: Write Variables - id: vars - run: | - tag=`date +%s` - container_name="ghcr.io/$GITHUB_REPOSITORY_OWNER/ci-ubuntu-22.04" - echo "container-name=$container_name" >> $GITHUB_OUTPUT - echo "container-name-tag=$container_name:$tag" >> $GITHUB_OUTPUT - - - name: Checkout LLVM - uses: actions/checkout@v4 - with: - sparse-checkout: .github/workflows/containers/github-action-ci/ - - - name: Change podman Root Direcotry - run: | - mkdir -p ~/.config/containers - sudo mkdir -p /mnt/podman - sudo chown `whoami`:`whoami` /mnt/podman - cp ./.github/workflows/containers/github-action-ci/storage.conf ~/.config/containers/storage.conf - podman info - - # Download the container image into /mnt/podman rather than - # $GITHUB_WORKSPACE to avoid space limitations on the default drive - # and use the permissions setup for /mnt/podman. - - name: Download stage1-toolchain - uses: actions/download-artifact@v4 - with: - name: stage1-toolchain - path: /mnt/podman - - - name: Load stage1-toolchain - run: | - podman load -i /mnt/podman/stage1-toolchain.tar - - - name: Build Container - working-directory: ./.github/workflows/containers/github-action-ci/ - run: | - podman build -t ${{ steps.vars.outputs.container-name-tag }} -f stage2.Dockerfile . - podman tag ${{ steps.vars.outputs.container-name-tag }} ${{ steps.vars.outputs.container-name }}:latest - - - name: Test Container - run: | - for image in ${{ steps.vars.outputs.container-name-tag }} ${{ steps.vars.outputs.container-name }}; do - podman run --rm -it $image /usr/bin/bash -x -c 'printf '\''#include \nint main(int argc, char **argv) { std::cout << "Hello\\n"; }'\'' | clang++ -x c++ - && ./a.out | grep Hello' - done - - - name: Push Container - if: github.event_name == 'push' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - podman login -u ${{ github.actor }} -p $GITHUB_TOKEN ghcr.io - podman push ${{ steps.vars.outputs.container-name-tag }} - podman push ${{ steps.vars.outputs.container-name }}:latest diff --git a/.github/workflows/ci-post-commit-analyzer-run.py b/.github/workflows/ci-post-commit-analyzer-run.py deleted file mode 100644 index e5f52d3b2fa6..000000000000 --- a/.github/workflows/ci-post-commit-analyzer-run.py +++ /dev/null @@ -1,34 +0,0 @@ -import json -import multiprocessing -import os -import re -import subprocess -import sys - - -def run_analyzer(data): - os.chdir(data["directory"]) - command = ( - data["command"] - + f" --analyze --analyzer-output html -o analyzer-results -Xclang -analyzer-config -Xclang max-nodes=75000" - ) - print(command) - subprocess.run(command, shell=True, check=True) - - -def pool_error(e): - print("Error analyzing file:", e) - - -def main(): - db_path = sys.argv[1] - database = json.load(open(db_path)) - - with multiprocessing.Pool() as pool: - pool.map_async(run_analyzer, [k for k in database], error_callback=pool_error) - pool.close() - pool.join() - - -if __name__ == "__main__": - main() diff --git a/.github/workflows/ci-post-commit-analyzer.yml b/.github/workflows/ci-post-commit-analyzer.yml deleted file mode 100644 index d614dd07b3a4..000000000000 --- a/.github/workflows/ci-post-commit-analyzer.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Post-Commit Static Analyzer - -permissions: - contents: read - -on: - push: - branches: - - 'release/**' - paths: - - 'clang/**' - - 'llvm/**' - - '.github/workflows/ci-post-commit-analyzer.yml' - pull_request: - types: - - opened - - synchronize - - reopened - - closed - paths: - - '.github/workflows/ci-post-commit-analyzer.yml' - - '.github/workflows/ci-post-commit-analyzer-run.py' - schedule: - - cron: '30 0 * * *' - -concurrency: - group: >- - llvm-project-${{ github.workflow }}-${{ github.event_name == 'pull_request' && - ( github.event.pull_request.number || github.ref) }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - post-commit-analyzer: - if: >- - github.repository_owner == 'llvm' && - github.event.action != 'closed' - runs-on: ubuntu-22.04 - container: - image: 'ghcr.io/llvm/ci-ubuntu-22.04:latest' - env: - LLVM_VERSION: 18 - steps: - - name: Checkout Source - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1 - with: - # A full build of llvm, clang, lld, and lldb takes about 250MB - # of ccache space. There's not much reason to have more than this, - # because we usually won't need to save cache entries from older - # builds. Also, there is an overall 10GB cache limit, and each - # run creates a new cache entry so we want to ensure that we have - # enough cache space for all the tests to run at once and still - # fit under the 10 GB limit. - # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 - max-size: 2G - key: post-commit-analyzer - variant: sccache - - - name: Configure - run: | - cmake -B build -S llvm -G Ninja \ - -DLLVM_ENABLE_ASSERTIONS=ON \ - -DLLVM_ENABLE_PROJECTS=clang \ - -DLLVM_BUILD_LLVM_DYLIB=ON \ - -DLLVM_LINK_LLVM_DYLIB=ON \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \ - -DCMAKE_C_COMPILER_LAUNCHER=sccache \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DCLANG_INCLUDE_TESTS=OFF \ - -DCMAKE_BUILD_TYPE=Release - - - name: Build - run: | - # FIXME: We need to build all the generated header files in order to be able to run - # the analyzer on every file. Building libLLVM and libclang is probably overkill for - # this, but it's better than building every target. - ninja -v -C build libLLVM.so libclang.so - - # Run the analyzer. - python3 .github/workflows/ci-post-commit-analyzer-run.py build/compile_commands.json - - scan-build --generate-index-only build/analyzer-results - - - name: Upload Results - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: analyzer-results - path: 'build/analyzer-results/*' - diff --git a/.github/workflows/clang-tests.yml b/.github/workflows/clang-tests.yml deleted file mode 100644 index 2569ce19518e..000000000000 --- a/.github/workflows/clang-tests.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Clang Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/clang-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/clang-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_clang: - if: github.repository_owner == 'llvm' - name: Test clang,lldb,libclc - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-clang - projects: clang;lldb;libclc diff --git a/.github/workflows/containers/github-action-ci/bootstrap.patch b/.github/workflows/containers/github-action-ci/bootstrap.patch deleted file mode 100644 index 55631c54a396..000000000000 --- a/.github/workflows/containers/github-action-ci/bootstrap.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/clang/cmake/caches/BOLT-PGO.cmake b/clang/cmake/caches/BOLT-PGO.cmake -index 1a04ca9a74e5..d092820e4115 100644 ---- a/clang/cmake/caches/BOLT-PGO.cmake -+++ b/clang/cmake/caches/BOLT-PGO.cmake -@@ -4,6 +4,8 @@ set(CLANG_BOOTSTRAP_TARGETS - stage2-clang-bolt - stage2-distribution - stage2-install-distribution -+ clang -+ lld - CACHE STRING "") - set(BOOTSTRAP_CLANG_BOOTSTRAP_TARGETS - clang-bolt diff --git a/.github/workflows/containers/github-action-ci/stage1.Dockerfile b/.github/workflows/containers/github-action-ci/stage1.Dockerfile deleted file mode 100644 index 8c6bcf463841..000000000000 --- a/.github/workflows/containers/github-action-ci/stage1.Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM docker.io/library/ubuntu:22.04 as base -ENV LLVM_SYSROOT=/opt/llvm - -FROM base as stage1-toolchain -ENV LLVM_VERSION=17.0.6 - -RUN apt-get update && \ - apt-get install -y \ - wget \ - gcc \ - g++ \ - cmake \ - ninja-build \ - python3 \ - git \ - curl - -RUN curl -O -L https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-$LLVM_VERSION.tar.gz && tar -xf llvmorg-$LLVM_VERSION.tar.gz - -WORKDIR /llvm-project-llvmorg-$LLVM_VERSION - -COPY bootstrap.patch / - -# TODO(boomanaiden154): Remove the patch pulled from a LLVM PR once we bump -# the toolchain to version 18 and the patch is in-tree. -# TODO(boomanaiden154): Remove the bootstrap patch once we unsplit the build -# and no longer need to explicitly build the stage2 dependencies. -RUN curl https://github.com/llvm/llvm-project/commit/dd0356d741aefa25ece973d6cc4b55dcb73b84b4.patch | patch -p1 && cat /bootstrap.patch | patch -p1 - -RUN mkdir build - -RUN cmake -B ./build -G Ninja ./llvm \ - -C ./clang/cmake/caches/BOLT-PGO.cmake \ - -DBOOTSTRAP_LLVM_ENABLE_LLD=ON \ - -DBOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LLD=ON \ - -DPGO_INSTRUMENT_LTO=Thin \ - -DLLVM_ENABLE_RUNTIMES="compiler-rt" \ - -DCMAKE_INSTALL_PREFIX="$LLVM_SYSROOT" \ - -DLLVM_ENABLE_PROJECTS="bolt;clang;lld;clang-tools-extra" \ - -DLLVM_DISTRIBUTION_COMPONENTS="lld;compiler-rt;clang-format;scan-build" \ - -DCLANG_DEFAULT_LINKER="lld" \ - -DBOOTSTRAP_CLANG_PGO_TRAINING_DATA_SOURCE_DIR=/llvm-project-llvmorg-$LLVM_VERSION/llvm - -RUN ninja -C ./build stage2-instrumented-clang stage2-instrumented-lld diff --git a/.github/workflows/containers/github-action-ci/stage2.Dockerfile b/.github/workflows/containers/github-action-ci/stage2.Dockerfile deleted file mode 100644 index 0ca0da87734c..000000000000 --- a/.github/workflows/containers/github-action-ci/stage2.Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM docker.io/library/ubuntu:22.04 as base -ENV LLVM_SYSROOT=/opt/llvm - -FROM stage1-toolchain AS stage2-toolchain - -RUN ninja -C ./build stage2-clang-bolt stage2-install-distribution && ninja -C ./build install-distribution && rm -rf ./build - -FROM base - -COPY --from=stage2-toolchain $LLVM_SYSROOT $LLVM_SYSROOT - -# Need to install curl for hendrikmuhs/ccache-action -# Need nodejs for some of the GitHub actions. -# Need perl-modules for clang analyzer tests. -# Need git for SPIRV-Tools tests. -RUN apt-get update && \ - apt-get install -y \ - binutils \ - cmake \ - curl \ - git \ - libstdc++-11-dev \ - ninja-build \ - nodejs \ - perl-modules \ - python3-psutil - -ENV LLVM_SYSROOT=$LLVM_SYSROOT -ENV PATH=${LLVM_SYSROOT}/bin:${PATH} diff --git a/.github/workflows/containers/github-action-ci/storage.conf b/.github/workflows/containers/github-action-ci/storage.conf deleted file mode 100644 index 60f295ff1e96..000000000000 --- a/.github/workflows/containers/github-action-ci/storage.conf +++ /dev/null @@ -1,4 +0,0 @@ -[storage] - driver = "overlay" - runroot = "/mnt/podman/container" - graphroot = "/mnt/podman/image" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 800e92915735..000000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,169 +0,0 @@ -# LLVM Documentation CI -# 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 - -name: "Test documentation build" - -permissions: - contents: read - -on: - push: - branches: - - 'main' - paths: - - 'llvm/docs/**' - - 'clang/docs/**' - - 'clang/include/clang/Basic/AttrDocs.td' - - 'clang/include/clang/Driver/ClangOptionDocs.td' - - 'clang/include/clang/Basic/DiagnosticDocs.td' - - 'clang-tools-extra/docs/**' - - 'lldb/docs/**' - - 'libunwind/docs/**' - - 'libcxx/docs/**' - - 'libc/docs/**' - - 'lld/docs/**' - - 'openmp/docs/**' - - 'polly/docs/**' - - 'flang/docs/**' - - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - - '.github/workflows/docs.yml' - pull_request: - paths: - - 'llvm/docs/**' - - 'clang/docs/**' - - 'clang/include/clang/Basic/AttrDocs.td' - - 'clang/include/clang/Driver/ClangOptionDocs.td' - - 'clang/include/clang/Basic/DiagnosticDocs.td' - - 'clang-tools-extra/docs/**' - - 'lldb/docs/**' - - 'libunwind/docs/**' - - 'libcxx/docs/**' - - 'libc/docs/**' - - 'lld/docs/**' - - 'openmp/docs/**' - - 'polly/docs/**' - - 'flang/docs/**' - - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - - '.github/workflows/docs.yml' - -jobs: - check-docs-build: - name: "Test documentation build" - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - # Don't fetch before checking for file changes to force the file changes - # action to use the Github API in pull requests. If it's a push to a - # branch we can't use the Github API to get the diff, so we need to have - # a local checkout beforehand. - - name: Fetch LLVM sources (Push) - if: ${{ github.event_name == 'push' }} - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - name: Get subprojects that have doc changes - id: docs-changed-subprojects - uses: tj-actions/changed-files@v39 - with: - files_yaml: | - llvm: - - 'llvm/docs/**' - clang: - - 'clang/docs/**' - - 'clang/include/clang/Basic/AttrDocs.td' - - 'clang/include/clang/Driver/ClangOptionDocs.td' - - 'clang/include/clang/Basic/DiagnosticDocs.td' - clang-tools-extra: - - 'clang-tools-extra/docs/**' - lldb: - - 'lldb/docs/**' - libunwind: - - 'libunwind/docs/**' - libcxx: - - 'libcxx/docs/**' - libc: - - 'libc/docs/**' - lld: - - 'lld/docs/**' - openmp: - - 'openmp/docs/**' - polly: - - 'polly/docs/**' - flang: - - 'flang/docs/**' - - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - - name: Fetch LLVM sources (PR) - if: ${{ github.event_name == 'pull_request' }} - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - name: Setup Python env - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - cache-dependency-path: 'llvm/docs/requirements.txt' - - name: Install python dependencies - run: pip install -r llvm/docs/requirements.txt - - name: Install system dependencies - run: | - sudo apt-get update - # swig and graphviz are lldb specific dependencies - sudo apt-get install -y cmake ninja-build swig graphviz - - name: Build LLVM docs - if: steps.docs-changed-subprojects.outputs.llvm_any_changed == 'true' - run: | - cmake -B llvm-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C llvm-build docs-llvm-html docs-llvm-man - - name: Build Clang docs - if: steps.docs-changed-subprojects.outputs.clang_any_changed == 'true' - run: | - cmake -B clang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C clang-build docs-clang-html docs-clang-man - - name: Build clang-tools-extra docs - if: steps.docs-changed-subprojects.outputs.clang-tools-extra_any_changed == 'true' - run: | - cmake -B clang-tools-extra-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C clang-tools-extra-build docs-clang-tools-html docs-clang-tools-man - - name: Build LLDB docs - if: steps.docs-changed-subprojects.outputs.lldb_any_changed == 'true' - run: | - cmake -B lldb-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lldb" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C lldb-build docs-lldb-html docs-lldb-man - - name: Build libunwind docs - if: steps.docs-changed-subprojects.outputs.libunwind_any_changed == 'true' - run: | - cmake -B libunwind-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes - TZ=UTC ninja -C libunwind-build docs-libunwind-html - - name: Build libcxx docs - if: steps.docs-changed-subprojects.outputs.libcxx_any_changed == 'true' - run: | - cmake -B libcxx-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libcxxabi;libcxx;libunwind" -DLLVM_ENABLE_SPHINX=ON ./runtimes - TZ=UTC ninja -C libcxx-build docs-libcxx-html - - name: Build libc docs - if: steps.docs-changed-subprojects.outputs.libc_any_changed == 'true' - run: | - cmake -B libc-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RUNTIMES="libc" -DLLVM_ENABLE_SPHINX=ON ./runtimes - TZ=UTC ninja -C libc-build docs-libc-html - - name: Build LLD docs - if: steps.docs-changed-subprojects.outputs.lld_any_changed == 'true' - run: | - cmake -B lld-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="lld" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C lld-build docs-lld-html - - name: Build OpenMP docs - if: steps.docs-changed-subprojects.outputs.openmp_any_changed == 'true' - run: | - cmake -B openmp-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;openmp" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C openmp-build docs-openmp-html - - name: Build Polly docs - if: steps.docs-changed-subprojects.outputs.polly_any_changed == 'true' - run: | - cmake -B polly-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="polly" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C polly-build docs-polly-html docs-polly-man - - name: Build Flang docs - if: steps.docs-changed-subprojects.outputs.flang_any_changed == 'true' - run: | - cmake -B flang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;mlir;flang" -DLLVM_ENABLE_SPHINX=ON ./llvm - TZ=UTC ninja -C flang-build docs-flang-html diff --git a/.github/workflows/email-check.yaml b/.github/workflows/email-check.yaml deleted file mode 100644 index 8f32d020975f..000000000000 --- a/.github/workflows/email-check.yaml +++ /dev/null @@ -1,46 +0,0 @@ -name: "Check for private emails used in PRs" - -on: - pull_request: - types: - - opened - -permissions: - contents: read - -jobs: - validate_email: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Extract author email - id: author - run: | - git log -1 - echo "EMAIL=$(git show -s --format='%ae' HEAD~0)" >> $GITHUB_OUTPUT - # Create empty comment file - echo "[]" > comments - - - name: Validate author email - if: ${{ endsWith(steps.author.outputs.EMAIL, 'noreply.github.com') }} - env: - COMMENT: >- - ⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
- Please turn off [Keep my email addresses private](https://github.com/settings/emails) setting in your account.
- See [LLVM Discourse](https://discourse.llvm.org/t/hidden-emails-on-github-should-we-do-something-about-it) for more information. - run: | - cat << EOF > comments - [{"body" : "$COMMENT"}] - EOF - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: workflow-args - path: | - comments diff --git a/.github/workflows/get-llvm-version/action.yml b/.github/workflows/get-llvm-version/action.yml deleted file mode 100644 index 2218d926fc13..000000000000 --- a/.github/workflows/get-llvm-version/action.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Get LLVM Version -description: >- - Get the LLVM version from the llvm-project source tree. This action assumes - the llvm-project sources have already been checked out into GITHUB_WORKSPACE. - -outputs: - major: - description: LLVM major version - value: ${{ steps.version.outputs.major }} - minor: - description: LLVM minor version - value: ${{ steps.version.outputs.minor }} - patch: - description: LLVM patch version - value: ${{ steps.version.outputs.patch }} - -runs: - using: "composite" - steps: - - name: Get Version - shell: bash - id: version - run: | - for v in major minor patch; do - echo "$v=`llvm/utils/release/get-llvm-version.sh --$v`" >> $GITHUB_OUTPUT - done diff --git a/.github/workflows/issue-release-workflow.yml b/.github/workflows/issue-release-workflow.yml deleted file mode 100644 index 5027d4f3ea6f..000000000000 --- a/.github/workflows/issue-release-workflow.yml +++ /dev/null @@ -1,69 +0,0 @@ -# This contains the workflow definitions that allow users to test backports -# to the release branch using comments on issues. -# -# /cherry-pick <...> -# -# This comment will attempt to cherry-pick the given commits to the latest -# release branch (release/Y.x) and if successful, push the result to a branch -# on github. -# -# /branch // -# -# This comment will create a pull request from to the latest release -# branch. - -name: Issue Release Workflow - -permissions: - contents: read - -on: - issue_comment: - types: - - created - - edited - issues: - types: - - opened - -env: - COMMENT_BODY: ${{ github.event.action == 'opened' && github.event.issue.body || github.event.comment.body }} - -jobs: - backport-commits: - name: Backport Commits - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - if: >- - (github.repository == 'llvm/llvm-project') && - !startswith(github.event.comment.body, '') && - contains(github.event.action == 'opened' && github.event.issue.body || github.event.comment.body, '/cherry-pick') - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - repository: llvm/llvm-project - # GitHub stores the token used for checkout and uses it for pushes - # too, but we want to use a different token for pushing, so we need - # to disable persist-credentials here. - persist-credentials: false - fetch-depth: 0 - - - name: Setup Environment - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - ./llvm/utils/git/github-automation.py --token ${{ github.token }} setup-llvmbot-git - - - name: Backport Commits - run: | - printf "%s" "$COMMENT_BODY" | - ./llvm/utils/git/github-automation.py \ - --repo "$GITHUB_REPOSITORY" \ - --token "${{ secrets.RELEASE_WORKFLOW_PR_CREATE }}" \ - release-workflow \ - --branch-repo-token ${{ secrets.RELEASE_WORKFLOW_PUSH_SECRET }} \ - --issue-number ${{ github.event.issue.number }} \ - --requested-by ${{ (github.event.action == 'opened' && github.event.issue.user.login) || github.event.comment.user.login }} \ - auto diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml deleted file mode 100644 index ef4fdf441819..000000000000 --- a/.github/workflows/issue-subscriber.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Issue Subscriber - -on: - issues: - types: - - labeled - -permissions: - contents: read - -jobs: - auto-subscribe: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Update watchers - working-directory: ./llvm/utils/git/ - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - env: - LABEL_NAME: ${{ github.event.label.name }} - run: | - python3 ./github-automation.py \ - --token '${{ secrets.ISSUE_SUBSCRIBER_TOKEN }}' \ - issue-subscriber \ - --issue-number '${{ github.event.issue.number }}' \ - --label-name "$LABEL_NAME" diff --git a/.github/workflows/issue-write.yml b/.github/workflows/issue-write.yml deleted file mode 100644 index 5334157a7fd2..000000000000 --- a/.github/workflows/issue-write.yml +++ /dev/null @@ -1,157 +0,0 @@ -name: Comment on an issue - -on: - workflow_run: - workflows: - - "Check code formatting" - - "Check for private emails used in PRs" - - "PR Request Release Note" - types: - - completed - -permissions: - contents: read - -jobs: - pr-comment: - runs-on: ubuntu-latest - permissions: - pull-requests: write - if: > - github.event.workflow_run.event == 'pull_request' && - ( - github.event.workflow_run.conclusion == 'success' || - github.event.workflow_run.conclusion == 'failure' - ) - steps: - - name: Fetch Sources - uses: actions/checkout@v4 - with: - sparse-checkout: | - .github/workflows/unprivileged-download-artifact/action.yml - sparse-checkout-cone-mode: false - - name: 'Download artifact' - uses: ./.github/workflows/unprivileged-download-artifact - id: download-artifact - with: - run-id: ${{ github.event.workflow_run.id }} - artifact-name: workflow-args - - - name: 'Comment on PR' - if: steps.download-artifact.outputs.artifact-id != '' - uses: actions/github-script@v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - var fs = require('fs'); - const comments = JSON.parse(fs.readFileSync('./comments')); - if (!comments || comments.length == 0) { - return; - } - - let runInfo = await github.actions.getWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id - }); - - console.log(runInfo); - - - // Query to find the number of the pull request that triggered this job. - // The associated pull requests are based off of the branch name, so if - // you create a pull request for a branch, close it, and then create - // another pull request with the same branch, then this query will return - // two associated pull requests. This is why we have to fetch all the - // associated pull requests and then iterate through them to find the - // one that is open. - const gql_query = ` - query($repo_owner : String!, $repo_name : String!, $branch: String!) { - repository(owner: $repo_owner, name: $repo_name) { - ref (qualifiedName: $branch) { - associatedPullRequests(first: 100) { - nodes { - baseRepository { - owner { - login - } - } - number - state - } - } - } - } - } - ` - const gql_variables = { - repo_owner: runInfo.data.head_repository.owner.login, - repo_name: runInfo.data.head_repository.name, - branch: runInfo.data.head_branch - } - const gql_result = await github.graphql(gql_query, gql_variables); - console.log(gql_result); - // If the branch for the PR was deleted before this job has a chance - // to run, then the ref will be null. This can happen if someone: - // 1. Rebase the PR, which triggers some workflow. - // 2. Immediately merges the PR and deletes the branch. - // 3. The workflow finishes and triggers this job. - if (!gql_result.repository.ref) { - console.log("Ref has been deleted"); - return; - } - console.log(gql_result.repository.ref.associatedPullRequests.nodes); - - var pr_number = 0; - gql_result.repository.ref.associatedPullRequests.nodes.forEach((pr) => { - - // The largest PR number is the one we care about. The only way - // to have more than one associated pull requests is if all the - // old pull requests are in the closed state. - if (pr.baseRepository.owner.login = context.repo.owner && pr.number > pr_number) { - pr_number = pr.number; - } - }); - if (pr_number == 0) { - console.log("Error retrieving pull request number"); - return; - } - - await comments.forEach(function (comment) { - if (comment.id) { - // Security check: Ensure that this comment was created by - // the github-actions bot, so a malicious input won't overwrite - // a user's comment. - github.issues.getComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: comment.id - }).then((old_comment) => { - console.log(old_comment); - if (old_comment.data.user.login != "github-actions[bot]") { - console.log("Invalid comment id: " + comment.id); - return; - } - github.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr_number, - comment_id: comment.id, - body: comment.body - }); - }); - } else { - github.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr_number, - body: comment.body - }); - } - }); - - - name: Dump comments file - if: >- - always() && - steps.download-artifact.outputs.artifact-id != '' - run: cat comments diff --git a/.github/workflows/libclang-abi-tests.yml b/.github/workflows/libclang-abi-tests.yml deleted file mode 100644 index 9e839ff49e28..000000000000 --- a/.github/workflows/libclang-abi-tests.yml +++ /dev/null @@ -1,168 +0,0 @@ -name: libclang ABI Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/libclang-abi-tests.yml' - pull_request: - branches: - - 'release/**' - paths: - - 'clang/**' - - '.github/workflows/libclang-abi-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - abi-dump-setup: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - outputs: - BASELINE_REF: ${{ steps.vars.outputs.BASELINE_REF }} - ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} - ABI_LIBS: ${{ steps.vars.outputs.ABI_LIBS }} - BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} - LLVM_VERSION_MAJOR: ${{ steps.version.outputs.major }} - LLVM_VERSION_MINOR: ${{ steps.version.outputs.minor }} - LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }} - steps: - - name: Checkout source - uses: actions/checkout@v4 - with: - fetch-depth: 250 - - - name: Get LLVM version - id: version - uses: ./.github/workflows/get-llvm-version - - - name: Setup Variables - id: vars - run: | - remote_repo='https://github.com/llvm/llvm-project' - if [ ${{ steps.version.outputs.patch }} -eq 0 ]; then - major_version=$(( ${{ steps.version.outputs.major }} - 1)) - baseline_ref="llvmorg-$major_version.1.0" - - # If there is a minor release, we want to use that as the base line. - minor_ref=$(git ls-remote --refs -t "$remote_repo" llvmorg-"$major_version".[1-9].[0-9] | tail -n1 | grep -o 'llvmorg-.\+' || true) - if [ -n "$minor_ref" ]; then - baseline_ref="$minor_ref" - else - # Check if we have a release candidate - rc_ref=$(git ls-remote --refs -t "$remote_repo" llvmorg-"$major_version".[1-9].[0-9]-rc* | tail -n1 | grep -o 'llvmorg-.\+' || true) - if [ -n "$rc_ref" ]; then - baseline_ref="$rc_ref" - fi - fi - { - echo "BASELINE_VERSION_MAJOR=$major_version" - echo "BASELINE_REF=$baseline_ref" - echo "ABI_HEADERS=clang-c" - echo "ABI_LIBS=libclang.so" - } >> "$GITHUB_OUTPUT" - else - { - echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.major }}" - echo "BASELINE_REF=llvmorg-${{ steps.version.outputs.major }}.1.0" - echo "ABI_HEADERS=." - echo "ABI_LIBS=libclang.so libclang-cpp.so" - } >> "$GITHUB_OUTPUT" - fi - - abi-dump: - if: github.repository_owner == 'llvm' - needs: abi-dump-setup - runs-on: ubuntu-latest - strategy: - matrix: - name: - - build-baseline - - build-latest - include: - - name: build-baseline - llvm_version_major: ${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MAJOR }} - ref: ${{ needs.abi-dump-setup.outputs.BASELINE_REF }} - repo: llvm/llvm-project - - name: build-latest - llvm_version_major: ${{ needs.abi-dump-setup.outputs.LLVM_VERSION_MAJOR }} - ref: ${{ github.sha }} - repo: ${{ github.repository }} - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@main - - name: Install abi-compliance-checker - run: | - sudo apt-get install abi-dumper autoconf pkg-config - - name: Install universal-ctags - run: | - git clone https://github.com/universal-ctags/ctags.git - cd ctags - ./autogen.sh - ./configure - sudo make install - - name: Download source code - uses: llvm/actions/get-llvm-project-src@main - with: - ref: ${{ matrix.ref }} - repo: ${{ matrix.repo }} - - name: Configure - run: | - mkdir install - cmake -B build -S llvm -G Ninja -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON -DCMAKE_C_FLAGS_DEBUG="-g1 -Og" -DCMAKE_CXX_FLAGS_DEBUG="-g1 -Og" -DCMAKE_INSTALL_PREFIX="$(pwd)"/install llvm - - name: Build - run: ninja -C build/ ${{ needs.abi-dump-setup.outputs.ABI_LIBS }} install-clang-headers - - name: Dump ABI - run: | - parallel abi-dumper -lver ${{ matrix.ref }} -skip-cxx -public-headers ./install/include/${{ needs.abi-dump-setup.outputs.ABI_HEADERS }} -o {}-${{ matrix.ref }}.abi ./build/lib/{} ::: ${{ needs.abi-dump-setup.outputs.ABI_LIBS }} - for lib in ${{ needs.abi-dump-setup.outputs.ABI_LIBS }}; do - # Remove symbol versioning from dumps, so we can compare across major versions. - sed -i 's/LLVM_[0-9]\+/LLVM_NOVERSION/' $lib-${{ matrix.ref }}.abi - done - - name: Upload ABI file - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.name }} - path: '*${{ matrix.ref }}.abi' - - abi-compare: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: - - abi-dump-setup - - abi-dump - steps: - - name: Download baseline - uses: actions/download-artifact@v3 - with: - name: build-baseline - path: build-baseline - - name: Download latest - uses: actions/download-artifact@v3 - with: - name: build-latest - path: build-latest - - - name: Install abi-compliance-checker - run: sudo apt-get install abi-compliance-checker - - name: Compare ABI - run: | - for lib in ${{ needs.abi-dump-setup.outputs.ABI_LIBS }}; do - abi-compliance-checker -lib $lib -old build-baseline/$lib*.abi -new build-latest/$lib*.abi - done - - name: Upload ABI Comparison - if: always() - uses: actions/upload-artifact@v3 - with: - name: compat-report-${{ github.sha }} - path: compat_reports/ diff --git a/.github/workflows/libclang-python-tests.yml b/.github/workflows/libclang-python-tests.yml deleted file mode 100644 index 43ded0af3ac2..000000000000 --- a/.github/workflows/libclang-python-tests.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Libclang Python Binding Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'main' - paths: - - 'clang/bindings/python/**' - - 'clang/tools/libclang/**' - - 'clang/CMakeList.txt' - - '.github/workflows/libclang-python-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - pull_request: - paths: - - 'clang/bindings/python/**' - - 'clang/tools/libclang/**' - - 'clang/CMakeList.txt' - - '.github/workflows/libclang-python-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check-clang-python: - # Build libclang and then run the libclang Python binding's unit tests. - name: Build and run Python unit tests - if: github.repository == 'llvm/llvm-project' - strategy: - fail-fast: false - matrix: - python-version: ["3.8", "3.11"] - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-clang-python - projects: clang - # There is an issue running on "windows-2019". - # See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082. - os_list: '["ubuntu-latest"]' - python_version: ${{ matrix.python-version }} diff --git a/.github/workflows/libclc-tests.yml b/.github/workflows/libclc-tests.yml deleted file mode 100644 index 23192f776a98..000000000000 --- a/.github/workflows/libclc-tests.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: libclc Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'libclc/**' - - '.github/workflows/libclc-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'libclc/**' - - '.github/workflows/libclc-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_libclc: - if: github.repository_owner == 'llvm' - name: Test libclc - uses: ./.github/workflows/llvm-project-tests.yml - with: - projects: clang;libclc diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml deleted file mode 100644 index 1456f245cf7c..000000000000 --- a/.github/workflows/libcxx-build-and-test.yaml +++ /dev/null @@ -1,234 +0,0 @@ -# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github). -# -# We split the configurations in multiple stages with the intent of saving compute time -# when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`. -# We try to run the CI configurations with the most signal in the first stage. -# -# Stages 1 & 2 are meant to be "smoke tests", and are meant to catch most build/test failures quickly and without using -# too many resources. -# Stage 3 is "everything else", and is meant to catch breakages on more niche or unique configurations. -# -# Therefore, we "fail-fast" for any failures during stages 1 & 2, meaning any job failing cancels all other running jobs, -# under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way. -# However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific -# -name: Build and Test libc++ -on: - pull_request: - paths: - - 'libcxx/**' - - 'libcxxabi/**' - - 'libunwind/**' - - 'runtimes/**' - - 'cmake/**' - - '.github/workflows/libcxx-build-and-test.yaml' - schedule: - # Run nightly at 08:00 UTC (aka 00:00 Pacific, aka 03:00 Eastern) - - cron: '0 8 * * *' - -permissions: - contents: read # Default everything to read-only - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - - -env: - # LLVM POST-BRANCH bump version - # LLVM POST-BRANCH add compiler test for ToT - 1, e.g. "Clang 17" - # LLVM RELEASE bump remove compiler ToT - 3, e.g. "Clang 15" - LLVM_HEAD_VERSION: "19" # Used compiler, update POST-BRANCH. - LLVM_PREVIOUS_VERSION: "18" - LLVM_OLDEST_VERSION: "17" - GCC_STABLE_VERSION: "13" - LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-19" - CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics" - - -jobs: - stage1: - if: github.repository_owner == 'llvm' - runs-on: libcxx-runners-8-set - continue-on-error: false - strategy: - fail-fast: false - matrix: - config: [ - 'generic-cxx03', - 'generic-cxx26', - 'generic-modules' - ] - cc: [ 'clang-19' ] - cxx: [ 'clang++-19' ] - include: - - config: 'generic-gcc' - cc: 'gcc-14' - cxx: 'g++-14' - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.config }}.${{ matrix.cxx }} - run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} - env: - CC: ${{ matrix.cc }} - CXX: ${{ matrix.cxx }} - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - if: always() - with: - name: ${{ matrix.config }}-${{ matrix.cxx }}-results - path: | - **/test-results.xml - **/*.abilist - **/CMakeError.log - **/CMakeOutput.log - **/crash_diagnostics/* - stage2: - if: github.repository_owner == 'llvm' - runs-on: libcxx-runners-8-set - needs: [ stage1 ] - continue-on-error: false - strategy: - fail-fast: false - matrix: - config: [ - 'generic-cxx11', - 'generic-cxx14', - 'generic-cxx17', - 'generic-cxx20', - 'generic-cxx23' - ] - cc: [ 'clang-19' ] - cxx: [ 'clang++-19' ] - include: - - config: 'generic-gcc-cxx11' - cc: 'gcc-14' - cxx: 'g++-14' - - config: 'generic-cxx23' - cc: 'clang-17' - cxx: 'clang++-17' - - config: 'generic-cxx26' - cc: 'clang-18' - cxx: 'clang++-18' - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.config }} - run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} - env: - CC: ${{ matrix.cc }} - CXX: ${{ matrix.cxx }} - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - if: always() # Upload artifacts even if the build or test suite fails - with: - name: ${{ matrix.config }}-${{ matrix.cxx }}-results - path: | - **/test-results.xml - **/*.abilist - **/CMakeError.log - **/CMakeOutput.log - **/crash_diagnostics/* - stage3: - if: github.repository_owner == 'llvm' - needs: [ stage1, stage2 ] - continue-on-error: false - strategy: - fail-fast: false - max-parallel: 8 - matrix: - config: [ - 'generic-abi-unstable', - 'generic-hardening-mode-debug', - 'generic-hardening-mode-extensive', - 'generic-hardening-mode-fast', - 'generic-hardening-mode-fast-with-abi-breaks', - 'generic-merged', - 'generic-modules-lsv', - 'generic-no-exceptions', - 'generic-no-experimental', - 'generic-no-filesystem', - 'generic-no-localization', - 'generic-no-random_device', - 'generic-no-threads', - 'generic-no-tzdb', - 'generic-no-unicode', - 'generic-no-wide-characters', - 'generic-no-rtti', - 'generic-optimized-speed', - 'generic-static', - # TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive - # or don't provide much value since the benchmark run results are too noise on the bots. - 'benchmarks', - 'bootstrapping-build' - ] - machine: [ 'libcxx-runners-8-set' ] - include: - - config: 'generic-cxx26' - machine: libcxx-runners-8-set - - config: 'generic-asan' - machine: libcxx-runners-8-set - - config: 'generic-tsan' - machine: libcxx-runners-8-set - - config: 'generic-ubsan' - machine: libcxx-runners-8-set - # Use a larger machine for MSAN to avoid timeout and memory allocation issues. - - config: 'generic-msan' - machine: libcxx-runners-8-set - runs-on: ${{ matrix.machine }} - steps: - - uses: actions/checkout@v4 - - name: ${{ matrix.config }} - run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} - env: - CC: clang-19 - CXX: clang++-19 - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 - if: always() - with: - name: ${{ matrix.config }}-results - path: | - **/test-results.xml - **/*.abilist - **/CMakeError.log - **/CMakeOutput.log - **/crash_diagnostics/* - windows: - runs-on: windows-2022 - needs: [ stage1 ] - strategy: - fail-fast: false - matrix: - include: - - { config: clang-cl-dll, mingw: false } - - { config: clang-cl-static, mingw: false } - - { config: clang-cl-no-vcruntime, mingw: false } - - { config: clang-cl-debug, mingw: false } - - { config: clang-cl-static-crt, mingw: false } - - { config: mingw-dll, mingw: true } - - { config: mingw-static, mingw: true } - - { config: mingw-dll-i686, mingw: true } - steps: - - uses: actions/checkout@v4 - - name: Install dependencies - run: | - choco install -y ninja - pip install psutil - - name: Install a current LLVM - if: ${{ matrix.mingw != true }} - run: | - choco install -y llvm --version=18.1.6 --allow-downgrade - - name: Install llvm-mingw - if: ${{ matrix.mingw == true }} - run: | - curl -LO https://github.com/mstorsjo/llvm-mingw/releases/download/20240606/llvm-mingw-20240606-ucrt-x86_64.zip - powershell Expand-Archive llvm-mingw*.zip -DestinationPath . - del llvm-mingw*.zip - mv llvm-mingw* c:\llvm-mingw - echo "c:\llvm-mingw\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append - - name: Add Git Bash to the path - run: | - echo "c:\Program Files\Git\usr\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append - - name: Set up the MSVC dev environment - if: ${{ matrix.mingw != true }} - uses: ilammy/msvc-dev-cmd@v1 - - name: Build and test - run: | - bash libcxx/utils/ci/run-buildbot ${{ matrix.config }} diff --git a/.github/workflows/libcxx-check-generated-files.yml b/.github/workflows/libcxx-check-generated-files.yml deleted file mode 100644 index 570055624b2a..000000000000 --- a/.github/workflows/libcxx-check-generated-files.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: "Check libc++ generated files" -on: - pull_request: - paths: - - 'libcxx/**' - -permissions: - contents: read - -jobs: - check_generated_files: - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - - - name: Install dependencies - uses: aminya/setup-cpp@v1 - with: - clangformat: 17.0.1 - ninja: true - - - name: Check generated files - run: libcxx/utils/ci/run-buildbot check-generated-output diff --git a/.github/workflows/libcxx-restart-preempted-jobs.yaml b/.github/workflows/libcxx-restart-preempted-jobs.yaml deleted file mode 100644 index 21879ce19c27..000000000000 --- a/.github/workflows/libcxx-restart-preempted-jobs.yaml +++ /dev/null @@ -1,132 +0,0 @@ -name: Restart Preempted Libc++ Workflow - -# The libc++ builders run on preemptable VMs, which can be shutdown at any time. -# This workflow identifies when a workflow run was canceled due to the VM being preempted, -# and restarts the workflow run. - -# We identify a canceled workflow run by checking the annotations of the check runs in the check suite, -# which should contain the message "The runner has received a shutdown signal." - -# Note: If a job is both preempted and also contains a non-preemption failure, we do not restart the workflow. - -on: - workflow_run: - workflows: [Build and Test libc\+\+] - types: - - completed - -permissions: - contents: read - -jobs: - restart: - if: github.repository_owner == 'llvm' && (github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'cancelled') - name: "Restart Job" - permissions: - statuses: read - checks: write - actions: write - runs-on: ubuntu-latest - steps: - - name: "Restart Job" - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1 - with: - script: | - const failure_regex = /Process completed with exit code 1./ - const preemption_regex = /The runner has received a shutdown signal/ - - const wf_run = context.payload.workflow_run - core.notice(`Running on "${wf_run.display_title}" by @${wf_run.actor.login} (event: ${wf_run.event})\nWorkflow run URL: ${wf_run.html_url}`) - - - async function create_check_run(conclusion, message) { - // Create a check run on the given workflow run to indicate if - // we are restarting the workflow or not. - if (conclusion != 'success' && conclusion != 'skipped' && conclusion != 'neutral') { - core.setFailed('Invalid conclusion: ' + conclusion) - } - await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: 'Restart Preempted Job', - head_sha: wf_run.head_sha, - status: 'completed', - conclusion: conclusion, - output: { - title: 'Restarted Preempted Job', - summary: message - } - }) - } - - console.log('Listing check runs for suite') - const check_suites = await github.rest.checks.listForSuite({ - owner: context.repo.owner, - repo: context.repo.repo, - check_suite_id: context.payload.workflow_run.check_suite_id, - per_page: 100 // FIXME: We don't have 100 check runs yet, but we should handle this better. - }) - - check_run_ids = []; - for (check_run of check_suites.data.check_runs) { - console.log('Checking check run: ' + check_run.id); - if (check_run.status != 'completed') { - console.log('Check run was not completed. Skipping.'); - continue; - } - if (check_run.conclusion != 'failure' && check_run.conclusion != 'cancelled') { - console.log('Check run had conclusion: ' + check_run.conclusion + '. Skipping.'); - continue; - } - check_run_ids.push(check_run.id); - } - - has_preempted_job = false; - - for (check_run_id of check_run_ids) { - console.log('Listing annotations for check run: ' + check_run_id); - - annotations = await github.rest.checks.listAnnotations({ - owner: context.repo.owner, - repo: context.repo.repo, - check_run_id: check_run_id - }) - - for (annotation of annotations.data) { - if (annotation.annotation_level != 'failure') { - continue; - } - - const preemption_match = annotation.message.match(preemption_regex); - - if (preemption_match != null) { - console.log('Found preemption message: ' + annotation.message); - has_preempted_job = true; - } - - const failure_match = annotation.message.match(failure_regex); - if (failure_match != null) { - // We only want to restart the workflow if all of the failures were due to preemption. - // We don't want to restart the workflow if there were other failures. - core.notice('Choosing not to rerun workflow because we found a non-preemption failure' + - 'Failure message: "' + annotation.message + '"'); - await create_check_run('skipped', 'Choosing not to rerun workflow because we found a non-preemption failure\n' - + 'Failure message: ' + annotation.message) - return; - } - } - } - - if (!has_preempted_job) { - core.notice('No preempted jobs found. Not restarting workflow.'); - await create_check_run('neutral', 'No preempted jobs found. Not restarting workflow.') - return; - } - - core.notice("Restarted workflow: " + context.payload.workflow_run.id); - await github.rest.actions.reRunWorkflowFailedJobs({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id - }) - await create_check_run('success', 'Restarted workflow run due to preempted job') diff --git a/.github/workflows/lld-tests.yml b/.github/workflows/lld-tests.yml deleted file mode 100644 index 599c0975fa68..000000000000 --- a/.github/workflows/lld-tests.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: LLD Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'lld/**' - - '.github/workflows/lld-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'lld/**' - - '.github/workflows/lld-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_lld: - if: github.repository_owner == 'llvm' - name: Test lld - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-lld - projects: lld diff --git a/.github/workflows/lldb-tests.yml b/.github/workflows/lldb-tests.yml deleted file mode 100644 index 6bb972195625..000000000000 --- a/.github/workflows/lldb-tests.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: lldb Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'lldb/**' - - '.github/workflows/lldb-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - pull_request: - branches: - - 'release/**' - paths: - - 'lldb/**' - - '.github/workflows/lldb-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - - '!clang/**' - - '!llvm/**' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - build_lldb: - if: github.repository_owner == 'llvm' - name: Build lldb - uses: ./.github/workflows/llvm-project-tests.yml - with: - projects: clang;lldb diff --git a/.github/workflows/llvm-bugs.yml b/.github/workflows/llvm-bugs.yml deleted file mode 100644 index c392078fa452..000000000000 --- a/.github/workflows/llvm-bugs.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: LLVM Bugs notifier - -permissions: - contents: read - issues: read - -on: - issues: - types: - - opened - -jobs: - auto-subscribe: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - uses: actions/setup-node@v4 - with: - node-version: 18 - check-latest: true - - run: npm install mailgun.js form-data - - name: Send notification - uses: actions/github-script@v6 - env: - MAILGUN_API_KEY: ${{ secrets.LLVM_BUGS_KEY }} - with: - script: | - const Mailgun = require('mailgun.js'); - const formData = require('form-data'); - - const mailgun = new Mailgun(formData); - const DOMAIN = 'email.llvm.org'; - - const mg = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY }); - - github.rest.issues.get({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo - }) - .then((issue) => { - const payload = { - author : issue.data.user.login, - issue : issue.data.number, - title : issue.data.title, - url : issue.data.html_url, - labels : issue.data.labels.map((label) => label.name), - assignee : issue.data.assignees.map((assignee) => assignee.login), - body : issue.data.body - }; - - const data = { - from: 'LLVM Bugs ', - to: 'llvm-bugs@lists.llvm.org', - subject: `[Bug ${issue.data.number}] ${issue.data.title}`, - template: 'new-github-issue', - 'o:tracking-clicks': 'no', - 'h:X-Mailgun-Variables': JSON.stringify(payload) - }; - - return mg.messages.create(DOMAIN, data); - }) - .then((msg) => console.log(msg)); diff --git a/.github/workflows/llvm-project-tests.yml b/.github/workflows/llvm-project-tests.yml deleted file mode 100644 index 17a54be16bad..000000000000 --- a/.github/workflows/llvm-project-tests.yml +++ /dev/null @@ -1,148 +0,0 @@ -name: LLVM Project Tests - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - build_target: - required: false - projects: - required: false - extra_cmake_args: - required: false - os_list: - required: false - default: '["ubuntu-latest", "windows-2019", "macOS-13"]' - python_version: - required: false - type: string - default: '3.11' - workflow_call: - inputs: - build_target: - required: false - type: string - default: "all" - - projects: - required: true - type: string - - extra_cmake_args: - required: false - type: string - - os_list: - required: false - type: string - # Use windows-2019 due to: - # https://developercommunity.visualstudio.com/t/Prev-Issue---with-__assume-isnan-/1597317 - default: '["ubuntu-latest", "windows-2019", "macOS-13"]' - - python_version: - required: false - type: string - default: '3.11' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - # If the group name here is the same as the group name in the workflow that includes - # this one, then the action will try to wait on itself and get stuck. - group: llvm-project-${{ github.workflow }}-${{ inputs.projects }}${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - lit-tests: - name: Lit Tests - runs-on: ${{ matrix.os }} - container: - image: ${{(startsWith(matrix.os, 'ubuntu') && 'ghcr.io/llvm/ci-ubuntu-22.04:latest') || null}} - volumes: - - /mnt/:/mnt/ - strategy: - fail-fast: false - matrix: - os: ${{ fromJSON(inputs.os_list) }} - steps: - - name: Setup Windows - if: startsWith(matrix.os, 'windows') - uses: llvm/actions/setup-windows@main - with: - arch: amd64 - # On Windows, starting with win19/20220814.1, cmake choose the 32-bit - # python3.10.6 libraries instead of the 64-bit libraries when building - # lldb. Using this setup-python action to make 3.10 the default - # python fixes this. - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ inputs.python_version }} - - name: Install Ninja - if: runner.os != 'Linux' - uses: llvm/actions/install-ninja@main - # actions/checkout deletes any existing files in the new git directory, - # so this needs to either run before ccache-action or it has to use - # clean: false. - - uses: actions/checkout@v4 - with: - fetch-depth: 250 - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1 - with: - # A full build of llvm, clang, lld, and lldb takes about 250MB - # of ccache space. There's not much reason to have more than this, - # because we usually won't need to save cache entries from older - # builds. Also, there is an overall 10GB cache limit, and each - # run creates a new cache entry so we want to ensure that we have - # enough cache space for all the tests to run at once and still - # fit under the 10 GB limit. - # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 - max-size: 2G - key: ${{ matrix.os }} - variant: sccache - - name: Build and Test - env: - # Workaround for https://github.com/actions/virtual-environments/issues/5900. - # This should be a no-op for non-mac OSes - PKG_CONFIG_PATH: /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig//12 - shell: bash - id: build-llvm - run: | - if [ "${{ runner.os }}" == "Linux" ]; then - builddir="/mnt/build/" - mkdir -p $builddir - extra_cmake_args="-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang" - else - builddir="$(pwd)"/build - fi - if [ "${{ runner.os }}" == "macOS" ]; then - # Workaround test failure on some lld tests on MacOS - # https://github.com/llvm/llvm-project/issues/81967 - extra_cmake_args="-DLLVM_DISABLE_ASSEMBLY_FILES=ON" - fi - echo "llvm-builddir=$builddir" >> "$GITHUB_OUTPUT" - cmake -G Ninja \ - -B "$builddir" \ - -S llvm \ - -DLLVM_ENABLE_PROJECTS="${{ inputs.projects }}" \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_ENABLE_ASSERTIONS=ON \ - -DLLDB_INCLUDE_TESTS=OFF \ - -DLIBCLC_TARGETS_TO_BUILD="amdgcn--;amdgcn--amdhsa;r600--;nvptx--;nvptx64--;nvptx--nvidiacl;nvptx64--nvidiacl" \ - -DCMAKE_C_COMPILER_LAUNCHER=sccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \ - $extra_cmake_args \ - ${{ inputs.extra_cmake_args }} - ninja -C "$builddir" '${{ inputs.build_target }}' - - - name: Build and Test libclc - if: "!startsWith(matrix.os, 'windows') && contains(inputs.projects, 'libclc')" - env: - LLVM_BUILDDIR: ${{ steps.build-llvm.outputs.llvm-builddir }} - run: | - # The libclc tests don't have a generated check target so all we can - # do is build it. - ninja -C "$LLVM_BUILDDIR" diff --git a/.github/workflows/llvm-project-workflow-tests.yml b/.github/workflows/llvm-project-workflow-tests.yml deleted file mode 100644 index a2539b279be0..000000000000 --- a/.github/workflows/llvm-project-workflow-tests.yml +++ /dev/null @@ -1,32 +0,0 @@ -# This workflow will test the llvm-project-tests workflow in PRs -# targetting the main branch. Since this workflow doesn't normally -# run on main PRs, we need some way to test it to ensure new updates -# don't break it. - -name: LLVM Workflow Test - -permissions: - contents: read - -on: - pull_request: - branches: - - 'main' - paths: - - '.github/workflows/llvm-project-tests.yml' - - '.github/workflows/llvm-project-workflow-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - llvm-test: - if: github.repository_owner == 'llvm' - name: Build and Test - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-all - projects: clang;lld;libclc;lldb diff --git a/.github/workflows/llvm-tests.yml b/.github/workflows/llvm-tests.yml deleted file mode 100644 index 26e644229aaa..000000000000 --- a/.github/workflows/llvm-tests.yml +++ /dev/null @@ -1,192 +0,0 @@ -name: LLVM Tests - -permissions: - contents: read - -on: - workflow_dispatch: - push: - branches: - - 'release/**' - paths: - - 'llvm/**' - - '.github/workflows/llvm-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - pull_request: - branches: - - 'release/**' - paths: - - 'llvm/**' - - '.github/workflows/llvm-tests.yml' - - '.github/workflows/llvm-project-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check-all: - if: github.repository_owner == 'llvm' - name: Build and Test - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-all - projects: clang;lld;libclc;lldb - - abi-dump-setup: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - outputs: - BASELINE_REF: ${{ steps.vars.outputs.BASELINE_REF }} - ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} - BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} - BASELINE_VERSION_MINOR: ${{ steps.vars.outputs.BASELINE_VERSION_MINOR }} - LLVM_VERSION_MAJOR: ${{ steps.version.outputs.major }} - LLVM_VERSION_MINOR: ${{ steps.version.outputs.minor }} - LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }} - steps: - - name: Checkout source - uses: actions/checkout@v4 - with: - fetch-depth: 250 - - - name: Get LLVM version - id: version - uses: ./.github/workflows/get-llvm-version - - - name: Setup Variables - id: vars - run: | - # C++ ABI: - # 18.1.0 we aren't doing ABI checks. - # 18.1.1 We want to check 18.1.0. - # C ABI: - # 18.1.0 We want to check 17.0.x - # 18.1.1 We want to check 18.1.0 - echo "BASELINE_VERSION_MINOR=1" >> "$GITHUB_OUTPUT" - if [ ${{ steps.version.outputs.patch }} -eq 0 ]; then - { - echo "BASELINE_VERSION_MAJOR=$(( ${{ steps.version.outputs.major }} - 1))" - echo "ABI_HEADERS=llvm-c" - } >> "$GITHUB_OUTPUT" - else - { - echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.major }}" - echo "ABI_HEADERS=." - } >> "$GITHUB_OUTPUT" - fi - - abi-dump: - if: github.repository_owner == 'llvm' - needs: abi-dump-setup - runs-on: ubuntu-latest - strategy: - matrix: - name: - - build-baseline - - build-latest - include: - - name: build-baseline - llvm_version_major: ${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MAJOR }} - ref: llvmorg-${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MAJOR }}.${{ needs.abi-dump-setup.outputs.BASELINE_VERSION_MINOR }}.0 - repo: llvm/llvm-project - - name: build-latest - llvm_version_major: ${{ needs.abi-dump-setup.outputs.LLVM_VERSION_MAJOR }} - ref: ${{ github.sha }} - repo: ${{ github.repository }} - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@main - - name: Install abi-compliance-checker - run: | - sudo apt-get install abi-dumper autoconf pkg-config - - name: Install universal-ctags - run: | - git clone https://github.com/universal-ctags/ctags.git - cd ctags - ./autogen.sh - ./configure - sudo make install - - name: Download source code - uses: llvm/actions/get-llvm-project-src@main - with: - ref: ${{ matrix.ref }} - repo: ${{ matrix.repo }} - - name: Configure - run: | - mkdir install - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_LLVM_DYLIB=ON -DCMAKE_C_FLAGS_DEBUG="-g1 -Og" -DCMAKE_CXX_FLAGS_DEBUG="-g1 -Og" -DCMAKE_INSTALL_PREFIX="$(pwd)"/install llvm - - name: Build - # Need to run install-LLVM twice to ensure the symlink is installed (this is a bug). - run: | - ninja -C build install-LLVM - ninja -C build install-LLVM - ninja -C build install-llvm-headers - - name: Dump ABI - run: | - if [ "${{ needs.abi-dump-setup.outputs.ABI_HEADERS }}" = "llvm-c" ]; then - nm ./install/lib/libLLVM.so | awk "/T _LLVM/ || /T LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" | cut -d ' ' -f 3 > llvm.symbols - # Even though the -symbols-list option doesn't seem to filter out the symbols, I believe it speeds up processing, so I'm leaving it in. - export EXTRA_ARGS="-symbols-list llvm.symbols" - else - touch llvm.symbols - fi - abi-dumper $EXTRA_ARGS -lver ${{ matrix.ref }} -skip-cxx -public-headers ./install/include/${{ needs.abi-dump-setup.outputs.ABI_HEADERS }} -o ${{ matrix.ref }}.abi ./install/lib/libLLVM.so - # Remove symbol versioning from dumps, so we can compare across major versions. - sed -i 's/LLVM_${{ matrix.llvm_version_major }}/LLVM_NOVERSION/' ${{ matrix.ref }}.abi - - name: Upload ABI file - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.name }} - path: ${{ matrix.ref }}.abi - - - name: Upload symbol list file - if: matrix.name == 'build-baseline' - uses: actions/upload-artifact@v3 - with: - name: symbol-list - path: llvm.symbols - - abi-compare: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: - - abi-dump-setup - - abi-dump - steps: - - name: Download baseline - uses: actions/download-artifact@v3 - with: - name: build-baseline - path: build-baseline - - name: Download latest - uses: actions/download-artifact@v3 - with: - name: build-latest - path: build-latest - - name: Download symbol list - uses: actions/download-artifact@v3 - with: - name: symbol-list - path: symbol-list - - - name: Install abi-compliance-checker - run: sudo apt-get install abi-compliance-checker - - name: Compare ABI - run: | - if [ -s symbol-list/llvm.symbols ]; then - # This option doesn't seem to work with the ABI dumper, so passing it here. - export EXTRA_ARGS="-symbols-list symbol-list/llvm.symbols" - fi - # FIXME: Reading of gzip'd abi files on the GitHub runners stop - # working some time in March of 2021, likely due to a change in the - # runner's environment. - abi-compliance-checker $EXTRA_ARGS -l libLLVM.so -old build-baseline/*.abi -new build-latest/*.abi || test "${{ needs.abi-dump-setup.outputs.ABI_HEADERS }}" = "llvm-c" - - name: Upload ABI Comparison - if: always() - uses: actions/upload-artifact@v3 - with: - name: compat-report-${{ github.sha }} - path: compat_reports/ diff --git a/.github/workflows/merged-prs.yml b/.github/workflows/merged-prs.yml deleted file mode 100644 index e29afd4097f9..000000000000 --- a/.github/workflows/merged-prs.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: "Add buildbot information to first PRs from new contributors" - -permissions: - contents: read - -on: - # It's safe to use pull_request_target here, because we aren't checking out - # code from the pull request branch. - # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - pull_request_target: - types: - - closed - -jobs: - buildbot_comment: - runs-on: ubuntu-latest - permissions: - pull-requests: write - if: >- - (github.repository == 'llvm/llvm-project') && - (github.event.pull_request.merged == true) - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Add Buildbot information comment - working-directory: ./llvm/utils/git/ - run: | - python3 ./github-automation.py \ - --token '${{ secrets.GITHUB_TOKEN }}' \ - pr-buildbot-information \ - --issue-number "${{ github.event.pull_request.number }}" \ - --author "${{ github.event.pull_request.user.login }}" diff --git a/.github/workflows/new-issues.yml b/.github/workflows/new-issues.yml deleted file mode 100644 index ed15fdb9fba6..000000000000 --- a/.github/workflows/new-issues.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Labeling new issues -on: - issues: - types: ['opened'] - -permissions: - contents: read - -jobs: - automate-issues-labels: - permissions: - issues: write - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - uses: llvm/actions/issue-labeler@main - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - configuration-path: .github/new-issues-labeler.yml - include-title: 1 - include-body: 0 - sync-labels: 0 - enable-versioned-regex: 0 diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml deleted file mode 100644 index 88175d6f8d64..000000000000 --- a/.github/workflows/new-prs.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: "Labelling new pull requests" - -permissions: - contents: read - -on: - # It's safe to use pull_request_target here, because we aren't checking out - # code from the pull request branch. - # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - pull_request_target: - types: - - opened - - reopened - - ready_for_review - - synchronize - -jobs: - greeter: - runs-on: ubuntu-latest - permissions: - pull-requests: write - # Only comment on PRs that have been opened for the first time, by someone - # new to LLVM or to GitHub as a whole. Ideally we'd look for FIRST_TIMER - # or FIRST_TIME_CONTRIBUTOR, but this does not appear to work. Instead check - # that we do not have any of the other author associations. - # See https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=opened#pull_request - # for all the possible values. - if: >- - (github.repository == 'llvm/llvm-project') && - (github.event.action == 'opened') && - (github.event.pull_request.author_association != 'COLLABORATOR') && - (github.event.pull_request.author_association != 'CONTRIBUTOR') && - (github.event.pull_request.author_association != 'MANNEQUIN') && - (github.event.pull_request.author_association != 'MEMBER') && - (github.event.pull_request.author_association != 'OWNER') - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Greet Author - working-directory: ./llvm/utils/git/ - run: | - python3 ./github-automation.py \ - --token '${{ secrets.GITHUB_TOKEN }}' \ - pr-greeter \ - --issue-number "${{ github.event.pull_request.number }}" - - automate-prs-labels: - # Greet first so that only the author gets that notification. - needs: greeter - runs-on: ubuntu-latest - # Ignore PRs with more than 10 commits. Pull requests with a lot of - # commits tend to be accidents usually when someone made a mistake while trying - # to rebase. We want to ignore these pull requests to avoid excessive - # notifications. - # always() means that even if greeter is skipped, this job will run. - if: > - always() && github.repository == 'llvm/llvm-project' && - github.event.pull_request.draft == false && - github.event.pull_request.commits < 10 - steps: - - uses: actions/labeler@v4 - with: - configuration-path: .github/new-prs-labeler.yml - # workaround for https://github.com/actions/labeler/issues/112 - sync-labels: '' - repo-token: ${{ secrets.ISSUE_SUBSCRIBER_TOKEN }} diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml deleted file mode 100644 index 22357e5d99e4..000000000000 --- a/.github/workflows/pr-code-format.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: "Check code formatting" - -permissions: - contents: read - -on: - pull_request: - branches: - - main - -jobs: - code_formatter: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Checkout through merge base - uses: rmacklin/fetch-through-merge-base@v0 - with: - base_ref: ${{ github.event.pull_request.base.ref }} - head_ref: ${{ github.event.pull_request.head.sha }} - deepen_length: 500 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v39 - with: - separator: "," - skip_initial_fetch: true - - # We need to pull the script from the main branch, so that we ensure - # we get the latest version of this script. - - name: Fetch code formatting utils - uses: actions/checkout@v4 - with: - repository: ${{ github.repository }} - ref: ${{ github.base_ref }} - sparse-checkout: | - llvm/utils/git/requirements_formatting.txt - llvm/utils/git/code-format-helper.py - sparse-checkout-cone-mode: false - path: code-format-tools - - - name: "Listed files" - env: - CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - run: | - echo "Formatting files:" - echo "$CHANGED_FILES" - - - name: Install clang-format - uses: aminya/setup-cpp@v1 - with: - clangformat: 18.1.7 - - - name: Setup Python env - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - cache-dependency-path: 'code-format-tools/llvm/utils/git/requirements_formatting.txt' - - - name: Install python dependencies - run: pip install -r code-format-tools/llvm/utils/git/requirements_formatting.txt - - - name: Run code formatter - env: - GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} - START_REV: ${{ github.event.pull_request.base.sha }} - END_REV: ${{ github.event.pull_request.head.sha }} - CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - # TODO(boomanaiden154): Once clang v18 is released, we should be able - # to take advantage of the new --diff_from_common_commit option - # explicitly in code-format-helper.py and not have to diff starting at - # the merge base. - # Create an empty comments file so the pr-write job doesn't fail. - run: | - echo "[]" > comments && - python ./code-format-tools/llvm/utils/git/code-format-helper.py \ - --write-comment-to-file \ - --token ${{ secrets.GITHUB_TOKEN }} \ - --issue-number $GITHUB_PR_NUMBER \ - --start-rev $(git merge-base $START_REV $END_REV) \ - --end-rev $END_REV \ - --changed-files "$CHANGED_FILES" - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: workflow-args - path: | - comments diff --git a/.github/workflows/pr-request-release-note.yml b/.github/workflows/pr-request-release-note.yml deleted file mode 100644 index 2fa501dda16b..000000000000 --- a/.github/workflows/pr-request-release-note.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: PR Request Release Note - -permissions: - contents: read - -on: - pull_request: - types: - - closed - -jobs: - request-release-note: - if: >- - github.repository_owner == 'llvm' && - startsWith(github.ref, 'refs/heads/release') - - runs-on: ubuntu-latest - steps: - # We need to pull the script from the main branch, so that we ensure - # we get the latest version of this script. - - name: Checkout Scripts - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - sparse-checkout: | - llvm/utils/git/requirements.txt - llvm/utils/git/github-automation.py - sparse-checkout-cone-mode: false - - - name: Install Dependencies - run: | - pip install --require-hashes -r llvm/utils/git/requirements.txt - - - name: Request Release Note - env: - # We need to use an llvmbot token here, because we are mentioning a user. - GITHUB_TOKEN: ${{ github.token }} - run: | - python3 llvm/utils/git/github-automation.py \ - --repo "$GITHUB_REPOSITORY" \ - --token "$GITHUB_TOKEN" \ - request-release-note \ - --pr-number ${{ github.event.pull_request.number}} - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() - with: - name: workflow-args - path: | - comments diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml deleted file mode 100644 index 272d3e2f9ef8..000000000000 --- a/.github/workflows/pr-subscriber.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: PR Subscriber - -on: - pull_request_target: - types: - - labeled - -permissions: - contents: read - -jobs: - auto-subscribe: - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - steps: - - name: Checkout Automation Script - uses: actions/checkout@v4 - with: - sparse-checkout: llvm/utils/git/ - ref: main - - - name: Setup Automation Script - working-directory: ./llvm/utils/git/ - run: | - pip install --require-hashes -r requirements.txt - - - name: Update watchers - working-directory: ./llvm/utils/git/ - run: | - python3 ./github-automation.py \ - --token '${{ secrets.ISSUE_SUBSCRIBER_TOKEN }}' \ - pr-subscriber \ - --issue-number "${{ github.event.number }}" \ - --label-name "${{ github.event.label.name }}" diff --git a/.github/workflows/release-binaries-all.yml b/.github/workflows/release-binaries-all.yml deleted file mode 100644 index 394b0c74d24e..000000000000 --- a/.github/workflows/release-binaries-all.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Release Binaries All - -permissions: - contents: read # Default everything to read-only - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - - pull_request: - types: - - opened - - synchronize - - reopened - # When a PR is closed, we still start this workflow, but then skip - # all the jobs, which makes it effectively a no-op. The reason to - # do this is that it allows us to take advantage of concurrency groups - # to cancel in progress CI jobs whenever the PR is closed. - - closed - paths: - - '.github/workflows/release-binaries-all.yml' - - '.github/workflows/release-binaries.yml' - - '.github/workflows/release-binaries-setup-stage/*' - - '.github/workflows/release-binaries-save-stage/*' - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || 'dispatch' }} - cancel-in-progress: True - -jobs: - setup-variables: - if: >- - (github.event_name != 'pull_request' || github.event.action != 'closed') - runs-on: ubuntu-22.04 - outputs: - release-version: ${{ steps.vars.outputs.release-version }} - upload: ${{ steps.vars.outputs.upload }} - steps: - - shell: bash - id: vars - run: | - upload="${{ inputs.upload }}" - release_version="${{ inputs.release-version }}" - if [ "${{ github.event_name }}" = "pull_request" ]; then - upload="false" - release_version="" - fi - echo "release-version=$release_version" >> "$GITHUB_OUTPUT" - echo "upload=$upload" >> "$GITHUB_OUTPUT" - - release-binaries-all: - name: Build Release Binaries - needs: - - setup-variables - permissions: - contents: write # For release uploads - id-token: write # For artifact attestations - attestations: write # For artifact attestations - strategy: - fail-fast: false - matrix: - runs-on: - - ubuntu-22.04 - - windows-2022 - - macos-13 - - macos-14 - - uses: ./.github/workflows/release-binaries.yml - with: - release-version: "${{ needs.setup-variables.outputs.release-version }}" - upload: ${{ needs.setup-variables.outputs.upload == 'true'}} - runs-on: "${{ matrix.runs-on }}" - secrets: - # This will be empty for pull_request events, but that's fine, because - # the release-binaries workflow does not use this secret for the - # pull_request event. - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} diff --git a/.github/workflows/release-binaries-save-stage/action.yml b/.github/workflows/release-binaries-save-stage/action.yml deleted file mode 100644 index f08088c7bc56..000000000000 --- a/.github/workflows/release-binaries-save-stage/action.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Save Stage -description: >- - Upload the source and binary directories from a build stage so that they - can be re-used in the next stage. This action is used to the release - binaries workflow into multiple stages to avoid the 6 hour timeout on - the GitHub hosted runners. -inputs: - build-prefix: - description: "Directory containing the build directory." - required: true - type: 'string' - -permissions: - contents: read - -runs: - using: "composite" - steps: - # We need to create an archive of the build directory, because it has too - # many files to upload. - - name: Package Build and Source Directories - shell: bash - run: | - # Remove .git/config to avoid leaking GITHUB_TOKEN stored there. - # See https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/ - rm -Rf .git/config - # Windows does not support symlinks, so we need to dereference them. - tar --exclude build/ ${{ (runner.os == 'Windows' && '-h') || '' }} -c . | zstd -T0 -c > ../llvm-project.tar.zst - mv ../llvm-project.tar.zst . - tar -C ${{ inputs.build-prefix }} -c build/ | zstd -T0 -c > build.tar.zst - - - name: Upload Stage 1 Source - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-source - path: llvm-project.tar.zst - retention-days: 2 - - - name: Upload Stage 1 Build Dir - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os}}-${{ runner.arch }}-${{ github.job }}-build - path: build.tar.zst - retention-days: 2 diff --git a/.github/workflows/release-binaries-setup-stage/action.yml b/.github/workflows/release-binaries-setup-stage/action.yml deleted file mode 100644 index f5e5db27e659..000000000000 --- a/.github/workflows/release-binaries-setup-stage/action.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Setup Stage -description: >- - Setup the next stage of the release binaries workflow. This sets up the - environment correctly for a new stage of the release binaries workflow - and also restores the source and build directory from the previous stage. - -inputs: - previous-artifact: - description: >- - A unique descriptor for the artifact from the previous stage. This will - be used to construct the final artifact pattern, which is: - $RUNNER_OS-$RUNNER_ARCH-$PREVIOUS_ARTIFACT-* - required: false - type: 'string' - -outputs: - build-prefix: - description: "Directory containing the build directory." - value: ${{ steps.build-prefix.outputs.build-prefix }} - -runs: - using: "composite" - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main - - - name: Setup Windows - if: startsWith(runner.os, 'Windows') - uses: llvm/actions/setup-windows@main - with: - arch: amd64 - - - name: Set Build Prefix - id: build-prefix - shell: bash - run: | - build_prefix=`pwd` - if [ "${{ runner.os }}" = "Linux" ]; then - sudo chown $USER:$USER /mnt/ - build_prefix=/mnt/ - fi - echo "build-prefix=$build_prefix" >> $GITHUB_OUTPUT - - - name: Download Previous Stage Artifact - if: ${{ inputs.previous-artifact }} - id: download - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 - with: - pattern: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.previous-artifact }}-* - merge-multiple: true - - - name: Unpack Artifact - if: ${{ steps.download.outputs.download-path }} - shell: bash - run: | - tar --zstd -xf llvm-project.tar.zst - rm llvm-project.tar.zst - tar --zstd -C ${{ steps.build-prefix.outputs.build-prefix}} -xf build.tar.zst - rm build.tar.zst diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml deleted file mode 100644 index f24e25879b96..000000000000 --- a/.github/workflows/release-binaries.yml +++ /dev/null @@ -1,492 +0,0 @@ -name: Release Binaries - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: false - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - runs-on: - description: "Runner to use for the build" - required: true - type: choice - options: - - ubuntu-22.04 - - windows-2022 - - macos-13 - - macos-14 - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: false - type: string - upload: - description: 'Upload binaries to the release page' - required: true - default: false - type: boolean - runs-on: - description: "Runner to use for the build" - required: true - type: string - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - - -permissions: - contents: read # Default everything to read-only - -jobs: - prepare: - name: Prepare to build binaries - runs-on: ${{ inputs.runs-on }} - if: github.repository == 'llvm/llvm-project' - outputs: - release-version: ${{ steps.vars.outputs.release-version }} - ref: ${{ steps.vars.outputs.ref }} - upload: ${{ steps.vars.outputs.upload }} - target-cmake-flags: ${{ steps.vars.outputs.target-cmake-flags }} - build-flang: ${{ steps.vars.outputs.build-flang }} - enable-pgo: ${{ steps.vars.outputs.enable-pgo }} - release-binary-basename: ${{ steps.vars.outputs.release-binary-basename }} - release-binary-filename: ${{ steps.vars.outputs.release-binary-filename }} - - steps: - # It's good practice to use setup-python, but this is also required on macos-14 - # due to https://github.com/actions/runner-images/issues/10385 - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f - with: - python-version: '3.12' - - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Install Dependencies - shell: bash - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Check Permissions - if: github.event_name != 'pull_request' - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - shell: bash - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - - - name: Collect Variables - id: vars - shell: bash - # In order for the test-release.sh script to run correctly, the LLVM - # source needs to be at the following location relative to the build dir: - # | X.Y.Z-rcN | ./rcN/llvm-project - # | X.Y.Z | ./final/llvm-project - # - # We also need to set divergent flags based on the release version: - # | X.Y.Z-rcN | -rc N -test-asserts - # | X.Y.Z | -final - run: | - trimmed=$(echo ${{ inputs.release-version }} | xargs) - if [ -n "$trimmed" ]; then - release_version="$trimmed" - ref="llvmorg-$release_version" - else - release_version="${{ (github.event_name == 'pull_request' && format('PR{0}', github.event.pull_request.number)) || 'CI'}}-${{ github.sha }}" - ref=${{ github.sha }} - fi - if [ -n "${{ inputs.upload }}" ]; then - upload="${{ inputs.upload }}" - else - upload="false" - fi - echo "release-version=$release_version">> $GITHUB_OUTPUT - echo "ref=$ref" >> $GITHUB_OUTPUT - echo "upload=$upload" >> $GITHUB_OUTPUT - - release_binary_basename="LLVM-$release_version-${{ runner.os }}-${{ runner.arch }}" - echo "release-binary-basename=$release_binary_basename" >> $GITHUB_OUTPUT - echo "release-binary-filename=$release_binary_basename.tar.xz" >> $GITHUB_OUTPUT - - # Detect necessary CMake flags - target="${{ runner.os }}-${{ runner.arch }}" - echo "enable-pgo=false" >> $GITHUB_OUTPUT - target_cmake_flags="-DLLVM_RELEASE_ENABLE_PGO=OFF" - # The macOS builds try to cross compile some libraries so we need to - # add extra CMake args to disable them. - # See https://github.com/llvm/llvm-project/issues/99767 - if [ "${{ runner.os }}" = "macOS" ]; then - target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_COMPILER_RT_ENABLE_IOS=OFF" - if [ "${{ runner.arch }}" = "ARM64" ]; then - arches=arm64 - else - arches=x86_64 - fi - target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_DARWIN_osx_ARCHS=$arches -DBOOTSTRAP_DARWIN_osx_BUILTIN_ARCHS=$arches" - fi - - build_flang="true" - - if [ "${{ runner.os }}" = "Windows" ]; then - # The build times out on Windows, so we need to disable LTO. - target_cmake_flags="$target_cmake_flags -DLLVM_RELEASE_ENABLE_LTO=OFF" - fi - - echo "target-cmake-flags=$target_cmake_flags" >> $GITHUB_OUTPUT - echo "build-flang=$build_flang" >> $GITHUB_OUTPUT - - build-stage1: - name: "Build Stage 1" - needs: prepare - if: github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - - name: Checkout Actions - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - # Check out outside of working directory so the source checkout doesn't - # remove it. - path: workflows - - # actions/checkout does not support paths outside of the GITHUB_WORKSPACE. - # Also, anything that we put inside of GITHUB_WORKSPACE will be overwritten - # by future actions/checkout steps. Therefore, in order to checkout the - # latest actions from main, we need to first checkout out the actions inside of - # GITHUB_WORKSPACE (see previous step), then use actions/checkout to checkout - # the code being built and the move the actions from main back into GITHUB_WORKSPACE, - # becasue the uses on composite actions only reads workflows from inside GITHUB_WORKSPACE. - - shell: bash - run: mv workflows ../workflows-main - - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ needs.prepare.outputs.ref }} - - - name: Copy main workflows - shell: bash - run: | - mv ../workflows-main . - - - name: Setup Stage - id: setup-stage - uses: ./workflows-main/.github/workflows/release-binaries-setup-stage - - - name: Setup sccache - uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9 - with: - # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 - max-size: 2G - key: sccache-${{ runner.os }}-${{ runner.arch }}-release - variant: sccache - - - name: Build Stage 1 Clang - id: build - shell: bash - run: | - # There were some issues on the ARM64 MacOS runners with trying to build x86 object, - # so we need to set some extra cmake flags to disable this. - cmake -G Ninja -S llvm -B ${{ steps.setup-stage.outputs.build-prefix }}/build \ - ${{ needs.prepare.outputs.target-cmake-flags }} \ - -C clang/cmake/caches/Release.cmake \ - -DBOOTSTRAP_LLVM_PARALLEL_LINK_JOBS=1 \ - -DBOOTSTRAP_CPACK_PACKAGE_FILE_NAME="${{ needs.prepare.outputs.release-binary-basename }}" \ - -DCMAKE_C_COMPILER_LAUNCHER=sccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=sccache - ninja -v -C ${{ steps.setup-stage.outputs.build-prefix }}/build - # There is a race condition on the MacOS builders and this command is here - # to help debug that when it happens. - ls -ltr ${{ steps.setup-stage.outputs.build-prefix }}/build - - - name: Save Stage - uses: ./workflows-main/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage2: - name: "Build Stage 2" - needs: - - prepare - - build-stage1 - if: github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage1 - - - name: Build Stage 2 - # Re-enable once PGO builds are supported. - if: needs.prepare.outputs.enable-pgo == 'true' - shell: bash - run: | - ninja -C ${{ steps.setup-stage.outputs.build-prefix}}/build stage2-instrumented - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage3-clang: - name: "Build Stage 3 LLVM/Clang" - needs: - - prepare - - build-stage2 - if: github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage2 - - - name: Build LLVM/Clang - shell: bash - run: | - # There is a race condition on the MacOS builders and this command is here - # to help debug that when it happens. - ls -ltr ${{ steps.setup-stage.outputs.build-prefix }}/build - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-clang - # Build some of the larger binaries here too. - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ - clang-scan-deps \ - modularize clangd \ - clangd-indexer \ - clang-check \ - ${{ (runner.os == 'Linux' && 'clangd-fuzzer') || '' }} \ - clang-tidy \ - llc \ - lli \ - llvm-exegesis \ - llvm-opt-fuzzer \ - llvm-reduce \ - llvm-lto \ - dsymutil - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage3-flang: - name: "Build Stage 3 Flang/MLIR/Bolt" - needs: - - prepare - - build-stage3-clang - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage3-clang - - - name: Build Flang / MLIR / Bolt - shell: bash - run: | - # Build some of the mlir tools that take a long time to link - if [ "${{ needs.prepare.outputs.build-flang }}" = "true" ]; then - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang-new bbc - fi - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ - mlir-bytecode-parser-fuzzer \ - mlir-cpu-runner \ - mlir-lsp-server \ - mlir-opt \ - mlir-query \ - mlir-reduce \ - mlir-text-parser-fuzzer \ - mlir-translate \ - mlir-transform-opt \ - mlir-cat \ - mlir-minimal-opt \ - mlir-minimal-opt-canonicalize \ - mlir-pdll-lsp-server \ - llvm-bolt \ - llvm-bolt-heatmap - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - build-stage3-all: - name: "Build Stage 3" - needs: - - prepare - - build-stage3-flang - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage3-flang - - - name: Build Release Package - shell: bash - run: | - which cmake - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-package - # Copy Release artifact to the workspace so it is easier to upload. - # This is necessary, because on Windows, the build-prefix path can - # only be used on bash steps, because it uses the form of /d/files/ - # and other steps expect D:\files. - mv ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/${{ needs.prepare.outputs.release-binary-filename }} . - - - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os }}-${{ runner.arch }}-release-binary - # Due to path differences on Windows when running in bash vs running on node, - # we need to search for files in the current workspace. - path: | - ${{ needs.prepare.outputs.release-binary-filename }} - - # Clean up some build files to reduce size of artifact. - - name: Clean Up Build Directory - shell: bash - run: | - find ${{ steps.setup-stage.outputs.build-prefix }}/build -iname ${{ needs.prepare.outputs.release-binary-filename }} -delete - rm -Rf ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/_CPack_Packages - - - name: Save Stage - uses: ./workflows/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - upload-release-binaries: - name: "Upload Release Binaries" - needs: - - prepare - - build-stage3-all - if: >- - always() && - github.event_name != 'pull_request' && - needs.prepare.outputs.upload == 'true' - runs-on: ubuntu-22.04 - permissions: - contents: write # For release uploads - id-token: write # For artifact attestations - attestations: write # For artifact attestations - - steps: - - name: Checkout Release Scripts - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - sparse-checkout: | - llvm/utils/release/github-upload-release.py - llvm/utils/git/requirements.txt - sparse-checkout-cone-mode: false - - - name: 'Download artifact' - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 - with: - pattern: '*-release-binary' - merge-multiple: true - - - name: Attest Build Provenance - id: provenance - uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0 - with: - subject-path: ${{ needs.prepare.outputs.release-binary-filename }} - - - name: Rename attestation file - run: - mv ${{ steps.provenance.outputs.bundle-path }} ${{ needs.prepare.outputs.release-binary-filename }}.jsonl - - - name: Upload Build Provenance - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3 - with: - name: ${{ needs.prepare.outputs.release-binary-filename }}-attestation - path: ${{ needs.prepare.outputs.release-binary-filename }}.jsonl - - - name: Install Python Requirements - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Upload Release - shell: bash - run: | - ./llvm/utils/release/github-upload-release.py \ - --token ${{ github.token }} \ - --release ${{ needs.prepare.outputs.release-version }} \ - upload \ - --files ${{ needs.prepare.outputs.release-binary-filename }}* - - test-stage3: - name: "Test Stage 3" - needs: - - prepare - - build-stage3-all - if: >- - github.repository == 'llvm/llvm-project' - runs-on: ${{ inputs.runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-stage3-all - - - name: Run Tests - shell: bash - run: | - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-check-all diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml deleted file mode 100644 index 922c5093f135..000000000000 --- a/.github/workflows/release-documentation.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Release Documentation - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - -jobs: - release-documentation: - name: Build and Upload Release Documentation - runs-on: ubuntu-latest - env: - upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }} - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Setup Python env - uses: actions/setup-python@v5 - with: - cache: 'pip' - cache-dependency-path: './llvm/docs/requirements.txt' - - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - graphviz \ - python3-github \ - ninja-build \ - texlive-font-utils - pip3 install --user -r ./llvm/docs/requirements.txt - - - name: Build Documentation - env: - GITHUB_TOKEN: ${{ github.token }} - run: | - ./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-doxygen - - - name: Create Release Notes Artifact - uses: actions/upload-artifact@v3 - with: - name: release-notes - path: docs-build/html-export/ - - - name: Clone www-releases - if: env.upload - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - repository: ${{ github.repository_owner }}/www-releases - ref: main - fetch-depth: 0 - path: www-releases - persist-credentials: false - - - name: Upload Release Notes - if: env.upload - env: - GH_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }} - run: | - mkdir -p www-releases/${{ inputs.release-version }} - mv ./docs-build/html-export/* www-releases/${{ inputs.release-version }} - cd www-releases - git checkout -b ${{ inputs.release-version }} - git add ${{ inputs.release-version }} - git config user.email "llvmbot@llvm.org" - git config user.name "llvmbot" - git commit -a -m "Add ${{ inputs.release-version }} documentation" - git push --force "https://$GH_TOKEN@github.com/llvmbot/www-releases.git" HEAD:refs/heads/${{ inputs.release-version }} - gh pr create -f -B main -H ${{ inputs.release-version }} -R llvmbot/www-releases diff --git a/.github/workflows/release-doxygen.yml b/.github/workflows/release-doxygen.yml deleted file mode 100644 index ea95e5bb12b2..000000000000 --- a/.github/workflows/release-doxygen.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Release Doxygen - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - upload: - description: 'Upload documentation' - required: false - type: boolean - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - -jobs: - release-doxygen: - name: Build and Upload Release Doxygen - runs-on: ubuntu-latest - permissions: - contents: write - env: - upload: ${{ inputs.upload && !contains(inputs.release-version, 'rc') }} - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Setup Python env - uses: actions/setup-python@v5 - with: - cache: 'pip' - cache-dependency-path: './llvm/docs/requirements.txt' - - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - doxygen \ - graphviz \ - python3-github \ - ninja-build \ - texlive-font-utils - pip3 install --user -r ./llvm/docs/requirements.txt - - - name: Build Doxygen - run: | - ./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-sphinx - - - name: Upload Doxygen - if: env.upload - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" --user-token "$USER_TOKEN" upload --files ./*doxygen*.tar.xz diff --git a/.github/workflows/release-lit.yml b/.github/workflows/release-lit.yml deleted file mode 100644 index 9d6f3140e688..000000000000 --- a/.github/workflows/release-lit.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Release Lit - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - - workflow_call: - inputs: - release-version: - description: 'Release Version' - required: true - type: string - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - -jobs: - release-lit: - name: Release Lit - runs-on: ubuntu-latest - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: "llvmorg-${{ inputs.release-version }}" - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y python3-setuptools python3-psutil python3-github - - - name: Check Permissions - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - - - name: Setup Cpp - uses: aminya/setup-cpp@v1 - with: - compiler: llvm-16.0.6 - cmake: true - ninja: true - - - name: Test lit - run: | - mkdir build && cd build - export FILECHECK_OPTS='-dump-input-filter=all -vv -color' - cmake ../llvm -DCMAKE_BUILD_TYPE=Release -G Ninja - ninja -v -j $(nproc) check-lit - - - name: Package lit - run: | - cd llvm/utils/lit - # Remove 'dev' suffix from lit version. - sed -i 's/ + "dev"//g' lit/__init__.py - python3 setup.py sdist bdist_wheel - - - name: Upload lit to test.pypi.org - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.LLVM_LIT_TEST_PYPI_API_TOKEN }} - repository-url: https://test.pypi.org/legacy/ - packages-dir: llvm/utils/lit/dist/ - - - name: Upload lit to pypi.org - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.LLVM_LIT_PYPI_API_TOKEN }} - packages-dir: llvm/utils/lit/dist/ diff --git a/.github/workflows/release-sources.yml b/.github/workflows/release-sources.yml deleted file mode 100644 index a6c86823f99d..000000000000 --- a/.github/workflows/release-sources.yml +++ /dev/null @@ -1,108 +0,0 @@ -name: Release Sources - -permissions: - contents: read - -on: - workflow_dispatch: - inputs: - release-version: - description: Release Version - required: true - type: string - workflow_call: - inputs: - release-version: - description: Release Version - required: true - type: string - secrets: - RELEASE_TASKS_USER_TOKEN: - description: "Secret used to check user permissions." - required: false - # Run on pull_requests for testing purposes. - pull_request: - paths: - - '.github/workflows/release-sources.yml' - types: - - opened - - synchronize - - reopened - # When a PR is closed, we still start this workflow, but then skip - # all the jobs, which makes it effectively a no-op. The reason to - # do this is that it allows us to take advantage of concurrency groups - # to cancel in progress CI jobs whenever the PR is closed. - - closed - -concurrency: - group: ${{ github.workflow }}-${{ inputs.release-version || github.event.pull_request.number }} - cancel-in-progress: True - -jobs: - inputs: - name: Collect Job Inputs - if: >- - github.repository_owner == 'llvm' && - github.event.action != 'closed' - outputs: - ref: ${{ steps.inputs.outputs.ref }} - export-args: ${{ steps.inputs.outputs.export-args }} - runs-on: ubuntu-latest - steps: - - id: inputs - run: | - ref=${{ (inputs.release-version && format('llvmorg-{0}', inputs.release-version)) || github.sha }} - if [ -n "${{ inputs.release-version }}" ]; then - export_args="-release ${{ inputs.release-version }} -final" - else - export_args="-git-ref ${{ github.sha }}" - fi - echo "ref=$ref" >> $GITHUB_OUTPUT - echo "export-args=$export_args" >> $GITHUB_OUTPUT - - release-sources: - name: Package Release Sources - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - needs: - - inputs - permissions: - id-token: write - attestations: write - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ needs.inputs.outputs.ref }} - fetch-tags: true - - name: Install Dependencies - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Check Permissions - if: github.event_name != 'pull_request' - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - - name: Create Tarballs - run: | - ./llvm/utils/release/export.sh ${{ needs.inputs.outputs.export-args }} - - name: Attest Build Provenance - if: github.event_name != 'pull_request' - id: provenance - uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0 - with: - subject-path: "*.xz" - - if: github.event_name != 'pull_request' - run: | - mv ${{ steps.provenance.outputs.bundle-path }} . - - name: Create Tarball Artifacts - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3 - with: - path: | - *.xz - attestation.jsonl - - diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml deleted file mode 100644 index 780dd0ff6325..000000000000 --- a/.github/workflows/release-tasks.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Release Task - -permissions: - contents: read - -on: - push: - tags: - # The regex support here is limited, so just match everything that starts with llvmorg- and filter later. - - 'llvmorg-*' - -jobs: - validate-tag: - name: Validate Tag - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - outputs: - release-version: ${{ steps.validate-tag.outputs.release-version }} - steps: - - name: Validate Tag - id: validate-tag - run: | - echo "${{ github.ref_name }}" | grep -e '^llvmorg-[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc[0-9]\+\)\?$' - release_version=$(echo "${{ github.ref_name }}" | sed 's/llvmorg-//g') - echo "release-version=$release_version" >> "$GITHUB_OUTPUT" - - release-create: - name: Create a New Release - runs-on: ubuntu-latest - permissions: - contents: write # For creating the release. - needs: validate-tag - - steps: - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install python3-github - - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Create Release - env: - GITHUB_TOKEN: ${{ github.token }} - USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --release ${{ needs.validate-tag.outputs.release-version }} --user ${{ github.actor }} --user-token "$USER_TOKEN" create - release-documentation: - name: Build and Upload Release Documentation - needs: - - validate-tag - uses: ./.github/workflows/release-documentation.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - upload: true - - release-doxygen: - name: Build and Upload Release Doxygen - permissions: - contents: write - needs: - - validate-tag - - release-create - uses: ./.github/workflows/release-doxygen.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - upload: true - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - - release-lit: - name: Release Lit - needs: validate-tag - uses: ./.github/workflows/release-lit.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - - release-binaries: - name: Build Release Binaries - permissions: - contents: write - id-token: write - attestations: write - needs: - - validate-tag - - release-create - strategy: - fail-fast: false - matrix: - runs-on: - - ubuntu-22.04 - - windows-2022 - - macos-13 - - macos-14 - - uses: ./.github/workflows/release-binaries.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - upload: true - runs-on: ${{ matrix.runs-on }} - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} - - release-sources: - name: Package Release Sources - permissions: - contents: read - id-token: write - attestations: write - needs: - - validate-tag - uses: ./.github/workflows/release-sources.yml - with: - release-version: ${{ needs.validate-tag.outputs.release-version }} - # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. - secrets: - RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml deleted file mode 100644 index ff61cf83a6af..000000000000 --- a/.github/workflows/scorecard.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. They are provided -# by a third-party and are governed by separate terms of service, privacy -# policy, and support documentation. - -# Check current LLVM-Project results here: https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project - -name: Scorecard supply-chain security -on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '38 20 * * *' - -# Declare default permissions as read only. -permissions: - contents: read - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - if: github.repository == 'llvm/llvm-project' - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - - steps: - - name: "Checkout code" - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 - with: - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 - with: - results_file: results.sarif - results_format: sarif - - # - Publish results to OpenSSF REST API for easy access by consumers - # - Allows the repository to include the Scorecard badge. - # - See https://github.com/ossf/scorecard-action#publishing-results. - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - # Upload the results to GitHub's code scanning dashboard. - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 - with: - sarif_file: results.sarif diff --git a/.github/workflows/set-release-binary-outputs.sh b/.github/workflows/set-release-binary-outputs.sh deleted file mode 100644 index 14d0798364e9..000000000000 --- a/.github/workflows/set-release-binary-outputs.sh +++ /dev/null @@ -1,34 +0,0 @@ -# Usage: set-release-binary-outputs.sh - -set -e - -if [ -z "$GITHUB_OUTPUT" ]; then - export GITHUB_OUTPUT=`mktemp` - echo "Warning: Environment variable GITHUB_OUTPUT is not set." - echo "Writing output variables to $GITHUB_OUTPUT" -fi - -tag=$1 -upload=$2 - -if echo $tag | grep -e '^[0-9a-f]\+$'; then - # This is a plain commit. - # TODO: Don't hardcode this. - release_version="18" - upload='false' - ref="$tag" - -else - - pattern='^llvmorg-[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc[0-9]\+\)\?$' - echo "$tag" | grep -e $pattern - if [ $? != 0 ]; then - echo "ERROR: Tag '$tag' doesn't match pattern: $pattern" - exit 1 - fi - release_version=`echo "$tag" | sed 's/llvmorg-//g'` - release=`echo "$release_version" | sed 's/-.*//g'` -fi -echo "release-version=$release_version" >> $GITHUB_OUTPUT -echo "upload=$upload" >> $GITHUB_OUTPUT -echo "ref=$tag" >> $GITHUB_OUTPUT diff --git a/.github/workflows/spirv-tests.yml b/.github/workflows/spirv-tests.yml deleted file mode 100644 index 75918e73e897..000000000000 --- a/.github/workflows/spirv-tests.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: SPIR-V Tests - -permissions: - contents: read - -on: - workflow_dispatch: - pull_request: - paths: - - 'llvm/lib/Target/SPIRV/**' - - 'llvm/test/CodeGen/SPIRV/**' - - '.github/workflows/spirv-tests.yml' - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - check_spirv: - if: github.repository_owner == 'llvm' - name: Test SPIR-V - uses: ./.github/workflows/llvm-project-tests.yml - with: - build_target: check-llvm-codegen-spirv - projects: - extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="SPIRV" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON' - os_list: '["ubuntu-latest"]' diff --git a/.github/workflows/unprivileged-download-artifact/action.yml b/.github/workflows/unprivileged-download-artifact/action.yml deleted file mode 100644 index 9d8fb59a67c0..000000000000 --- a/.github/workflows/unprivileged-download-artifact/action.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Unprivileged Download Artifact -description: >- - Download artifacts from another workflow run without using an access token. -inputs: - run-id: - description: >- - The run-id for the workflow run that you want to download the artifact - from. If ommitted it will download the most recently created artifact - from the repo with the artifact-name. - required: false - artifact-name: - desciption: The name of the artifact to download. - required: true - - -outputs: - filename: - description: >- - The filename of the downloaded artifact or the empty string if the - artifact was not found. - value: ${{ steps.download-artifact.outputs.filename }} - artifact-id: - description: "The id of the artifact being downloaded." - value: ${{ steps.artifact-url.outputs.id }} - - -runs: - using: "composite" - steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1 - id: artifact-url - with: - script: | - var response; - if (!"${{ inputs.run-id }}") { - response = await github.rest.actions.listArtifactsForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - name: "${{ inputs.artifact-name }}" - }) - } else { - response = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: "${{ inputs.run-id }}", - name: "${{ inputs.artifact-name }}" - }) - } - - console.log(response) - - for (artifact of response.data.artifacts) { - console.log(artifact); - } - - if (response.data.artifacts.length == 0) { - console.log("Could not find artifact ${{ inputs.artifact-name }} for workflow run ${{ inputs.run-id }}") - return; - } - - const url_response = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: response.data.artifacts[0].id, - archive_format: "zip" - }) - - core.setOutput("url", url_response.url); - core.setOutput("id", response.data.artifacts[0].id); - - - shell: bash - if: steps.artifact-url.outputs.url != '' - id: download-artifact - run: | - curl -L -o ${{ inputs.artifact-name }}.zip "${{ steps.artifact-url.outputs.url }}" - echo "filename=${{ inputs.artifact-name }}.zip" >> $GITHUB_OUTPUT - - - shell: bash - if: steps.download-artifact.outputs.filename != '' - run: | - unzip ${{ steps.download-artifact.outputs.filename }} diff --git a/.github/workflows/version-check.py b/.github/workflows/version-check.py deleted file mode 100755 index f75fd5030088..000000000000 --- a/.github/workflows/version-check.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python3 - -from git import Repo -import re -import sys - - -def get_version_from_tag(tag): - m = re.match("llvmorg-([0-9]+)\.([0-9]+)\.([0-9]+)(-rc[0-9]+)?$", tag) - if m: - if m.lastindex == 4: - # We have an rc tag. - return m.group(1, 2, 3) - # We have a final release tag. - return (m.group(1), m.group(2), str(int(m.group(3)) + 1)) - - m = re.match("llvmorg-([0-9]+)-init", tag) - if m: - return (m.group(1), "1", "0") - - raise Exception(f"error: Tag is not valid: {tag}") - - -version = sys.argv[1] - -repo = Repo() - -tag = repo.git.describe(tags=True, abbrev=0) -expected_version = ".".join(get_version_from_tag(tag)) - -if version != expected_version: - print("error: Expected version", expected_version, "but found version", version) - sys.exit(1) - -print("Versions match:", version, expected_version) -sys.exit(0) diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml deleted file mode 100644 index 894e07d323ca..000000000000 --- a/.github/workflows/version-check.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: LLVM Project Version Check - -on: - push: - branches: - - 'release/**' - pull_request: - branches: - - 'release/**' - -permissions: - contents: read - -jobs: - version_check: - if: github.repository_owner == 'llvm' - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install dependencies - run: | - pip install --require-hashes -r ./llvm/utils/git/requirements.txt - - - name: Version Check - run: | - version=$(grep -o 'LLVM_VERSION_\(MAJOR\|MINOR\|PATCH\) [0-9]\+' cmake/Modules/LLVMVersion.cmake | cut -d ' ' -f 2 | tr "\n" "." | sed 's/.$//g') - .github/workflows/version-check.py "$version" diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index f6a5e6c01629..000000000000 --- a/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reporting LLVM Security Issues - -To report security issues in LLVM, please follow the steps outlined on the -[LLVM Security Group](https://llvm.org/docs/Security.html#how-to-report-a-security-issue) -page. From b5d766b119a447adec051cba9d21ebbf5e068ac2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 28 Mar 2024 13:12:51 +0200 Subject: [PATCH 053/203] Update md files for EraVM, add LICENSE --- CONTRIBUTING.md | 20 ++-- LICENSE | 278 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 111 +++++++++++++------ 3 files changed, 365 insertions(+), 44 deletions(-) create mode 100644 LICENSE diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6424f9b26a9d..f129e606f7a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,17 +1,9 @@ -# Contributing to LLVM +# Contribution Guidelines -Thank you for your interest in contributing to LLVM! There are many ways to -contribute, and we appreciate all contributions. +Thank you for considering helping out with the source code! We are extremely grateful for any consideration of +contributions to this repository. However, at this time, we generally do not accept external contributions. This policy +will change in the future, so please check back regularly for updates. -To get started with contributing, please take a look at the -[Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. It -describes how to get involved, raise issues and submit patches. +For security issues, please contact us at [security@matterlabs.dev](mailto:security@matterlabs.dev). -## Getting in touch - -Join the [LLVM Discourse forums](https://discourse.llvm.org/), [Discord -chat](https://discord.gg/xS7Z362), or #llvm IRC channel on -[OFTC](https://oftc.net/). - -The LLVM project has adopted a [code of conduct](https://llvm.org/docs/CodeOfConduct.html) for -participants to all modes of communication within the project. +Thank you for your support in accelerating the mass adoption of crypto for personal sovereignty! diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..e7ac8892af2b --- /dev/null +++ b/LICENSE @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Matter Labs + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/README.md b/README.md index a9b29ecbc1a3..f95409291f05 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,95 @@ -# The LLVM Compiler Infrastructure +# zkSync Era: The zkEVM LLVM Framework -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project) -[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8273/badge)](https://www.bestpractices.dev/projects/8273) -[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml?query=event%3Aschedule) +[![Logo](eraLogo.svg)](https://zksync.io/) -Welcome to the LLVM project! +zkSync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security +or decentralization. As it's EVM-compatible (with Solidity/Vyper), 99% of Ethereum projects can redeploy without +needing to refactor or re-audit any code. zkSync Era also uses an LLVM-based compiler that will eventually enable +developers to write smart contracts in popular languages such as C++ and Rust. -This repository contains the source code for LLVM, a toolkit for the -construction of highly optimized compilers, optimizers, and run-time -environments. +This directory and its sub-directories contain the source code for the zkEVM fork of the [LLVM](https://llvm.org) framework, +a toolkit for the construction of highly optimized compilers, optimizers, and run-time environments +used by the Solidity and Vyper compilers developed by Matter Labs. -The LLVM project has multiple components. The core of the project is -itself called "LLVM". This contains all of the tools, libraries, and header +## Overview + +Welcome to the zkEVM LLVM project! + +The project has multiple components. The core of the project is +the `llvm` directory. This contains all of the tools, libraries, and header files needed to process intermediate representations and convert them into object files. Tools include an assembler, disassembler, bitcode analyzer, and -bitcode optimizer. +bitcode optimizer. These tools are not yet officially supported for third-party front-ends. +It also contains zkEVM modifications of the standard [LLVM regression tests](https://llvm.org/docs/TestingGuide.html#regression-tests). + +The zkEVM back-end is called `EraVM`, and the architecture is called `eravm`. + +## Building + +The zkEVM LLVM framework must be built with our tool called `zkevm-llvm`: + +1. Install some tools system-wide: + 1.a. `apt install cmake ninja-build clang-13 lld-13` on a Debian-based Linux, with optional `musl-tools` if you need a `musl` build + 1.b. `pacman -S cmake ninja clang lld` on an Arch-based Linux + 1.c. On MacOS, install the [HomeBrew](https://brew.sh) package manager (being careful to install it as the appropriate user), then `brew install cmake ninja coreutils`. Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apple’s Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. + 1.d. Their equivalents with other package managers + +2. [Install Rust](https://www.rust-lang.org/tools/install) + + Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. + Also install the `musl` target if you are compiling on Linux in order to distribute the binaries: + `rustup target add x86_64-unknown-linux-musl` + +3. Install the zkEVM LLVM framework builder: + + 3.a. `cargo install compiler-llvm-builder` on MacOS, or Linux for personal use + 3.b. `cargo install compiler-llvm-builder --target x86_64-unknown-linux-musl` on Linux for distribution + + The builder is not the zkEVM LLVM framework itself, but a tool that clones its repository and runs the sequence of build commands. + By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. + +4. In a directory in which you want the `llvm` directory, create an `LLVM.lock` file with the URL and branch or tag you want to build. For example: + + ``` + url = "" + branch = "" + ``` + +5. Run the builder to clone and build the zkevm LLVM framework: + 5.1. `zkevm-llvm clone` + 5.2. `zkevm-llvm build` + + The build artifacts will end up in the `./target-llvm/target-final/` directory. + You may point your `LLVM_SYS_150_PREFIX` to that directory to use this build as a compiler dependency. + If built with the `--enable-tests` option, test tools will be in the `./target-llvm/build-final/` directory, along with copies of the build artifacts. + +## Troubleshooting + +- If you get a “failed to authenticate when downloading repository… if the git CLI succeeds then net.git-fetch-with-cli may help here” error, +then prepending the `cargo` command with `CARGO_NET_GIT_FETCH_WITH_CLI=true` may help. +- Unset any LLVM-related environment variables you may have set. -C-like languages use the [Clang](https://clang.llvm.org/) frontend. This -component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode --- and from there into object files, using LLVM. +## License -Other components include: -the [libc++ C++ standard library](https://libcxx.llvm.org), -the [LLD linker](https://lld.llvm.org), and more. +The zkEVM fork of the LLVM framework is distributed under the terms of +Apache License, Version 2.0 with LLVM Exceptions, ([LICENSE](LICENSE) or ) -## Getting the Source Code and Building LLVM +## Resources -Consult the -[Getting Started with LLVM](https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm) -page for information on building and running LLVM. +[Official LLVM documentation](https://llvm.org/docs/GettingStarted.html) -For information on how to contribute to the LLVM project, please take a look at -the [Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. +## Official Links -## Getting in touch +- [Website](https://zksync.io/) +- [GitHub](https://github.com/matter-labs) +- [Twitter](https://twitter.com/zksync) +- [Twitter for Devs](https://twitter.com/zkSyncDevs) +- [Discord](https://join.zksync.dev/) -Join the [LLVM Discourse forums](https://discourse.llvm.org/), [Discord -chat](https://discord.gg/xS7Z362), -[LLVM Office Hours](https://llvm.org/docs/GettingInvolved.html#office-hours) or -[Regular sync-ups](https://llvm.org/docs/GettingInvolved.html#online-sync-ups). +## Disclaimer -The LLVM project has adopted a [code of conduct](https://llvm.org/docs/CodeOfConduct.html) for -participants to all modes of communication within the project. +zkSync Era has been through extensive testing and audits, and although it is live, it is still in alpha state and +will undergo further audits and bug bounty programs. We would love to hear our community's thoughts and suggestions +about it! +It's important to note that forking it now could potentially lead to missing important +security updates, critical features, and performance improvements. From c7c5a982e1764e9c82df98491e59c923c4df008d Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 28 Aug 2024 11:37:58 +0200 Subject: [PATCH 054/203] Create SECURITY.md --- SECURITY.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..d77060818a22 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,74 @@ +# Security Policy + +We truly appreciate efforts to discover and disclose security issues responsibly! + +## Vulnerabilities + +If you'd like to report a security issue in the repositories of matter-labs organization, please proceed to our +[Bug Bounty Program on Immunefi](https://docs.zksync.io/build/resources/audit-bug-bounty#bug-bounty-program). + +## Other Security Issues + +We take an impact-first approach instead of a rules-first approach. Therefore, if you believe you found the impactful +issue but can't report it via the Bug Bounty, please email us at +[security@matterlabs.dev](mailto:security@matterlabs.dev). + +### PGP Key + +The following PGP key may be used to communicate sensitive information to developers: + +Fingerprint: `5FED B2D0 EA2C 4906 DD66 71D7 A2C5 0B40 CE3C F297` + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGEBmQkBEAD6tlkBEZFMvR8kOgxXX857nC2+oTik6TopJz4uCskuqDaeldMy +l+26BBzLkIeO1loS+bzVgnNFJRrGt9gv98MzNEHJVv6D7GsSLlUX/pz7Lxn0J4ry +o5XIk3MQTCUBdaXGs6GBLl5Xe8o+zNj4MKd4zjgDLinITNlE/YZCDsXyvYS3YFTQ +cwaUTNlawkKgw4BLaEqwB2JuyEhI9wx5X7ibjFL32sWMolYsNAlzFQzM09HCurTn +q0DYau9kPJARcEk9/DK2iq0z3gMCQ8iRTDaOWd8IbSP3HxcEoM5j5ZVAlULmjmUE +StDaMPLj0Kh01Tesh/j+vjchPXHT0n4zqi1+KOesAOk7SIwLadHfQMTpkU7G2fR1 +BrA5MtlzY+4Rm6o7qu3dpZ+Nc4iM3FUnaQRpvn4g5nTh8vjG94OCzX8DXWrCKyxx +amCs9PLDYOpx84fXYv4frkWpKh2digDSUGKhoHaOSnqyyvu3BNWXBCQZJ20rqEIu +sXOQMxWIoWCOOPRRvrHrKDA2hpoKjs3pGsProfpVRzb9702jhWpTfbDp9WjQlFtX +2ZIDxlwAxcugClgrp5JiUxvhg2A9lDNwCF7r1e68uNv5usBZQVKPJmnvS2nWgKy8 +x9oJsnwrEjxwiRHd34UvfMkwY9RENSJ+NoXqBdS7Lwz4m6vgbzq6K56WPQARAQAB +tCRaa1N5bmMgU2VjdXJpdHkgPHNlY3VyaXR5QHprc3luYy5pbz6JAk4EEwEKADgW +IQRf7bLQ6ixJBt1mcdeixQtAzjzylwUCYQGZCQIbAwULCQgHAgYVCgkICwIEFgID +AQIeAQIXgAAKCRCixQtAzjzyl5y8EAC/T3oq88Dak2b+5TlWdU2Gpm6924eAqlMt +y1KksDezzNQUlPiCUVllpin2PIjU/S+yzMWKXJA04LoVkEPfPOWjAaavLOjRumxu +MR6P2dVUg1InqzYVsJuRhKSpeexzNA5qO2BPM7/I2Iea1IoJPjogGbfXCo0r5kne +KU7a5GEa9eDHxpHTsbphQe2vpQ1239mUJrFpzAvILn6jV1tawMn5pNCXbsa8l6l2 +gtlyQPdOQECy77ZJxrgzaUBcs/RPzUGhwA/qNuvpF0whaCvZuUFMVuCTEu5LZka2 +I9Rixy+3jqBeONBgb+Fiz5phbiMX33M9JQwGONFaxdvpFTerLwPK2N1T8zcufa01 +ypzkWGheScFZemBxUwXwK4x579wjsnfrY11w0p1jtDgPTnLlXUA2mom4+7MyXPg0 +F75qh6vU1pdXaCVkruFgPVtIw+ccw2AxD50iZQ943ZERom9k165dR9+QxOVMXQ4P +VUxsFZWvK70/s8TLjsGljvSdSOa85iEUqSqh0AlCwIAxLMiDwh5s/ZgiHoIM6Xih +oCpuZyK9p0dn+DF/XkgAZ/S91PesMye3cGm6M5r0tS26aoc2Pk6X37Hha1pRALwo +MOHyaGjc/jjcXXxv6o55ALrOrzS0LQmLZ+EHuteCT15kmeY3kqYJ3og62KgiDvew +dKHENvg7d7kCDQRhAZleARAA6uD6WfdqGeKV5i170+kLsxR3QGav0qGNAbxpSJyn +iHQ8u7mQk3S+ziwN2AAopfBk1je+vCWtEGC3+DWRRfJSjLbtaBG8e6kLP3/cGA75 +qURz6glTG4nl5fcEAa6B1st0OxjVWiSLX3g/yjz8lznQb9awuRjdeHMnyx5DsJUN +d+Iu5KxGupQvKGOMKivSvC8VWk9taaQRpRF+++6stLCDk3ZtlxiopMs3X2jAp6xG +sOBbix1cv9BTsfaiL7XDL/gviqBPXYY5L42x6+jnPo5lROfnlLYkWrv6KZr7HD4k +tRXeaSwxLD2EkUyb16Jpp0be/ofvBtITGUDDLCGBiaXtx/v8d52MARjsyLJSYloj +1yiW01LfAiWHUC4z5jl2T7E7sicrlLH1M8Z6WbuqjdeaYwtfyPA2YCKr/3fn6pIo +D+pYaBSESmhA92P+XVaf5y2BZ6Qf8LveDpWwsVGdBGh9T0raA1ooe1GESLjmIjUa +z5AeQ/uXL5Md9I6bpMUUJYQiH19RPcFlJriI3phXyyf6Wlkk8oVEeCWyzcmw+x1V +deRTvE2x4WIwKGLXRNjin2j1AP7vU2HaNwlPrLijqdyi68+0irRQONoH7Qonr4ca +xWgL+pAaa3dWxf0xqK7uZFp4aTVWlr2uXtV/eaUtLmGMCU0jnjb109wg5L0F7WRT +PfEAEQEAAYkCNgQYAQoAIBYhBF/tstDqLEkG3WZx16LFC0DOPPKXBQJhAZleAhsM +AAoJEKLFC0DOPPKXAAEP/jK7ch9GkoaYlsuqY/aHtxEwVddUDOxjyn3FMDoln85L +/n8AmLQb2bcpKSqpaJwMbmfEyr5MDm8xnsBTfx3u6kgaLOWfKxjLQ6PM7kgIMdi4 +bfaRRuSEI1/R6c/hNpiGnzAeeexldH1we+eH1IVmh4crdat49S2xh7Qlv9ahvgsP +LfKl3rJ+aaX/Ok0AHzhvSfhFpPr1gAaGeaRt+rhlZsx2QyG4Ez8p2nDAcAzPiB3T +73ENoBIX6mTPfPm1UgrRyFKBqtUzAodz66j3r6ebBlWzIRg8iZenVMAxzjINAsxN +w1Bzfgsi5ZespfsSlmEaa7jJkqqDuEcLa2YuiFAue7Euqwz1aGeq1GfTicQioSCb +Ur/LGyz2Mj3ykbaP8p5mFVcUN51yQy6OcpvR/W1DfRT9SHFT/bCf9ixsjB2HlZGo +uxPJowwqmMgHd755ZzPDUM9YDgLI1yXdcYshObv3Wq537JAxnZJCGRK4Y8SwrMSh +8WRxlaM0AGWXiJFIDD4bQPIdnF3X8w0cGWE5Otkb8mMHOT+rFTVlDODwm1zF6oIG +PTwfVrpiZBwiUtfJol1exr/MzSPyGoJnYs3cRf2E3O+D1LbcR8w0LbjGuUy38Piz +ZO/vCeyJ3JZC5kE8nD+XBA4idwzh0BKEfH9t+WchQ3Up9rxyzLyQamoqt5Xby4pY +=xkM3 +-----END PGP PUBLIC KEY BLOCK----- +``` From 630a8fb12ceeddf034d2d5793ad32aa31848cb17 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Mon, 27 Nov 2023 17:47:26 +0200 Subject: [PATCH 055/203] Add clang-tidy checkers --- llvm/.clang-tidy | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/llvm/.clang-tidy b/llvm/.clang-tidy index 47bc73c13f19..f29cbbd6b357 100644 --- a/llvm/.clang-tidy +++ b/llvm/.clang-tidy @@ -1 +1,64 @@ InheritParentConfig: true +# EVM local begin +Checks: > + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-implicit-widening-of-multiplication-result, + -bugprone-narrowing-conversions, + -bugprone-unchecked-optional-access, + cert-*, + clang-analyzer-*, + -clang-analyzer-optin.cplusplus.UninitializedObject, + concurrency-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-narrowing-conversions, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-pro-type-static-cast-downcast, + -cppcoreguidelines-pro-type-union-access, + hicpp-*, + -hicpp-braces-around-statements, + -hicpp-named-parameter, + -hicpp-signed-bitwise, + -hicpp-special-member-functions, + llvm-*, + misc-redundant-expression, + misc-static-assert, + misc-unused-using-decls, + -misc-const-correctness, + modernize-loop-convert, + modernize-make-unique, + modernize-raw-string-literal, + modernize-use-bool-literals, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-equals-default, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-*, + portability-*, + readability-*, + -readability-braces-around-statements, + -readability-convert-member-functions-to-static, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-identifier-naming, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-named-parameter, + -readability-simplify-boolean-expr, + -readability-uppercase-literal-suffix, +CheckOptions: + - key: concurrency-mt-unsafe.FunctionSet + value: posix +WarningsAsErrors: '*' +# EVM local end From 11baa4354cae3a30409c43da964030d88f517e5e Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Fri, 11 Nov 2022 11:34:41 +0200 Subject: [PATCH 056/203] [EVM] Adding EVM backend skeleton. It includes: - Description of EVM instructions - Instruction selection (ISelLowering, DAGtoDAG) - Standard library implementation - Initial implementation of MC Layer - Initial implementation of AsmPrinter that is able to output virtual registes - CI config: compiler-integration-tests.yml. Signed-off-by: Pavel Kopyl --- llvm/CMakeLists.txt | 7 + llvm/docs/CompilerWriterInfo.rst | 7 + llvm/include/llvm/CodeGen/TargetInstrInfo.h | 13 +- llvm/include/llvm/CodeGen/TargetLowering.h | 1 - llvm/include/llvm/CodeGen/ValueTypes.td | 432 +++++----- llvm/include/llvm/IR/CMakeLists.txt | 3 + llvm/include/llvm/IR/IRBuilder.h | 5 + llvm/include/llvm/IR/Intrinsics.td | 11 +- llvm/include/llvm/IR/IntrinsicsEVM.td | 249 ++++++ llvm/include/llvm/IR/Type.h | 7 + llvm/include/llvm/MC/MCAsmInfo.h | 8 + llvm/include/llvm/TargetParser/Triple.h | 9 + llvm/lib/Analysis/MemoryLocation.cpp | 27 + llvm/lib/CodeGen/MachineBasicBlock.cpp | 4 +- llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp | 8 + .../lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 7 +- .../SelectionDAG/ScheduleDAGRRList.cpp | 5 +- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 12 +- .../SelectionDAGAddressAnalysis.cpp | 18 + .../SelectionDAG/SelectionDAGBuilder.cpp | 9 + .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 9 +- .../CodeGen/SelectionDAG/TargetLowering.cpp | 8 + llvm/lib/CodeGen/TargetPassConfig.cpp | 8 +- llvm/lib/IR/Core.cpp | 16 + llvm/lib/IR/Function.cpp | 6 + llvm/lib/IR/LLVMContextImpl.cpp | 5 +- llvm/lib/IR/LLVMContextImpl.h | 5 +- llvm/lib/IR/Type.cpp | 13 + llvm/lib/MC/MCAsmStreamer.cpp | 11 + llvm/lib/MC/MCExpr.cpp | 2 + llvm/lib/Target/EVM/CMakeLists.txt | 80 ++ llvm/lib/Target/EVM/EVM.h | 60 ++ llvm/lib/Target/EVM/EVM.td | 54 ++ llvm/lib/Target/EVM/EVMArgumentMove.cpp | 87 ++ llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 81 ++ llvm/lib/Target/EVM/EVMCodegenPrepare.cpp | 127 +++ llvm/lib/Target/EVM/EVMFrameLowering.cpp | 23 + llvm/lib/Target/EVM/EVMFrameLowering.h | 35 + llvm/lib/Target/EVM/EVMISD.def | 23 + llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp | 102 +++ llvm/lib/Target/EVM/EVMISelLowering.cpp | 610 +++++++++++++++ llvm/lib/Target/EVM/EVMISelLowering.h | 153 ++++ llvm/lib/Target/EVM/EVMInstrFormats.td | 61 ++ llvm/lib/Target/EVM/EVMInstrInfo.cpp | 204 +++++ llvm/lib/Target/EVM/EVMInstrInfo.h | 55 ++ llvm/lib/Target/EVM/EVMInstrInfo.td | 740 ++++++++++++++++++ llvm/lib/Target/EVM/EVMLinkRuntime.cpp | 113 +++ llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp | 89 +++ llvm/lib/Target/EVM/EVMMCInstLower.cpp | 147 ++++ llvm/lib/Target/EVM/EVMMCInstLower.h | 59 ++ llvm/lib/Target/EVM/EVMRegisterInfo.cpp | 86 ++ llvm/lib/Target/EVM/EVMRegisterInfo.h | 44 ++ llvm/lib/Target/EVM/EVMRegisterInfo.td | 39 + llvm/lib/Target/EVM/EVMSubtarget.cpp | 27 + llvm/lib/Target/EVM/EVMSubtarget.h | 62 ++ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 148 ++++ llvm/lib/Target/EVM/EVMTargetMachine.h | 56 ++ .../lib/Target/EVM/EVMTargetTransformInfo.cpp | 60 ++ llvm/lib/Target/EVM/EVMTargetTransformInfo.h | 123 +++ .../Target/EVM/MCTargetDesc/CMakeLists.txt | 18 + .../Target/EVM/MCTargetDesc/EVMAsmBackend.cpp | 80 ++ .../EVM/MCTargetDesc/EVMELFObjectWriter.cpp | 43 + .../Target/EVM/MCTargetDesc/EVMFixupKinds.h | 31 + .../EVM/MCTargetDesc/EVMInstPrinter.cpp | 88 +++ .../Target/EVM/MCTargetDesc/EVMInstPrinter.h | 40 + .../Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp | 31 + .../Target/EVM/MCTargetDesc/EVMMCAsmInfo.h | 29 + .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 49 ++ .../lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp | 27 + llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h | 49 ++ .../EVM/MCTargetDesc/EVMMCTargetDesc.cpp | 115 +++ .../Target/EVM/MCTargetDesc/EVMMCTargetDesc.h | 51 ++ .../EVM/MCTargetDesc/EVMTargetStreamer.cpp | 31 + .../EVM/MCTargetDesc/EVMTargetStreamer.h | 41 + llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt | 10 + .../Target/EVM/TargetInfo/EVMTargetInfo.cpp | 26 + .../lib/Target/EVM/TargetInfo/EVMTargetInfo.h | 24 + llvm/lib/Target/EVM/evm-stdlib.ll | 84 ++ llvm/lib/TargetParser/Triple.cpp | 29 + llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 2 +- .../Transforms/Scalar/LoopIdiomRecognize.cpp | 10 + llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 13 +- .../lib/Transforms/Utils/SimplifyLibCalls.cpp | 27 + .../Analysis/GlobalsModRef/memset-escape.ll | 1 + llvm/test/CodeGen/EVM/add.ll | 34 + llvm/test/CodeGen/EVM/aext.ll | 14 + llvm/test/CodeGen/EVM/br.ll | 50 ++ llvm/test/CodeGen/EVM/call.ll | 51 ++ llvm/test/CodeGen/EVM/div.ll | 32 + llvm/test/CodeGen/EVM/frameidx.ll | 60 ++ llvm/test/CodeGen/EVM/globals.ll | 89 +++ llvm/test/CodeGen/EVM/icmp.ll | 130 +++ llvm/test/CodeGen/EVM/intrinsic.ll | 581 ++++++++++++++ llvm/test/CodeGen/EVM/lit.local.cfg | 2 + .../CodeGen/EVM/load-narrowing-disable.ll | 15 + llvm/test/CodeGen/EVM/logical.ll | 43 + llvm/test/CodeGen/EVM/mem_call_data.ll | 13 + llvm/test/CodeGen/EVM/memintrinsics-opt.ll | 13 + llvm/test/CodeGen/EVM/memintrinsics.ll | 126 +++ llvm/test/CodeGen/EVM/memory.ll | 35 + llvm/test/CodeGen/EVM/mod.ll | 32 + llvm/test/CodeGen/EVM/mul.ll | 26 + llvm/test/CodeGen/EVM/select.ll | 22 + llvm/test/CodeGen/EVM/sext.ll | 78 ++ llvm/test/CodeGen/EVM/shift.ll | 34 + llvm/test/CodeGen/EVM/signextload.ll | 59 ++ llvm/test/CodeGen/EVM/stdlib-call-ellision.ll | 114 +++ llvm/test/CodeGen/EVM/storage.ll | 23 + llvm/test/CodeGen/EVM/sub.ll | 24 + llvm/test/CodeGen/EVM/truncstore.ll | 34 + llvm/test/CodeGen/EVM/tstorage.ll | 23 + llvm/test/CodeGen/EVM/zero_any_extload.ll | 109 +++ llvm/test/CodeGen/EVM/zext.ll | 49 ++ .../2002-04-16-StackFrameSizeAlignment.ll | 1 + .../CodeGen/Generic/2003-05-27-phifcmpd.ll | 3 + .../Generic/2003-05-27-useboolinotherbb.ll | 2 + .../Generic/2003-05-27-usefsubasbool.ll | 2 + .../CodeGen/Generic/2003-05-28-ManyArgs.ll | 1 + .../CodeGen/Generic/2003-05-30-BadFoldGEP.ll | 1 + .../Generic/2003-05-30-BadPreselectPhi.ll | 3 + .../CodeGen/Generic/2003-07-06-BadIntCmp.ll | 1 + .../Generic/2003-07-07-BadLongConst.ll | 1 + .../Generic/2003-07-08-BadCastToBool.ll | 1 + .../Generic/2003-07-29-BadConstSbyte.ll | 1 + .../2004-05-09-LiveVarPartialRegister.ll | 2 + .../CodeGen/Generic/2005-04-09-GlobalInPHI.ll | 2 + .../Generic/2005-10-18-ZeroSizeStackObject.ll | 1 + llvm/test/CodeGen/Generic/2005-12-01-Crash.ll | 2 + .../Generic/2006-02-12-InsertLibcall.ll | 2 + .../Generic/2006-03-01-dagcombineinfloop.ll | 2 + .../2006-06-13-ComputeMaskedBitsCrash.ll | 3 +- .../Generic/2006-06-28-SimplifySetCCCrash.ll | 2 + .../CodeGen/Generic/2006-07-03-schedulers.ll | 2 + .../2007-04-08-MultipleFrameIndices.ll | 3 + .../Generic/2007-04-27-InlineAsm-X-Dest.ll | 3 + .../Generic/2007-04-27-LargeMemObject.ll | 3 + .../CodeGen/Generic/2007-12-17-InvokeAsm.ll | 2 + .../Generic/2007-12-31-UnusedSelector.ll | 1 + .../Generic/2008-01-25-dag-combine-mul.ll | 1 + .../CodeGen/Generic/2008-01-30-LoadCrash.ll | 1 + llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll | 3 + .../CodeGen/Generic/2008-02-20-MatchingMem.ll | 2 + .../CodeGen/Generic/2008-02-25-NegateZero.ll | 2 + .../Generic/2008-08-07-PtrToInt-SmallerInt.ll | 2 + .../2009-03-29-SoftFloatVectorExtract.ll | 1 + .../Generic/2009-11-16-BadKillsCrash.ll | 2 + .../CodeGen/Generic/2010-11-04-BigByval.ll | 2 + .../test/CodeGen/Generic/2010-ZeroSizedArg.ll | 2 +- .../Generic/2013-03-20-APFloatCrash.ll | 3 + .../Generic/2014-02-05-OpaqueConstants.ll | 1 + llvm/test/CodeGen/Generic/APIntLoadStore.ll | 3 + llvm/test/CodeGen/Generic/APIntParam.ll | 3 + llvm/test/CodeGen/Generic/APIntSextParam.ll | 3 + llvm/test/CodeGen/Generic/APIntZextParam.ll | 3 + .../CodeGen/Generic/ConstantExprLowering.ll | 1 + llvm/test/CodeGen/Generic/ForceStackAlign.ll | 3 + .../MIRDebugify/check-line-and-variables.ll | 1 + llvm/test/CodeGen/Generic/PBQP.ll | 1 + .../CodeGen/Generic/add-with-overflow-128.ll | 3 + .../CodeGen/Generic/add-with-overflow-24.ll | 1 + .../test/CodeGen/Generic/add-with-overflow.ll | 1 + llvm/test/CodeGen/Generic/allow-check.ll | 1 + .../CodeGen/Generic/asm-large-immediate.ll | 3 + llvm/test/CodeGen/Generic/badFoldGEP.ll | 1 + llvm/test/CodeGen/Generic/badarg6.ll | 1 + llvm/test/CodeGen/Generic/bool-to-double.ll | 2 + llvm/test/CodeGen/Generic/builtin-expect.ll | 1 + llvm/test/CodeGen/Generic/cast-fp.ll | 2 + llvm/test/CodeGen/Generic/constindices.ll | 2 + llvm/test/CodeGen/Generic/crash.ll | 2 + .../test/CodeGen/Generic/dag-combine-crash.ll | 1 + .../Generic/dag-combine-ossfuzz-crash.ll | 1 + llvm/test/CodeGen/Generic/dwarf-md5.ll | 2 + llvm/test/CodeGen/Generic/dwarf-source.ll | 2 + llvm/test/CodeGen/Generic/empty-load-store.ll | 2 +- .../CodeGen/Generic/exception-handling.ll | 2 + llvm/test/CodeGen/Generic/fastcall.ll | 1 + .../test/CodeGen/Generic/fp-to-int-invalid.ll | 2 + llvm/test/CodeGen/Generic/fp_to_int.ll | 2 + llvm/test/CodeGen/Generic/fpoperations.ll | 2 + llvm/test/CodeGen/Generic/fpowi-promote.ll | 2 + llvm/test/CodeGen/Generic/fwdtwice.ll | 1 + llvm/test/CodeGen/Generic/global-ret0.ll | 1 + llvm/test/CodeGen/Generic/hello.ll | 1 + .../CodeGen/Generic/inline-asm-mem-clobber.ll | 3 + .../Generic/inline-asm-special-strings.ll | 2 + llvm/test/CodeGen/Generic/intrinsics.ll | 7 + llvm/test/CodeGen/Generic/invalid-memcpy.ll | 2 + llvm/test/CodeGen/Generic/isunord.ll | 2 + llvm/test/CodeGen/Generic/live-debug-label.ll | 3 + llvm/test/CodeGen/Generic/llc-start-stop.ll | 2 + .../CodeGen/Generic/llvm-ct-intrinsics.ll | 3 + llvm/test/CodeGen/Generic/negintconst.ll | 1 + llvm/test/CodeGen/Generic/pr12507.ll | 2 +- llvm/test/CodeGen/Generic/pr2625.ll | 2 + llvm/test/CodeGen/Generic/pr33094.ll | 1 + llvm/test/CodeGen/Generic/print-add.ll | 1 + llvm/test/CodeGen/Generic/print-arith-fp.ll | 2 + llvm/test/CodeGen/Generic/print-arith-int.ll | 2 + llvm/test/CodeGen/Generic/print-int.ll | 1 + llvm/test/CodeGen/Generic/print-mul-exp.ll | 1 + llvm/test/CodeGen/Generic/print-mul.ll | 3 + llvm/test/CodeGen/Generic/print-shift.ll | 4 + llvm/test/CodeGen/Generic/ptr-annotate.ll | 1 + llvm/test/CodeGen/Generic/select-cc.ll | 2 + llvm/test/CodeGen/Generic/select.ll | 43 +- .../Generic/selection-dag-determinism.ll | 1 + .../test/CodeGen/Generic/stacksave-restore.ll | 2 + llvm/test/CodeGen/Generic/storetrunc-fp.ll | 2 + llvm/test/CodeGen/Generic/trap.ll | 4 + llvm/test/CodeGen/Generic/undef-phi.ll | 3 + llvm/test/CodeGen/Generic/v-split.ll | 2 + llvm/test/CodeGen/Generic/vector-casts.ll | 2 + .../CodeGen/Generic/vector-constantexpr.ll | 2 + .../Generic/vector-identity-shuffle.ll | 2 + llvm/test/CodeGen/Generic/vector.ll | 2 + .../MLRegAlloc/default-eviction-advisor.ll | 2 + .../MLRegAlloc/default-priority-advisor.ll | 2 +- llvm/test/DebugInfo/Generic/lit.local.cfg | 2 +- llvm/test/Feature/optnone-llc.ll | 1 + llvm/test/Linker/subprogram-linkonce-weak.ll | 1 + llvm/test/MC/AsmParser/include.ll | 3 + .../FunctionAttrs/2008-09-03-Mutual.ll | 1 + .../FunctionAttrs/2008-09-03-ReadNone.ll | 1 + .../FunctionAttrs/2008-12-29-Constant.ll | 1 + .../Transforms/FunctionAttrs/argmemonly.ll | 1 + llvm/test/Transforms/FunctionAttrs/atomic.ll | 1 + .../Transforms/FunctionAttrs/convergent.ll | 1 + .../FunctionAttrs/incompatible_fn_attrs.ll | 1 + .../FunctionAttrs/int_sideeffect.ll | 1 + .../FunctionAttrs/make-buffer-rsrc.ll | 1 + .../Transforms/FunctionAttrs/nocapture.ll | 1 + llvm/test/Transforms/FunctionAttrs/nonnull.ll | 1 + .../Transforms/FunctionAttrs/norecurse.ll | 1 + llvm/test/Transforms/FunctionAttrs/nosync.ll | 1 + .../test/Transforms/FunctionAttrs/nounwind.ll | 1 + .../FunctionAttrs/operand-bundles-scc.ll | 1 + llvm/test/Transforms/FunctionAttrs/optnone.ll | 1 + .../FunctionAttrs/read-write-scc.ll | 1 + .../Transforms/FunctionAttrs/readattrs.ll | 1 + llvm/test/Transforms/FunctionAttrs/stats.ll | 1 + .../Transforms/FunctionAttrs/willreturn.ll | 1 + .../Transforms/FunctionAttrs/writeonly.ll | 1 + .../test/Transforms/IndVarSimplify/pr38855.ll | 1 + llvm/test/Transforms/Inline/cgscc-update.ll | 1 + .../LoopIdiom/X86/memset-size-compute.ll | 1 + .../LoopIdiom/basic-address-space.ll | 1 + llvm/test/Transforms/LoopIdiom/basic.ll | 1 + llvm/test/Transforms/LoopIdiom/debug-line.ll | 1 + .../Transforms/LoopIdiom/disable-options.ll | 1 + .../Transforms/LoopIdiom/int_sideeffect.ll | 1 + .../LoopIdiom/lir-heurs-multi-block-loop.ll | 1 + .../LoopIdiom/memset-debugify-remarks.ll | 1 + .../Transforms/LoopIdiom/memset-pr52104.ll | 1 + .../LoopIdiom/memset-runtime-32bit.ll | 1 + .../LoopIdiom/memset-runtime-64bit.ll | 1 + .../LoopIdiom/memset-runtime-debug.ll | 1 + llvm/test/Transforms/LoopIdiom/memset-tbaa.ll | 1 + llvm/test/Transforms/LoopIdiom/memset.ll | 1 + .../LoopIdiom/non-integral-pointers.ll | 1 + llvm/test/Transforms/LoopIdiom/pr70008.ll | 1 + llvm/test/Transforms/LoopIdiom/pr80954.ll | 1 + llvm/test/Transforms/LoopIdiom/reuse-cast.ll | 1 + .../Transforms/LoopIdiom/struct-custom-dl.ll | 1 + llvm/test/Transforms/LoopIdiom/struct.ll | 1 + .../Transforms/LoopIdiom/unroll-custom-dl.ll | 1 + llvm/test/Transforms/LoopIdiom/unroll.ll | 1 + .../X86/2011-11-29-postincphi.ll | 1 + .../LoopStrengthReduce/X86/ivchain-X86.ll | 1 + .../st_sink_bugfix_22613.ll | 1 + .../PhaseOrdering/X86/merge-functions.ll | 1 + .../X86/pr48844-br-to-switch-vectorization.ll | 1 + .../enable-loop-header-duplication-oz.ll | 1 + .../Transforms/PhaseOrdering/func-attrs.ll | 1 + .../Transforms/PhaseOrdering/memset-tail.ll | 1 + .../SimplifyCFG/X86/MagicPointer.ll | 1 + .../X86/switch-to-lookup-globals.ll | 1 + llvm/test/lit.cfg.py | 2 +- .../tools/llvm-reduce/file-output-type.test | 2 + .../DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 225 +++++- .../DebugInfo/DWARF/DWARFDebugLineTest.cpp | 228 +++++- .../DWARF/DWARFDieManualExtractTest.cpp | 4 + .../PassBuilderBindingsTest.cpp | 8 + llvm/unittests/TargetParser/TripleTest.cpp | 49 ++ 284 files changed, 7891 insertions(+), 271 deletions(-) create mode 100644 llvm/include/llvm/IR/IntrinsicsEVM.td create mode 100644 llvm/lib/Target/EVM/CMakeLists.txt create mode 100644 llvm/lib/Target/EVM/EVM.h create mode 100644 llvm/lib/Target/EVM/EVM.td create mode 100644 llvm/lib/Target/EVM/EVMArgumentMove.cpp create mode 100644 llvm/lib/Target/EVM/EVMAsmPrinter.cpp create mode 100644 llvm/lib/Target/EVM/EVMCodegenPrepare.cpp create mode 100644 llvm/lib/Target/EVM/EVMFrameLowering.cpp create mode 100644 llvm/lib/Target/EVM/EVMFrameLowering.h create mode 100644 llvm/lib/Target/EVM/EVMISD.def create mode 100644 llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp create mode 100644 llvm/lib/Target/EVM/EVMISelLowering.cpp create mode 100644 llvm/lib/Target/EVM/EVMISelLowering.h create mode 100644 llvm/lib/Target/EVM/EVMInstrFormats.td create mode 100644 llvm/lib/Target/EVM/EVMInstrInfo.cpp create mode 100644 llvm/lib/Target/EVM/EVMInstrInfo.h create mode 100644 llvm/lib/Target/EVM/EVMInstrInfo.td create mode 100644 llvm/lib/Target/EVM/EVMLinkRuntime.cpp create mode 100644 llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp create mode 100644 llvm/lib/Target/EVM/EVMMCInstLower.cpp create mode 100644 llvm/lib/Target/EVM/EVMMCInstLower.h create mode 100644 llvm/lib/Target/EVM/EVMRegisterInfo.cpp create mode 100644 llvm/lib/Target/EVM/EVMRegisterInfo.h create mode 100644 llvm/lib/Target/EVM/EVMRegisterInfo.td create mode 100644 llvm/lib/Target/EVM/EVMSubtarget.cpp create mode 100644 llvm/lib/Target/EVM/EVMSubtarget.h create mode 100644 llvm/lib/Target/EVM/EVMTargetMachine.cpp create mode 100644 llvm/lib/Target/EVM/EVMTargetMachine.h create mode 100644 llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp create mode 100644 llvm/lib/Target/EVM/EVMTargetTransformInfo.h create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp create mode 100644 llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h create mode 100644 llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt create mode 100644 llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp create mode 100644 llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h create mode 100644 llvm/lib/Target/EVM/evm-stdlib.ll create mode 100644 llvm/test/CodeGen/EVM/add.ll create mode 100644 llvm/test/CodeGen/EVM/aext.ll create mode 100644 llvm/test/CodeGen/EVM/br.ll create mode 100644 llvm/test/CodeGen/EVM/call.ll create mode 100644 llvm/test/CodeGen/EVM/div.ll create mode 100644 llvm/test/CodeGen/EVM/frameidx.ll create mode 100644 llvm/test/CodeGen/EVM/globals.ll create mode 100644 llvm/test/CodeGen/EVM/icmp.ll create mode 100644 llvm/test/CodeGen/EVM/intrinsic.ll create mode 100644 llvm/test/CodeGen/EVM/lit.local.cfg create mode 100644 llvm/test/CodeGen/EVM/load-narrowing-disable.ll create mode 100644 llvm/test/CodeGen/EVM/logical.ll create mode 100644 llvm/test/CodeGen/EVM/mem_call_data.ll create mode 100644 llvm/test/CodeGen/EVM/memintrinsics-opt.ll create mode 100644 llvm/test/CodeGen/EVM/memintrinsics.ll create mode 100644 llvm/test/CodeGen/EVM/memory.ll create mode 100644 llvm/test/CodeGen/EVM/mod.ll create mode 100644 llvm/test/CodeGen/EVM/mul.ll create mode 100644 llvm/test/CodeGen/EVM/select.ll create mode 100644 llvm/test/CodeGen/EVM/sext.ll create mode 100644 llvm/test/CodeGen/EVM/shift.ll create mode 100644 llvm/test/CodeGen/EVM/signextload.ll create mode 100644 llvm/test/CodeGen/EVM/stdlib-call-ellision.ll create mode 100644 llvm/test/CodeGen/EVM/storage.ll create mode 100644 llvm/test/CodeGen/EVM/sub.ll create mode 100644 llvm/test/CodeGen/EVM/truncstore.ll create mode 100644 llvm/test/CodeGen/EVM/tstorage.ll create mode 100644 llvm/test/CodeGen/EVM/zero_any_extload.ll create mode 100644 llvm/test/CodeGen/EVM/zext.ll diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 9d946450b0b1..a52d4e21f31b 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -473,6 +473,9 @@ set(LLVM_ALL_TARGETS ARM AVR BPF +# EVM local begin + EVM +# EVM local end Hexagon Lanai LoongArch @@ -498,6 +501,10 @@ set(LLVM_ALL_EXPERIMENTAL_TARGETS Xtensa ) +# EVM local begin +add_compile_definitions(_EVM) +# EVM local end + # List of targets with JIT support: set(LLVM_TARGETS_WITH_JIT X86 PowerPC AArch64 ARM Mips SystemZ) diff --git a/llvm/docs/CompilerWriterInfo.rst b/llvm/docs/CompilerWriterInfo.rst index 8b70dcb8b400..5e09295746a6 100644 --- a/llvm/docs/CompilerWriterInfo.rst +++ b/llvm/docs/CompilerWriterInfo.rst @@ -212,3 +212,10 @@ Miscellaneous Resources * `GCC prefetch project `_ page has a good survey of the prefetching capabilities of a variety of modern processors. + +EVM +=== + +* `https://www.evm.codes/about` + +* `https://github.com/ethereum/execution-specs/tree/master/src/ethereum/shanghai/vm` diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index 649711d8faf6..f1d0aefc31cb 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -28,6 +28,9 @@ #include "llvm/CodeGen/MachineOutliner.h" #include "llvm/CodeGen/RegisterClassInfo.h" #include "llvm/CodeGen/VirtRegMap.h" +// EVM local begin +#include "llvm/IR/Constants.h" +// EVM local end #include "llvm/MC/MCInstrInfo.h" #include "llvm/Support/BranchProbability.h" #include "llvm/Support/ErrorHandling.h" @@ -231,8 +234,14 @@ class TargetInstrInfo : public MCInstrInfo { /// that is set up between the frame setup and destroy pseudo instructions. int64_t getFrameSize(const MachineInstr &I) const { assert(isFrameInstr(I) && "Not a frame instruction"); - assert(I.getOperand(0).getImm() >= 0); - return I.getOperand(0).getImm(); + // EVM local begin + if (I.getOperand(0).isImm()) { + assert(I.getOperand(0).getImm() >= 0); + return I.getOperand(0).getImm(); + } else { + return I.getOperand(0).getCImm()->getZExtValue(); + } + // EVM local end } /// Returns the total frame size, which is made up of the space set up inside diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index 9d9886f4920a..b54d8f0d57a8 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -5075,7 +5075,6 @@ class TargetLowering : public TargetLoweringBase { //===--------------------------------------------------------------------===// // Div utility functions // - SDValue BuildSDIV(SDNode *N, SelectionDAG &DAG, bool IsAfterLegalization, SmallVectorImpl &Created) const; SDValue BuildUDIV(SDNode *N, SelectionDAG &DAG, bool IsAfterLegalization, diff --git a/llvm/include/llvm/CodeGen/ValueTypes.td b/llvm/include/llvm/CodeGen/ValueTypes.td index 963b6a71de38..ca2ea8191868 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.td +++ b/llvm/include/llvm/CodeGen/ValueTypes.td @@ -70,223 +70,227 @@ def i16 : VTInt<16, 6>; // 16-bit integer value def i32 : VTInt<32, 7>; // 32-bit integer value def i64 : VTInt<64, 8>; // 64-bit integer value def i128 : VTInt<128, 9>; // 128-bit integer value - -def bf16 : VTFP<16, 10>; // 16-bit brain floating point value -def f16 : VTFP<16, 11>; // 16-bit floating point value -def f32 : VTFP<32, 12>; // 32-bit floating point value -def f64 : VTFP<64, 13>; // 64-bit floating point value -def f80 : VTFP<80, 14>; // 80-bit floating point value -def f128 : VTFP<128, 15>; // 128-bit floating point value -def ppcf128 : VTFP<128, 16>; // PPC 128-bit floating point value - -def v1i1 : VTVec<1, i1, 17>; // 1 x i1 vector value -def v2i1 : VTVec<2, i1, 18>; // 2 x i1 vector value -def v3i1 : VTVec<3, i1, 19>; // 3 x i1 vector value -def v4i1 : VTVec<4, i1, 20>; // 4 x i1 vector value -def v8i1 : VTVec<8, i1, 21>; // 8 x i1 vector value -def v16i1 : VTVec<16, i1, 22>; // 16 x i1 vector value -def v32i1 : VTVec<32, i1, 23>; // 32 x i1 vector value -def v64i1 : VTVec<64, i1, 24>; // 64 x i1 vector value -def v128i1 : VTVec<128, i1, 25>; // 128 x i1 vector value -def v256i1 : VTVec<256, i1, 26>; // 256 x i1 vector value -def v512i1 : VTVec<512, i1, 27>; // 512 x i1 vector value -def v1024i1 : VTVec<1024, i1, 28>; // 1024 x i1 vector value -def v2048i1 : VTVec<2048, i1, 29>; // 2048 x i1 vector value - -def v128i2 : VTVec<128, i2, 30>; // 128 x i2 vector value -def v256i2 : VTVec<256, i2, 31>; // 256 x i2 vector value - -def v64i4 : VTVec<64, i4, 32>; // 64 x i4 vector value -def v128i4 : VTVec<128, i4, 33>; // 128 x i4 vector value - -def v1i8 : VTVec<1, i8, 34>; // 1 x i8 vector value -def v2i8 : VTVec<2, i8, 35>; // 2 x i8 vector value -def v3i8 : VTVec<3, i8, 36>; // 3 x i8 vector value -def v4i8 : VTVec<4, i8, 37>; // 4 x i8 vector value -def v8i8 : VTVec<8, i8, 38>; // 8 x i8 vector value -def v16i8 : VTVec<16, i8, 39>; // 16 x i8 vector value -def v32i8 : VTVec<32, i8, 40>; // 32 x i8 vector value -def v64i8 : VTVec<64, i8, 41>; // 64 x i8 vector value -def v128i8 : VTVec<128, i8, 42>; // 128 x i8 vector value -def v256i8 : VTVec<256, i8, 43>; // 256 x i8 vector value -def v512i8 : VTVec<512, i8, 44>; // 512 x i8 vector value -def v1024i8 : VTVec<1024, i8, 45>; // 1024 x i8 vector value - -def v1i16 : VTVec<1, i16, 46>; // 1 x i16 vector value -def v2i16 : VTVec<2, i16, 47>; // 2 x i16 vector value -def v3i16 : VTVec<3, i16, 48>; // 3 x i16 vector value -def v4i16 : VTVec<4, i16, 49>; // 4 x i16 vector value -def v8i16 : VTVec<8, i16, 50>; // 8 x i16 vector value -def v16i16 : VTVec<16, i16, 51>; // 16 x i16 vector value -def v32i16 : VTVec<32, i16, 52>; // 32 x i16 vector value -def v64i16 : VTVec<64, i16, 53>; // 64 x i16 vector value -def v128i16 : VTVec<128, i16, 54>; // 128 x i16 vector value -def v256i16 : VTVec<256, i16, 55>; // 256 x i16 vector value -def v512i16 : VTVec<512, i16, 56>; // 512 x i16 vector value - -def v1i32 : VTVec<1, i32, 57>; // 1 x i32 vector value -def v2i32 : VTVec<2, i32, 58>; // 2 x i32 vector value -def v3i32 : VTVec<3, i32, 59>; // 3 x i32 vector value -def v4i32 : VTVec<4, i32, 60>; // 4 x i32 vector value -def v5i32 : VTVec<5, i32, 61>; // 5 x i32 vector value -def v6i32 : VTVec<6, i32, 62>; // 6 x f32 vector value -def v7i32 : VTVec<7, i32, 63>; // 7 x f32 vector value -def v8i32 : VTVec<8, i32, 64>; // 8 x i32 vector value -def v9i32 : VTVec<9, i32, 65>; // 9 x i32 vector value -def v10i32 : VTVec<10, i32, 66>; // 10 x i32 vector value -def v11i32 : VTVec<11, i32, 67>; // 11 x i32 vector value -def v12i32 : VTVec<12, i32, 68>; // 12 x i32 vector value -def v16i32 : VTVec<16, i32, 69>; // 16 x i32 vector value -def v32i32 : VTVec<32, i32, 70>; // 32 x i32 vector value -def v64i32 : VTVec<64, i32, 71>; // 64 x i32 vector value -def v128i32 : VTVec<128, i32, 72>; // 128 x i32 vector value -def v256i32 : VTVec<256, i32, 73>; // 256 x i32 vector value -def v512i32 : VTVec<512, i32, 74>; // 512 x i32 vector value -def v1024i32 : VTVec<1024, i32, 75>; // 1024 x i32 vector value -def v2048i32 : VTVec<2048, i32, 76>; // 2048 x i32 vector value - -def v1i64 : VTVec<1, i64, 77>; // 1 x i64 vector value -def v2i64 : VTVec<2, i64, 78>; // 2 x i64 vector value -def v3i64 : VTVec<3, i64, 79>; // 3 x i64 vector value -def v4i64 : VTVec<4, i64, 80>; // 4 x i64 vector value -def v8i64 : VTVec<8, i64, 81>; // 8 x i64 vector value -def v16i64 : VTVec<16, i64, 82>; // 16 x i64 vector value -def v32i64 : VTVec<32, i64, 83>; // 32 x i64 vector value -def v64i64 : VTVec<64, i64, 84>; // 64 x i64 vector value -def v128i64 : VTVec<128, i64, 85>; // 128 x i64 vector value -def v256i64 : VTVec<256, i64, 86>; // 256 x i64 vector value - -def v1i128 : VTVec<1, i128, 87>; // 1 x i128 vector value - -def v1f16 : VTVec<1, f16, 88>; // 1 x f16 vector value -def v2f16 : VTVec<2, f16, 89>; // 2 x f16 vector value -def v3f16 : VTVec<3, f16, 90>; // 3 x f16 vector value -def v4f16 : VTVec<4, f16, 91>; // 4 x f16 vector value -def v8f16 : VTVec<8, f16, 92>; // 8 x f16 vector value -def v16f16 : VTVec<16, f16, 93>; // 16 x f16 vector value -def v32f16 : VTVec<32, f16, 94>; // 32 x f16 vector value -def v64f16 : VTVec<64, f16, 95>; // 64 x f16 vector value -def v128f16 : VTVec<128, f16, 96>; // 128 x f16 vector value -def v256f16 : VTVec<256, f16, 97>; // 256 x f16 vector value -def v512f16 : VTVec<512, f16, 98>; // 512 x f16 vector value - -def v2bf16 : VTVec<2, bf16, 99>; // 2 x bf16 vector value -def v3bf16 : VTVec<3, bf16, 100>; // 3 x bf16 vector value -def v4bf16 : VTVec<4, bf16, 101>; // 4 x bf16 vector value -def v8bf16 : VTVec<8, bf16, 102>; // 8 x bf16 vector value -def v16bf16 : VTVec<16, bf16, 103>; // 16 x bf16 vector value -def v32bf16 : VTVec<32, bf16, 104>; // 32 x bf16 vector value -def v64bf16 : VTVec<64, bf16, 105>; // 64 x bf16 vector value -def v128bf16 : VTVec<128, bf16, 106>; // 128 x bf16 vector value - -def v1f32 : VTVec<1, f32, 107>; // 1 x f32 vector value -def v2f32 : VTVec<2, f32, 108>; // 2 x f32 vector value -def v3f32 : VTVec<3, f32, 109>; // 3 x f32 vector value -def v4f32 : VTVec<4, f32, 110>; // 4 x f32 vector value -def v5f32 : VTVec<5, f32, 111>; // 5 x f32 vector value -def v6f32 : VTVec<6, f32, 112>; // 6 x f32 vector value -def v7f32 : VTVec<7, f32, 113>; // 7 x f32 vector value -def v8f32 : VTVec<8, f32, 114>; // 8 x f32 vector value -def v9f32 : VTVec<9, f32, 115>; // 9 x f32 vector value -def v10f32 : VTVec<10, f32, 116>; // 10 x f32 vector value -def v11f32 : VTVec<11, f32, 117>; // 11 x f32 vector value -def v12f32 : VTVec<12, f32, 118>; // 12 x f32 vector value -def v16f32 : VTVec<16, f32, 119>; // 16 x f32 vector value -def v32f32 : VTVec<32, f32, 120>; // 32 x f32 vector value -def v64f32 : VTVec<64, f32, 121>; // 64 x f32 vector value -def v128f32 : VTVec<128, f32, 122>; // 128 x f32 vector value -def v256f32 : VTVec<256, f32, 123>; // 256 x f32 vector value -def v512f32 : VTVec<512, f32, 124>; // 512 x f32 vector value -def v1024f32 : VTVec<1024, f32, 125>; // 1024 x f32 vector value -def v2048f32 : VTVec<2048, f32, 126>; // 2048 x f32 vector value - -def v1f64 : VTVec<1, f64, 127>; // 1 x f64 vector value -def v2f64 : VTVec<2, f64, 128>; // 2 x f64 vector value -def v3f64 : VTVec<3, f64, 129>; // 3 x f64 vector value -def v4f64 : VTVec<4, f64, 130>; // 4 x f64 vector value -def v8f64 : VTVec<8, f64, 131>; // 8 x f64 vector value -def v16f64 : VTVec<16, f64, 132>; // 16 x f64 vector value -def v32f64 : VTVec<32, f64, 133>; // 32 x f64 vector value -def v64f64 : VTVec<64, f64, 134>; // 64 x f64 vector value -def v128f64 : VTVec<128, f64, 135>; // 128 x f64 vector value -def v256f64 : VTVec<256, f64, 136>; // 256 x f64 vector value - -def nxv1i1 : VTScalableVec<1, i1, 137>; // n x 1 x i1 vector value -def nxv2i1 : VTScalableVec<2, i1, 138>; // n x 2 x i1 vector value -def nxv4i1 : VTScalableVec<4, i1, 139>; // n x 4 x i1 vector value -def nxv8i1 : VTScalableVec<8, i1, 140>; // n x 8 x i1 vector value -def nxv16i1 : VTScalableVec<16, i1, 141>; // n x 16 x i1 vector value -def nxv32i1 : VTScalableVec<32, i1, 142>; // n x 32 x i1 vector value -def nxv64i1 : VTScalableVec<64, i1, 143>; // n x 64 x i1 vector value - -def nxv1i8 : VTScalableVec<1, i8, 144>; // n x 1 x i8 vector value -def nxv2i8 : VTScalableVec<2, i8, 145>; // n x 2 x i8 vector value -def nxv4i8 : VTScalableVec<4, i8, 146>; // n x 4 x i8 vector value -def nxv8i8 : VTScalableVec<8, i8, 147>; // n x 8 x i8 vector value -def nxv16i8 : VTScalableVec<16, i8, 148>; // n x 16 x i8 vector value -def nxv32i8 : VTScalableVec<32, i8, 149>; // n x 32 x i8 vector value -def nxv64i8 : VTScalableVec<64, i8, 150>; // n x 64 x i8 vector value - -def nxv1i16 : VTScalableVec<1, i16, 151>; // n x 1 x i16 vector value -def nxv2i16 : VTScalableVec<2, i16, 152>; // n x 2 x i16 vector value -def nxv4i16 : VTScalableVec<4, i16, 153>; // n x 4 x i16 vector value -def nxv8i16 : VTScalableVec<8, i16, 154>; // n x 8 x i16 vector value -def nxv16i16 : VTScalableVec<16, i16, 155>; // n x 16 x i16 vector value -def nxv32i16 : VTScalableVec<32, i16, 156>; // n x 32 x i16 vector value - -def nxv1i32 : VTScalableVec<1, i32, 157>; // n x 1 x i32 vector value -def nxv2i32 : VTScalableVec<2, i32, 158>; // n x 2 x i32 vector value -def nxv4i32 : VTScalableVec<4, i32, 159>; // n x 4 x i32 vector value -def nxv8i32 : VTScalableVec<8, i32, 160>; // n x 8 x i32 vector value -def nxv16i32 : VTScalableVec<16, i32, 161>; // n x 16 x i32 vector value -def nxv32i32 : VTScalableVec<32, i32, 162>; // n x 32 x i32 vector value - -def nxv1i64 : VTScalableVec<1, i64, 163>; // n x 1 x i64 vector value -def nxv2i64 : VTScalableVec<2, i64, 164>; // n x 2 x i64 vector value -def nxv4i64 : VTScalableVec<4, i64, 165>; // n x 4 x i64 vector value -def nxv8i64 : VTScalableVec<8, i64, 166>; // n x 8 x i64 vector value -def nxv16i64 : VTScalableVec<16, i64, 167>; // n x 16 x i64 vector value -def nxv32i64 : VTScalableVec<32, i64, 168>; // n x 32 x i64 vector value - -def nxv1f16 : VTScalableVec<1, f16, 169>; // n x 1 x f16 vector value -def nxv2f16 : VTScalableVec<2, f16, 170>; // n x 2 x f16 vector value -def nxv4f16 : VTScalableVec<4, f16, 171>; // n x 4 x f16 vector value -def nxv8f16 : VTScalableVec<8, f16, 172>; // n x 8 x f16 vector value -def nxv16f16 : VTScalableVec<16, f16, 173>; // n x 16 x f16 vector value -def nxv32f16 : VTScalableVec<32, f16, 174>; // n x 32 x f16 vector value - -def nxv1bf16 : VTScalableVec<1, bf16, 175>; // n x 1 x bf16 vector value -def nxv2bf16 : VTScalableVec<2, bf16, 176>; // n x 2 x bf16 vector value -def nxv4bf16 : VTScalableVec<4, bf16, 177>; // n x 4 x bf16 vector value -def nxv8bf16 : VTScalableVec<8, bf16, 178>; // n x 8 x bf16 vector value -def nxv16bf16 : VTScalableVec<16, bf16, 179>; // n x 16 x bf16 vector value -def nxv32bf16 : VTScalableVec<32, bf16, 180>; // n x 32 x bf16 vector value - -def nxv1f32 : VTScalableVec<1, f32, 181>; // n x 1 x f32 vector value -def nxv2f32 : VTScalableVec<2, f32, 182>; // n x 2 x f32 vector value -def nxv4f32 : VTScalableVec<4, f32, 183>; // n x 4 x f32 vector value -def nxv8f32 : VTScalableVec<8, f32, 184>; // n x 8 x f32 vector value -def nxv16f32 : VTScalableVec<16, f32, 185>; // n x 16 x f32 vector value - -def nxv1f64 : VTScalableVec<1, f64, 186>; // n x 1 x f64 vector value -def nxv2f64 : VTScalableVec<2, f64, 187>; // n x 2 x f64 vector value -def nxv4f64 : VTScalableVec<4, f64, 188>; // n x 4 x f64 vector value -def nxv8f64 : VTScalableVec<8, f64, 189>; // n x 8 x f64 vector value - -def x86mmx : ValueType<64, 190>; // X86 MMX value -def Glue : ValueType<0, 191>; // Pre-RA sched glue -def isVoid : ValueType<0, 192>; // Produces no value -def untyped : ValueType<8, 193> { // Produces an untyped value +// EVM local begin +def i256 : VTInt<256, 10>; // 256-bit integer value +def i512 : VTInt<512, 11>; // 512-bit integer value + +def bf16 : VTFP<16, 12>; // 16-bit brain floating point value +def f16 : VTFP<16, 13>; // 16-bit floating point value +def f32 : VTFP<32, 14>; // 32-bit floating point value +def f64 : VTFP<64, 15>; // 64-bit floating point value +def f80 : VTFP<80, 16>; // 80-bit floating point value +def f128 : VTFP<128, 17>; // 128-bit floating point value +def ppcf128 : VTFP<128, 18>; // PPC 128-bit floating point value + +def v1i1 : VTVec<1, i1, 19>; // 1 x i1 vector value +def v2i1 : VTVec<2, i1, 20>; // 2 x i1 vector value +def v3i1 : VTVec<3, i1, 21>; // 3 x i1 vector value +def v4i1 : VTVec<4, i1, 22>; // 4 x i1 vector value +def v8i1 : VTVec<8, i1, 23>; // 8 x i1 vector value +def v16i1 : VTVec<16, i1, 24>; // 16 x i1 vector value +def v32i1 : VTVec<32, i1, 25>; // 32 x i1 vector value +def v64i1 : VTVec<64, i1, 26>; // 64 x i1 vector value +def v128i1 : VTVec<128, i1, 27>; // 128 x i1 vector value +def v256i1 : VTVec<256, i1, 28>; // 256 x i1 vector value +def v512i1 : VTVec<512, i1, 29>; // 512 x i1 vector value +def v1024i1 : VTVec<1024, i1, 30>; // 1024 x i1 vector value +def v2048i1 : VTVec<2048, i1, 31>; // 2048 x i1 vector value + +def v128i2 : VTVec<128, i2, 32>; // 128 x i2 vector value +def v256i2 : VTVec<256, i2, 33>; // 256 x i2 vector value + +def v64i4 : VTVec<64, i4, 34>; // 64 x i4 vector value +def v128i4 : VTVec<128, i4, 35>; // 128 x i4 vector value + +def v1i8 : VTVec<1, i8, 36>; // 1 x i8 vector value +def v2i8 : VTVec<2, i8, 37>; // 2 x i8 vector value +def v3i8 : VTVec<3, i8, 38>; // 3 x i8 vector value +def v4i8 : VTVec<4, i8, 39>; // 4 x i8 vector value +def v8i8 : VTVec<8, i8, 40>; // 8 x i8 vector value +def v16i8 : VTVec<16, i8, 41>; // 16 x i8 vector value +def v32i8 : VTVec<32, i8, 42>; // 32 x i8 vector value +def v64i8 : VTVec<64, i8, 43>; // 64 x i8 vector value +def v128i8 : VTVec<128, i8, 44>; // 128 x i8 vector value +def v256i8 : VTVec<256, i8, 45>; // 256 x i8 vector value +def v512i8 : VTVec<512, i8, 46>; // 512 x i8 vector value +def v1024i8 : VTVec<1024, i8, 47>; // 1024 x i8 vector value + +def v1i16 : VTVec<1, i16, 48>; // 1 x i16 vector value +def v2i16 : VTVec<2, i16, 49>; // 2 x i16 vector value +def v3i16 : VTVec<3, i16, 50>; // 3 x i16 vector value +def v4i16 : VTVec<4, i16, 51>; // 4 x i16 vector value +def v8i16 : VTVec<8, i16, 52>; // 8 x i16 vector value +def v16i16 : VTVec<16, i16, 53>; // 16 x i16 vector value +def v32i16 : VTVec<32, i16, 54>; // 32 x i16 vector value +def v64i16 : VTVec<64, i16, 55>; // 64 x i16 vector value +def v128i16 : VTVec<128, i16, 56>; // 128 x i16 vector value +def v256i16 : VTVec<256, i16, 57>; // 256 x i16 vector value +def v512i16 : VTVec<512, i16, 58>; // 512 x i16 vector value + +def v1i32 : VTVec<1, i32, 59>; // 1 x i32 vector value +def v2i32 : VTVec<2, i32, 60>; // 2 x i32 vector value +def v3i32 : VTVec<3, i32, 61>; // 3 x i32 vector value +def v4i32 : VTVec<4, i32, 62>; // 4 x i32 vector value +def v5i32 : VTVec<5, i32, 63>; // 5 x i32 vector value +def v6i32 : VTVec<6, i32, 64>; // 6 x f32 vector value +def v7i32 : VTVec<7, i32, 65>; // 7 x f32 vector value +def v8i32 : VTVec<8, i32, 66>; // 8 x i32 vector value +def v9i32 : VTVec<9, i32, 67>; // 9 x i32 vector value +def v10i32 : VTVec<10, i32, 68>; // 10 x i32 vector value +def v11i32 : VTVec<11, i32, 69>; // 11 x i32 vector value +def v12i32 : VTVec<12, i32, 70>; // 12 x i32 vector value +def v16i32 : VTVec<16, i32, 71>; // 16 x i32 vector value +def v32i32 : VTVec<32, i32, 72>; // 32 x i32 vector value +def v64i32 : VTVec<64, i32, 73>; // 64 x i32 vector value +def v128i32 : VTVec<128, i32, 74>; // 128 x i32 vector value +def v256i32 : VTVec<256, i32, 75>; // 256 x i32 vector value +def v512i32 : VTVec<512, i32, 76>; // 512 x i32 vector value +def v1024i32 : VTVec<1024, i32, 77>; // 1024 x i32 vector value +def v2048i32 : VTVec<2048, i32, 78>; // 2048 x i32 vector value + +def v1i64 : VTVec<1, i64, 79>; // 1 x i64 vector value +def v2i64 : VTVec<2, i64, 80>; // 2 x i64 vector value +def v3i64 : VTVec<3, i64, 81>; // 3 x i64 vector value +def v4i64 : VTVec<4, i64, 82>; // 4 x i64 vector value +def v8i64 : VTVec<8, i64, 83>; // 8 x i64 vector value +def v16i64 : VTVec<16, i64, 84>; // 16 x i64 vector value +def v32i64 : VTVec<32, i64, 85>; // 32 x i64 vector value +def v64i64 : VTVec<64, i64, 86>; // 64 x i64 vector value +def v128i64 : VTVec<128, i64, 87>; // 128 x i64 vector value +def v256i64 : VTVec<256, i64, 88>; // 256 x i64 vector value + +def v1i128 : VTVec<1, i128, 89>; // 1 x i128 vector value + +def v1f16 : VTVec<1, f16, 90>; // 1 x f16 vector value +def v2f16 : VTVec<2, f16, 91>; // 2 x f16 vector value +def v3f16 : VTVec<3, f16, 92>; // 3 x f16 vector value +def v4f16 : VTVec<4, f16, 93>; // 4 x f16 vector value +def v8f16 : VTVec<8, f16, 94>; // 8 x f16 vector value +def v16f16 : VTVec<16, f16, 95>; // 16 x f16 vector value +def v32f16 : VTVec<32, f16, 96>; // 32 x f16 vector value +def v64f16 : VTVec<64, f16, 97>; // 64 x f16 vector value +def v128f16 : VTVec<128, f16, 98>; // 128 x f16 vector value +def v256f16 : VTVec<256, f16, 99>; // 256 x f16 vector value +def v512f16 : VTVec<512, f16, 100>; // 512 x f16 vector value + +def v2bf16 : VTVec<2, bf16, 101>; // 2 x bf16 vector value +def v3bf16 : VTVec<3, bf16, 102>; // 3 x bf16 vector value +def v4bf16 : VTVec<4, bf16, 103>; // 4 x bf16 vector value +def v8bf16 : VTVec<8, bf16, 104>; // 8 x bf16 vector value +def v16bf16 : VTVec<16, bf16, 105>; // 16 x bf16 vector value +def v32bf16 : VTVec<32, bf16, 106>; // 32 x bf16 vector value +def v64bf16 : VTVec<64, bf16, 107>; // 64 x bf16 vector value +def v128bf16 : VTVec<128, bf16, 108>; // 128 x bf16 vector value + +def v1f32 : VTVec<1, f32, 109>; // 1 x f32 vector value +def v2f32 : VTVec<2, f32, 110>; // 2 x f32 vector value +def v3f32 : VTVec<3, f32, 111>; // 3 x f32 vector value +def v4f32 : VTVec<4, f32, 112>; // 4 x f32 vector value +def v5f32 : VTVec<5, f32, 113>; // 5 x f32 vector value +def v6f32 : VTVec<6, f32, 114>; // 6 x f32 vector value +def v7f32 : VTVec<7, f32, 115>; // 7 x f32 vector value +def v8f32 : VTVec<8, f32, 116>; // 8 x f32 vector value +def v9f32 : VTVec<9, f32, 117>; // 9 x f32 vector value +def v10f32 : VTVec<10, f32, 118>; // 10 x f32 vector value +def v11f32 : VTVec<11, f32, 119>; // 11 x f32 vector value +def v12f32 : VTVec<12, f32, 120>; // 12 x f32 vector value +def v16f32 : VTVec<16, f32, 121>; // 16 x f32 vector value +def v32f32 : VTVec<32, f32, 122>; // 32 x f32 vector value +def v64f32 : VTVec<64, f32, 123>; // 64 x f32 vector value +def v128f32 : VTVec<128, f32, 124>; // 128 x f32 vector value +def v256f32 : VTVec<256, f32, 125>; // 256 x f32 vector value +def v512f32 : VTVec<512, f32, 126>; // 512 x f32 vector value +def v1024f32 : VTVec<1024, f32, 127>; // 1024 x f32 vector value +def v2048f32 : VTVec<2048, f32, 128>; // 2048 x f32 vector value + +def v1f64 : VTVec<1, f64, 129>; // 1 x f64 vector value +def v2f64 : VTVec<2, f64, 130>; // 2 x f64 vector value +def v3f64 : VTVec<3, f64, 131>; // 3 x f64 vector value +def v4f64 : VTVec<4, f64, 132>; // 4 x f64 vector value +def v8f64 : VTVec<8, f64, 133>; // 8 x f64 vector value +def v16f64 : VTVec<16, f64, 134>; // 16 x f64 vector value +def v32f64 : VTVec<32, f64, 135>; // 32 x f64 vector value +def v64f64 : VTVec<64, f64, 136>; // 64 x f64 vector value +def v128f64 : VTVec<128, f64, 137>; // 128 x f64 vector value +def v256f64 : VTVec<256, f64, 138>; // 256 x f64 vector value + +def nxv1i1 : VTScalableVec<1, i1, 139>; // n x 1 x i1 vector value +def nxv2i1 : VTScalableVec<2, i1, 140>; // n x 2 x i1 vector value +def nxv4i1 : VTScalableVec<4, i1, 141>; // n x 4 x i1 vector value +def nxv8i1 : VTScalableVec<8, i1, 142>; // n x 8 x i1 vector value +def nxv16i1 : VTScalableVec<16, i1, 143>; // n x 16 x i1 vector value +def nxv32i1 : VTScalableVec<32, i1, 144>; // n x 32 x i1 vector value +def nxv64i1 : VTScalableVec<64, i1, 145>; // n x 64 x i1 vector value + +def nxv1i8 : VTScalableVec<1, i8, 146>; // n x 1 x i8 vector value +def nxv2i8 : VTScalableVec<2, i8, 147>; // n x 2 x i8 vector value +def nxv4i8 : VTScalableVec<4, i8, 148>; // n x 4 x i8 vector value +def nxv8i8 : VTScalableVec<8, i8, 149>; // n x 8 x i8 vector value +def nxv16i8 : VTScalableVec<16, i8, 150>; // n x 16 x i8 vector value +def nxv32i8 : VTScalableVec<32, i8, 151>; // n x 32 x i8 vector value +def nxv64i8 : VTScalableVec<64, i8, 152>; // n x 64 x i8 vector value + +def nxv1i16 : VTScalableVec<1, i16, 153>; // n x 1 x i16 vector value +def nxv2i16 : VTScalableVec<2, i16, 154>; // n x 2 x i16 vector value +def nxv4i16 : VTScalableVec<4, i16, 155>; // n x 4 x i16 vector value +def nxv8i16 : VTScalableVec<8, i16, 156>; // n x 8 x i16 vector value +def nxv16i16 : VTScalableVec<16, i16, 157>; // n x 16 x i16 vector value +def nxv32i16 : VTScalableVec<32, i16, 158>; // n x 32 x i16 vector value + +def nxv1i32 : VTScalableVec<1, i32, 159>; // n x 1 x i32 vector value +def nxv2i32 : VTScalableVec<2, i32, 160>; // n x 2 x i32 vector value +def nxv4i32 : VTScalableVec<4, i32, 161>; // n x 4 x i32 vector value +def nxv8i32 : VTScalableVec<8, i32, 162>; // n x 8 x i32 vector value +def nxv16i32 : VTScalableVec<16, i32, 163>; // n x 16 x i32 vector value +def nxv32i32 : VTScalableVec<32, i32, 164>; // n x 32 x i32 vector value + +def nxv1i64 : VTScalableVec<1, i64, 165>; // n x 1 x i64 vector value +def nxv2i64 : VTScalableVec<2, i64, 166>; // n x 2 x i64 vector value +def nxv4i64 : VTScalableVec<4, i64, 167>; // n x 4 x i64 vector value +def nxv8i64 : VTScalableVec<8, i64, 168>; // n x 8 x i64 vector value +def nxv16i64 : VTScalableVec<16, i64, 169>; // n x 16 x i64 vector value +def nxv32i64 : VTScalableVec<32, i64, 170>; // n x 32 x i64 vector value + +def nxv1f16 : VTScalableVec<1, f16, 171>; // n x 1 x f16 vector value +def nxv2f16 : VTScalableVec<2, f16, 172>; // n x 2 x f16 vector value +def nxv4f16 : VTScalableVec<4, f16, 173>; // n x 4 x f16 vector value +def nxv8f16 : VTScalableVec<8, f16, 174>; // n x 8 x f16 vector value +def nxv16f16 : VTScalableVec<16, f16, 175>; // n x 16 x f16 vector value +def nxv32f16 : VTScalableVec<32, f16, 176>; // n x 32 x f16 vector value + +def nxv1bf16 : VTScalableVec<1, bf16, 177>; // n x 1 x bf16 vector value +def nxv2bf16 : VTScalableVec<2, bf16, 178>; // n x 2 x bf16 vector value +def nxv4bf16 : VTScalableVec<4, bf16, 179>; // n x 4 x bf16 vector value +def nxv8bf16 : VTScalableVec<8, bf16, 180>; // n x 8 x bf16 vector value +def nxv16bf16 : VTScalableVec<16, bf16, 181>; // n x 16 x bf16 vector value +def nxv32bf16 : VTScalableVec<32, bf16, 182>; // n x 32 x bf16 vector value + +def nxv1f32 : VTScalableVec<1, f32, 183>; // n x 1 x f32 vector value +def nxv2f32 : VTScalableVec<2, f32, 184>; // n x 2 x f32 vector value +def nxv4f32 : VTScalableVec<4, f32, 185>; // n x 4 x f32 vector value +def nxv8f32 : VTScalableVec<8, f32, 186>; // n x 8 x f32 vector value +def nxv16f32 : VTScalableVec<16, f32, 187>; // n x 16 x f32 vector value + +def nxv1f64 : VTScalableVec<1, f64, 188>; // n x 1 x f64 vector value +def nxv2f64 : VTScalableVec<2, f64, 189>; // n x 2 x f64 vector value +def nxv4f64 : VTScalableVec<4, f64, 190>; // n x 4 x f64 vector value +def nxv8f64 : VTScalableVec<8, f64, 191>; // n x 8 x f64 vector value + +def x86mmx : ValueType<64, 192>; // X86 MMX value +def Glue : ValueType<0, 193>; // Pre-RA sched glue +def isVoid : ValueType<0, 194>; // Produces no value +def untyped : ValueType<8, 195> { // Produces an untyped value let LLVMName = "Untyped"; } -def funcref : ValueType<0, 194>; // WebAssembly's funcref type -def externref : ValueType<0, 195>; // WebAssembly's externref type -def exnref : ValueType<0, 196>; // WebAssembly's exnref type -def x86amx : ValueType<8192, 197>; // X86 AMX value -def i64x8 : ValueType<512, 198>; // 8 Consecutive GPRs (AArch64) +def funcref : ValueType<0, 196>; // WebAssembly's funcref type +def externref : ValueType<0, 197>; // WebAssembly's externref type +def exnref : ValueType<0, 198>; // WebAssembly's exnref type +def x86amx : ValueType<8192, 199>; // X86 AMX value +def i64x8 : ValueType<512, 200>; // 8 Consecutive GPRs (AArch64) def aarch64svcount - : ValueType<16, 199>; // AArch64 predicate-as-counter -def spirvbuiltin : ValueType<0, 200>; // SPIR-V's builtin type + : ValueType<16, 201>; // AArch64 predicate-as-counter +def spirvbuiltin : ValueType<0, 202>; // SPIR-V's builtin type +// EVM local end let isNormalValueType = false in { def token : ValueType<0, 248>; // TokenTy diff --git a/llvm/include/llvm/IR/CMakeLists.txt b/llvm/include/llvm/IR/CMakeLists.txt index 468d663796ed..71dcca28d054 100644 --- a/llvm/include/llvm/IR/CMakeLists.txt +++ b/llvm/include/llvm/IR/CMakeLists.txt @@ -22,4 +22,7 @@ tablegen(LLVM IntrinsicsWebAssembly.h -gen-intrinsic-enums -intrinsic-prefix=was tablegen(LLVM IntrinsicsX86.h -gen-intrinsic-enums -intrinsic-prefix=x86) tablegen(LLVM IntrinsicsXCore.h -gen-intrinsic-enums -intrinsic-prefix=xcore) tablegen(LLVM IntrinsicsVE.h -gen-intrinsic-enums -intrinsic-prefix=ve) +# EVM local begin +tablegen(LLVM IntrinsicsEVM.h -gen-intrinsic-enums -intrinsic-prefix=evm) +# EVM local end add_public_tablegen_target(intrinsics_gen) diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 31a1fef32199..b96eb987136a 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -532,6 +532,11 @@ class IRBuilderBase { /// Fetch the type representing a 128-bit integer. IntegerType *getInt128Ty() { return Type::getInt128Ty(Context); } + // EVM local begin + /// Fetch the type representing a 256-bit integer. + IntegerType *getInt256Ty() { return Type::getInt256Ty(Context); } + // EVM local end + /// Fetch the type representing an N-bit integer. IntegerType *getIntNTy(unsigned N) { return Type::getIntNTy(Context, N); diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index b4e758136b39..dee43218b12b 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -321,6 +321,9 @@ def IIT_I4 : IIT_Int<4, 58>; def IIT_AARCH64_SVCOUNT : IIT_VT; def IIT_V6 : IIT_Vec<6, 60>; def IIT_V10 : IIT_Vec<10, 61>; +// EVM local begin +def IIT_I256 : IIT_Int<256, 62>; +// EVM local end } defvar IIT_all_FixedTypes = !filter(iit, IIT_all, @@ -483,6 +486,10 @@ def llvm_i16_ty : LLVMType; def llvm_i32_ty : LLVMType; def llvm_i64_ty : LLVMType; def llvm_i128_ty : LLVMType; +// EVM local begin +def llvm_i256_ty : LLVMType; +def llvm_i512_ty : LLVMType; +// EVM local end def llvm_half_ty : LLVMType; def llvm_bfloat_ty : LLVMType; def llvm_float_ty : LLVMType; @@ -2764,5 +2771,7 @@ include "llvm/IR/IntrinsicsSPIRV.td" include "llvm/IR/IntrinsicsVE.td" include "llvm/IR/IntrinsicsDirectX.td" include "llvm/IR/IntrinsicsLoongArch.td" - +// EVM local begin +include "llvm/IR/IntrinsicsEVM.td" +// EVM local end #endif // TEST_INTRINSICS_SUPPRESS_DEFS diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td new file mode 100644 index 000000000000..6a0f6bf29425 --- /dev/null +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -0,0 +1,249 @@ +//===---- IntrinsicsEVM.td - Defines EVM intrinsics --------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines all EVM intrinsics. +// +//===----------------------------------------------------------------------===// + +// This must be in sync with the declaration of address spaces in EVM.h +def AS { + int STACK = 0; + int HEAP = 1; + int CALL_DATA = 2; + int RETURN_DATA = 3; + int CODE = 4; + int STORAGE = 5; + int TSTORAGE = 6; +} + +let TargetPrefix = "evm" in { + +// Arithmetical operations. +def int_evm_div + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_sdiv + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_mod + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_smod + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_shl + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_shr + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_sar + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_addmod + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrNoMem]>; + +def int_evm_mulmod + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrNoMem]>; + +def int_evm_exp + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_sha3 + : Intrinsic<[llvm_i256_ty], + [LLVMQualPointerType, llvm_i256_ty], [IntrReadMem]>; + +def int_evm_signextend + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + +def int_evm_byte + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem]>; + +// Memory operations. +def int_evm_mstore8 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], [IntrWriteMem]>; + +// TODO: CPR-1555. +// Review system intrinsics attributes taking into account EVM. +def int_evm_msize : Intrinsic<[llvm_i256_ty], [], []>; + +def int_evm_pc : Intrinsic<[llvm_i256_ty], [], []>; + +def int_evm_gas : Intrinsic<[llvm_i256_ty], [], []>; + + +// Getting values from the context. +def int_evm_address : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_origin : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_caller : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], []>; + +def int_evm_callvalue : Intrinsic<[llvm_i256_ty], [], []>; + +def int_evm_calldatasize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_calldataload + : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], []>; + +def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +// TODO: Ensure the gasprice is fixed while a function execution. +def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_extcodesize + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; + +def int_evm_extcodecopy + : Intrinsic<[], [llvm_i256_ty, LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty], [IntrWriteMem]>; + +def int_evm_returndatasize : Intrinsic<[llvm_i256_ty], [], []>; + +def int_evm_extcodehash + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; + +def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; + +def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; + +def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_number : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], []>; + +def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; + +// Logging. +def int_evm_log0 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], []>; + +def int_evm_log1 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], []>; + +def int_evm_log2 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty], []>; + +def int_evm_log3 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty], []>; + +def int_evm_log4 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], []>; + +// System calls +def int_evm_create + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], []>; + +def int_evm_call + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], []>; + +def int_evm_callcode + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], []>; + +def int_evm_delegatecall + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], []>; + +def int_evm_create2 + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, llvm_i256_ty], []>; + +def int_evm_staticcall + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], []>; + + +def int_evm_selfdestruct: Intrinsic<[], [llvm_i256_ty], []>; + +def int_evm_stop: Intrinsic<[], [], [IntrNoReturn]>; + +def int_evm_invalid: Intrinsic<[], [], [IntrNoReturn]>; + +// Return with error. +def int_evm_return: Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrNoReturn]>; + +def int_evm_revert: Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrNoReturn]>; + +// Stack manipulation. +def int_evm_pop: Intrinsic<[], [llvm_i256_ty], [IntrNoReturn]>; + +def int_evm_memmoveas1as1 + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, + IntrNoCallback, + NoCapture>, NoCapture>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +def int_evm_memcpyas1as2 + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, + IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +def int_evm_memcpyas1as3 + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, + IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +def int_evm_memcpyas1as4 + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, + IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; + +} // TargetPrefix = "evm" diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h index 1f0133c08e7d..636ac022b4c7 100644 --- a/llvm/include/llvm/IR/Type.h +++ b/llvm/include/llvm/IR/Type.h @@ -463,6 +463,10 @@ class Type { static IntegerType *getInt32Ty(LLVMContext &C); static IntegerType *getInt64Ty(LLVMContext &C); static IntegerType *getInt128Ty(LLVMContext &C); + // EVM local begin + static IntegerType *getInt256Ty(LLVMContext &C); + static IntegerType *getInt512Ty(LLVMContext &C); + // EVM local end template static Type *getScalarTy(LLVMContext &C) { int noOfBits = sizeof(ScalarTy) * CHAR_BIT; if (std::is_integral::value) { @@ -484,6 +488,9 @@ class Type { // static Type *getWasm_ExternrefTy(LLVMContext &C); static Type *getWasm_FuncrefTy(LLVMContext &C); + // EVM local begin + static PointerType *getInt256PtrTy(LLVMContext &C, unsigned AS = 0); + // EVM local end /// Return a pointer to the current type. This is equivalent to /// PointerType::get(Foo, AddrSpace). diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index add60ef7006b..0334fa74a762 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -495,6 +495,11 @@ class MCAsmInfo { /// '$' character to distinguish them from absolute names. bool UseParensForDollarSignNames = true; + // EVM local begin + /// True if target uses @symbol syntax. + bool PrependSymbolRefWithAt = false; + // EVM local end + /// True if the target supports flags in ".loc" directive, false if only /// location is allowed. bool SupportsExtendedDwarfLocDirective = true; @@ -806,6 +811,9 @@ class MCAsmInfo { bool useParensForDollarSignNames() const { return UseParensForDollarSignNames; } + // EVM local begin + bool prependSymbolRefWithAt() const { return PrependSymbolRefWithAt; } + // EVM local end bool supportsExtendedDwarfLocDirective() const { return SupportsExtendedDwarfLocDirective; } diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h index d2126a03db90..f843fc2f016b 100644 --- a/llvm/include/llvm/TargetParser/Triple.h +++ b/llvm/include/llvm/TargetParser/Triple.h @@ -106,6 +106,9 @@ class Triple { wasm64, // WebAssembly with 64-bit pointers renderscript32, // 32-bit RenderScript renderscript64, // 64-bit RenderScript + // EVM local begin + evm, // EVM: evm + // EVM local end ve, // NEC SX-Aurora Vector Engine LastArchType = ve }; @@ -1069,6 +1072,12 @@ class Triple { Env == llvm::Triple::EABIHF; } + // EVM local begin + bool isEVM() const { + return getArch() == Triple::evm; + } + // EVM local end + /// Tests whether the target supports comdat bool supportsCOMDAT() const { return !(isOSBinFormatMachO() || isOSBinFormatXCOFF() || diff --git a/llvm/lib/Analysis/MemoryLocation.cpp b/llvm/lib/Analysis/MemoryLocation.cpp index e0cd320e946a..72dd5b98ac13 100644 --- a/llvm/lib/Analysis/MemoryLocation.cpp +++ b/llvm/lib/Analysis/MemoryLocation.cpp @@ -167,6 +167,33 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, if (const IntrinsicInst *II = dyn_cast(Call)) { const DataLayout &DL = II->getDataLayout(); + // EVM local begin + // Check if the length is within i64 range. If not, return imprecise + // location. + auto T = Call->getModule()->getTargetTriple(); + if (Triple(T).isEVM()) { + switch (II->getIntrinsicID()) { + case Intrinsic::memcpy: + case Intrinsic::memcpy_inline: + case Intrinsic::memmove: + case Intrinsic::memset: + if (ConstantInt *LenCI = dyn_cast(II->getArgOperand(2))) + if (LenCI->getValue().getActiveBits() > 64) { + return MemoryLocation::getBeforeOrAfter(Call->getArgOperand(ArgIdx), + AATags); + } + break; + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + // it is okay to have lifetime intrinsic + break; + default: + llvm_unreachable("Unexpected intrinsic for EVM target"); + break; + } + } + // EVM local end + switch (II->getIntrinsicID()) { default: break; diff --git a/llvm/lib/CodeGen/MachineBasicBlock.cpp b/llvm/lib/CodeGen/MachineBasicBlock.cpp index d681d00b5d8c..67f23fe7a8ec 100644 --- a/llvm/lib/CodeGen/MachineBasicBlock.cpp +++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp @@ -708,8 +708,10 @@ void MachineBasicBlock::updateTerminator( if (TBB) { // The block has an unconditional branch. If its successor is now its // layout successor, delete the branch. - if (isLayoutSuccessor(TBB)) + // EVM local begin + if (isLayoutSuccessor(TBB) && !FBB) TII->removeBranch(*this); + // EVM local end } else { // The block has an unconditional fallthrough, or the end of the block is // unreachable. diff --git a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp index 19950f3eb67b..b7603854f951 100644 --- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp +++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp @@ -414,6 +414,10 @@ class PreISelIntrinsicLoweringLegacyPass : public ModulePass { }; const auto &TM = getAnalysis().getTM(); + // EVM local begin + if (TM.getTargetTriple().isEVM()) + return false; + // EVM local end PreISelIntrinsicLowering Lowering(TM, LookupTTI); return Lowering.lowerIntrinsics(M); } @@ -438,6 +442,10 @@ ModulePass *llvm::createPreISelIntrinsicLoweringPass() { PreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M, ModuleAnalysisManager &AM) { + // EVM local begin + if (TM.getTargetTriple().isEVM()) + return PreservedAnalyses::all(); + // EVM local end auto &FAM = AM.getResult(M).getManager(); auto LookupTTI = [&FAM](Function &F) -> TargetTransformInfo & { diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 4ce92e156cf8..334d307dc6c8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -405,7 +405,12 @@ void InstrEmitter::AddOperand(MachineInstrBuilder &MIB, AddRegisterOperand(MIB, Op, IIOpNum, II, VRBaseMap, IsDebug, IsClone, IsCloned); } else if (ConstantSDNode *C = dyn_cast(Op)) { - MIB.addImm(C->getSExtValue()); + // EVM local begin + if (C->getConstantIntValue()->getBitWidth() > 64) + MIB.addCImm(C->getConstantIntValue()); + else + MIB.addImm(C->getSExtValue()); + // EVM local end } else if (ConstantFPSDNode *F = dyn_cast(Op)) { MIB.addFPImm(F->getConstantFPValue()); } else if (RegisterSDNode *R = dyn_cast(Op)) { diff --git a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp index e4ee3fd99f16..78721351107a 100644 --- a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp @@ -525,7 +525,10 @@ FindCallSeqStart(SDNode *N, unsigned &NestLevel, unsigned &MaxNest, } // Otherwise, find the chain and continue climbing. for (const SDValue &Op : N->op_values()) - if (Op.getValueType() == MVT::Other) { + // EVM local begin + if (Op.getValueType() == MVT::Other + && Op.getOpcode() != ISD::BasicBlock) { + // EVM local end N = Op.getNode(); goto found_chain_operand; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index c1761cbe9e3a..7a523144e633 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -12588,7 +12588,17 @@ MaybeAlign SelectionDAG::InferPtrAlign(SDValue Ptr) const { isa(Ptr.getOperand(0))) { // Handle FI+Cst FrameIdx = cast(Ptr.getOperand(0))->getIndex(); - FrameOffset = Ptr.getConstantOperandVal(1); + // EVM local begin + // In case the offset is negative, it overflows 64 bits with EVM's i256. + // Though the architecture guarantees the offset fits 64 bits wide + // signed integer, so it's ok to truncate. + if (Ptr.getOperand(1).getSimpleValueType() == MVT::i256) + FrameOffset = cast(Ptr.getOperand(1)) + ->getAPIntValue() + .trunc(64) + .getZExtValue(); + + // EVM local end } if (FrameIdx != INT_MIN) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp index f2ab88851b78..10656ccebd73 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGAddressAnalysis.cpp @@ -225,6 +225,10 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, // Only consider ORs which act as adds. if (auto *C = dyn_cast(Base->getOperand(1))) if (DAG.MaskedValueIsZero(Base->getOperand(0), C->getAPIntValue())) { + // EVM local begin + if (C->getAPIntValue().getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end Offset += C->getSExtValue(); Base = DAG.getTargetLoweringInfo().unwrapAddress(Base->getOperand(0)); continue; @@ -232,6 +236,10 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, break; case ISD::ADD: if (auto *C = dyn_cast(Base->getOperand(1))) { + // EVM local begin + if (C->getAPIntValue().getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end Offset += C->getSExtValue(); Base = DAG.getTargetLoweringInfo().unwrapAddress(Base->getOperand(0)); continue; @@ -243,6 +251,10 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, unsigned int IndexResNo = (Base->getOpcode() == ISD::LOAD) ? 1 : 0; if (LSBase->isIndexed() && Base.getResNo() == IndexResNo) if (auto *C = dyn_cast(LSBase->getOffset())) { + // EVM local begin + if (C->getAPIntValue().getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end auto Off = C->getSExtValue(); if (LSBase->getAddressingMode() == ISD::PRE_DEC || LSBase->getAddressingMode() == ISD::POST_DEC) @@ -286,6 +298,12 @@ static BaseIndexOffset matchLSNode(const LSBaseSDNode *N, !isa(Index->getOperand(1))) return BaseIndexOffset(PotentialBase, Index, Offset, IsIndexSignExt); + // EVM local begin + if (cast(Index->getOperand(1)) + ->getAPIntValue() + .getSignificantBits() > 64) + return BaseIndexOffset(); + // EVM local end Offset += cast(Index->getOperand(1))->getSExtValue(); Index = Index->getOperand(0); if (Index->getOpcode() == ISD::SIGN_EXTEND) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index a223927de4cd..a58af7e2b73c 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -12343,9 +12343,18 @@ void SelectionDAGBuilder::visitSwitch(const SwitchInst &SI) { return; } + // EVM local begin + // TODO: CPR-688 EVM can build jump tables, though the constants are 4 + // times as expensive as instructions in terms of code size. For hot pieces of + // code it still makes sense. + if (!TM.getTargetTriple().isEVM()) { + // EVM local end SL->findJumpTables(Clusters, &SI, getCurSDLoc(), DefaultMBB, DAG.getPSI(), DAG.getBFI()); SL->findBitTestClusters(Clusters, &SI); + // EVM local begin + } + // EVM local end LLVM_DEBUG({ dbgs() << "Case clusters: "; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index d7260fc4cbf9..3451087f08cd 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -2936,7 +2936,14 @@ CheckInteger(const unsigned char *MatcherTable, unsigned &MatcherIndex, Val = decodeSignRotatedValue(Val); ConstantSDNode *C = dyn_cast(N); - return C && C->getAPIntValue().trySExtValue() == Val; + // EVM local begin + if (C) { + const APInt &CVal = C->getAPIntValue(); + if (CVal == APInt(CVal.getBitWidth(), Val, /*isSigned=*/true)) + return true; + } + return false; + // EVM local end } LLVM_ATTRIBUTE_ALWAYS_INLINE static bool diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 476b6c4957df..e7e40f954621 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -4641,6 +4641,10 @@ SDValue TargetLowering::SimplifySetCC(EVT VT, SDValue N0, SDValue N1, } } + // EVM local begin + // Load narrowing is not profiable for EVM. + if (!DAG.getTarget().getTargetTriple().isEVM()) { + // EVM local end // If the LHS is '(and load, const)', the RHS is 0, the test is for // equality or unsigned, and all 1 bits of the const are in the same // partial word, see if we can shorten the load. @@ -4712,6 +4716,9 @@ SDValue TargetLowering::SimplifySetCC(EVT VT, SDValue N0, SDValue N1, return DAG.getSetCC(dl, VT, And, DAG.getConstant(0LL, dl, newVT), Cond); } } + // EVM local begin + } + // EVM local end // If the LHS is a ZERO_EXTEND, perform the comparison on the input. if (N0.getOpcode() == ISD::ZERO_EXTEND) { @@ -10032,6 +10039,7 @@ SDValue TargetLowering::expandUnalignedStore(StoreSDNode *ST, assert(StoreMemVT.isInteger() && !StoreMemVT.isVector() && "Unaligned store of unknown type."); + // Get the half-size VT EVT NewStoredVT = StoreMemVT.getHalfSizedIntegerVT(*DAG.getContext()); unsigned NumBits = NewStoredVT.getFixedSizeInBits(); diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index 3658e8320a0c..21cc13abb363 100644 --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -1063,8 +1063,12 @@ bool TargetPassConfig::addISelPasses() { PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); addPass(createPreISelIntrinsicLoweringPass()); - addPass(createExpandLargeDivRemPass()); - addPass(createExpandLargeFpConvertPass()); + // EVM local begin + if (!TM->getTargetTriple().isEVM()) { + addPass(createExpandLargeDivRemPass()); + addPass(createExpandLargeFpConvertPass()); + } + // EVM local end addIRPasses(); addCodeGenPrepare(); addPassesToHandleExceptions(); diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp index cf7bbf6b2576..a051b333f40b 100644 --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -672,6 +672,14 @@ LLVMTypeRef LLVMInt64TypeInContext(LLVMContextRef C) { LLVMTypeRef LLVMInt128TypeInContext(LLVMContextRef C) { return (LLVMTypeRef) Type::getInt128Ty(*unwrap(C)); } +// EVM local begin +LLVMTypeRef LLVMInt256TypeInContext(LLVMContextRef C) { + return (LLVMTypeRef) Type::getInt256Ty(*unwrap(C)); +} +LLVMTypeRef LLVMInt512TypeInContext(LLVMContextRef C) { + return (LLVMTypeRef) Type::getInt512Ty(*unwrap(C)); +} +// EVM local end LLVMTypeRef LLVMIntTypeInContext(LLVMContextRef C, unsigned NumBits) { return wrap(IntegerType::get(*unwrap(C), NumBits)); } @@ -694,6 +702,14 @@ LLVMTypeRef LLVMInt64Type(void) { LLVMTypeRef LLVMInt128Type(void) { return LLVMInt128TypeInContext(LLVMGetGlobalContext()); } +// EVM local begin +LLVMTypeRef LLVMInt256Type(void) { + return LLVMInt256TypeInContext(LLVMGetGlobalContext()); +} +LLVMTypeRef LLVMInt512Type(void) { + return LLVMInt512TypeInContext(LLVMGetGlobalContext()); +} +// EVM local end LLVMTypeRef LLVMIntType(unsigned NumBits) { return LLVMIntTypeInContext(LLVMGetGlobalContext(), NumBits); } diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 20871982afb0..5cfa2de5b054 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -1346,7 +1346,13 @@ static void DecodeIITType(unsigned &NextElt, ArrayRef Infos, ArgInfo)); return; } + // EVM local begin + case IIT_I256: { + OutputTable.push_back(IITDescriptor::get(IITDescriptor::Integer, 256)); + return; + } } + // EVM local end llvm_unreachable("unhandled"); } diff --git a/llvm/lib/IR/LLVMContextImpl.cpp b/llvm/lib/IR/LLVMContextImpl.cpp index 0a376179d609..db6d9fe1f207 100644 --- a/llvm/lib/IR/LLVMContextImpl.cpp +++ b/llvm/lib/IR/LLVMContextImpl.cpp @@ -42,7 +42,10 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C) X86_FP80Ty(C, Type::X86_FP80TyID), FP128Ty(C, Type::FP128TyID), PPC_FP128Ty(C, Type::PPC_FP128TyID), X86_MMXTy(C, Type::X86_MMXTyID), X86_AMXTy(C, Type::X86_AMXTyID), Int1Ty(C, 1), Int8Ty(C, 8), - Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128) {} + Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128), + // EVM local begin + Int256Ty(C, 256), Int512Ty(C, 512) {} + // EVM local end LLVMContextImpl::~LLVMContextImpl() { #ifndef NDEBUG diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h index 937a87d68617..c739fd12542a 100644 --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1583,7 +1583,10 @@ class LLVMContextImpl { Type VoidTy, LabelTy, HalfTy, BFloatTy, FloatTy, DoubleTy, MetadataTy, TokenTy; Type X86_FP80Ty, FP128Ty, PPC_FP128Ty, X86_MMXTy, X86_AMXTy; - IntegerType Int1Ty, Int8Ty, Int16Ty, Int32Ty, Int64Ty, Int128Ty; + // EVM local begin + IntegerType Int1Ty, Int8Ty, Int16Ty, Int32Ty, Int64Ty, Int128Ty, Int256Ty, + Int512Ty; + // EVM local end std::unique_ptr TheNoneToken; diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp index 5c61ad9f000b..d367154ebac2 100644 --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -254,6 +254,10 @@ IntegerType *Type::getInt16Ty(LLVMContext &C) { return &C.pImpl->Int16Ty; } IntegerType *Type::getInt32Ty(LLVMContext &C) { return &C.pImpl->Int32Ty; } IntegerType *Type::getInt64Ty(LLVMContext &C) { return &C.pImpl->Int64Ty; } IntegerType *Type::getInt128Ty(LLVMContext &C) { return &C.pImpl->Int128Ty; } +// EVM local begin +IntegerType *Type::getInt256Ty(LLVMContext &C) { return &C.pImpl->Int256Ty; } +IntegerType *Type::getInt512Ty(LLVMContext &C) { return &C.pImpl->Int512Ty; } +// EVM local end IntegerType *Type::getIntNTy(LLVMContext &C, unsigned N) { return IntegerType::get(C, N); @@ -271,6 +275,12 @@ Type *Type::getWasm_FuncrefTy(LLVMContext &C) { return Ty; } +// EVM local begin +PointerType *Type::getInt256PtrTy(LLVMContext &C, unsigned AS) { + return getInt256Ty(C)->getPointerTo(AS); +} +// EVM local end + //===----------------------------------------------------------------------===// // IntegerType Implementation //===----------------------------------------------------------------------===// @@ -287,6 +297,9 @@ IntegerType *IntegerType::get(LLVMContext &C, unsigned NumBits) { case 32: return cast(Type::getInt32Ty(C)); case 64: return cast(Type::getInt64Ty(C)); case 128: return cast(Type::getInt128Ty(C)); + // EVM local begin + case 256: return cast(Type::getInt256Ty(C)); + // EVM local end default: break; } diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 9309d5987dc9..793dd5c4a3bc 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -1334,7 +1334,13 @@ void MCAsmStreamer::emitIntValueInHexWithPadding(uint64_t Value, void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) { + // EVM local begin +#if 0 + // EVM local end assert(Size <= 8 && "Invalid size"); + // EVM local begin +#endif + // EVM local end assert(getCurrentSectionOnly() && "Cannot emit contents before setting section!"); const char *Directive = nullptr; @@ -1346,6 +1352,11 @@ void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, case 8: Directive = MAI->getData64bitsDirective(); break; } + // EVM local begin + if (Size == 32) + Directive = ".cell\t"; + // EVM local end + if (!Directive) { int64_t IntValue; if (!Value->evaluateAsAbsolute(IntValue)) diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index b42a668bce23..3f1af18fda43 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -70,6 +70,8 @@ void MCExpr::print(raw_ostream &OS, const MCAsmInfo *MAI, bool InParens) const { return; } case MCExpr::SymbolRef: { + if (MAI->prependSymbolRefWithAt()) + OS << '@'; const MCSymbolRefExpr &SRE = cast(*this); const MCSymbol &Sym = SRE.getSymbol(); // Parenthesize names that start with $ so that they don't look like diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt new file mode 100644 index 000000000000..6fb6c9144f51 --- /dev/null +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -0,0 +1,80 @@ +add_llvm_component_group(EVM) + +set(LLVM_TARGET_DEFINITIONS EVM.td) + +tablegen(LLVM EVMGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM EVMGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM EVMGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM EVMGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM EVMGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM EVMGenSubtargetInfo.inc -gen-subtarget) + +add_public_tablegen_target(EVMCommonTableGen) + +set(EVM_STDLIB_PATH_IN ${CMAKE_CURRENT_SOURCE_DIR}/evm-stdlib.ll) +set(EVM_STDLIB_PATH_OUT ${CMAKE_CURRENT_BINARY_DIR}/EVMStdLib.inc) + +file( + GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/EVMGenRuntime.cmake" + CONTENT [[ +file(READ ${RT_PATH_IN} RT_CONTENT) +set (RT_CONTENT "R\"(${RT_CONTENT})\"") +file(WRITE ${RT_PATH_OUT} ${RT_CONTENT}) + ]] +) + +add_custom_command( + OUTPUT "${EVM_STDLIB_PATH_OUT}" + COMMAND ${CMAKE_COMMAND} -DRT_PATH_IN=${EVM_STDLIB_PATH_IN} + -DRT_PATH_OUT=${EVM_STDLIB_PATH_OUT} + -P ${CMAKE_CURRENT_BINARY_DIR}/EVMGenRuntime.cmake + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/EVMGenRuntime.cmake + ${EVM_STDLIB_PATH_IN} +) +add_custom_target(EVMStdLib DEPENDS "${EVM_STDLIB_PATH_OUT}") + +add_llvm_target(EVMCodeGen + EVMArgumentMove.cpp + EVMAsmPrinter.cpp + EVMCodegenPrepare.cpp + EVMFrameLowering.cpp + EVMISelDAGToDAG.cpp + EVMISelLowering.cpp + EVMInstrInfo.cpp + EVMLinkRuntime.cpp + EVMLowerIntrinsics.cpp + EVMMCInstLower.cpp + EVMRegisterInfo.cpp + EVMSubtarget.cpp + EVMTargetMachine.cpp + EVMTargetTransformInfo.cpp + + LINK_COMPONENTS + Analysis + AsmPrinter + CodeGen + Core + EVMDesc + EVMInfo + IPO + IRReader + Linker + MC + Scalar + SelectionDAG + Support + Target + TargetParser + TransformUtils + + ADD_TO_COMPONENT + EVM + + DEPENDS + EVMStdLib + intrinsics_gen +) + +add_subdirectory(TargetInfo) +add_subdirectory(MCTargetDesc) diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h new file mode 100644 index 000000000000..a0c1fe33342f --- /dev/null +++ b/llvm/lib/Target/EVM/EVM.h @@ -0,0 +1,60 @@ +//==-------- EVM.h - Top-level interface for EVM representation --*- 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 contains the entry points for global functions defined in +// the LLVM EVM backend. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVM_H +#define LLVM_LIB_TARGET_EVM_EVM_H + +#include "llvm/IR/PassManager.h" +#include "llvm/MC/TargetRegistry.h" + +namespace llvm { +class EVMTargetMachine; +class FunctionPass; +class ModulePass; +class PassRegistry; + +namespace EVMAS { +// EVM address spaces +enum AddressSpaces { + AS_STACK = 0, + AS_HEAP = 1, + AS_CALL_DATA = 2, + AS_RETURN_DATA = 3, + AS_CODE = 4, + AS_STORAGE = 5 +}; +} // namespace EVMAS + +// LLVM IR passes. +ModulePass *createEVMLowerIntrinsicsPass(); +FunctionPass *createEVMCodegenPreparePass(); + +// ISel and immediate followup passes. +FunctionPass *createEVMISelDag(EVMTargetMachine &TM, + CodeGenOptLevel OptLevel); +FunctionPass *createEVMArgumentMove(); +ModulePass *createEVMLinkRuntimePass(); + +// PassRegistry initialization declarations. +void initializeEVMCodegenPreparePass(PassRegistry &); +void initializeEVMLowerIntrinsicsPass(PassRegistry &); +void initializeEVMArgumentMovePass(PassRegistry &); +void initializeEVMLinkRuntimePass(PassRegistry &); + +struct EVMLinkRuntimePass : PassInfoMixin { + EVMLinkRuntimePass() {} + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // namespace llvm +#endif // LLVM_LIB_TARGET_EVM_EVM_H diff --git a/llvm/lib/Target/EVM/EVM.td b/llvm/lib/Target/EVM/EVM.td new file mode 100644 index 000000000000..770b989b8763 --- /dev/null +++ b/llvm/lib/Target/EVM/EVM.td @@ -0,0 +1,54 @@ +//===---- EVM.td - Describe the EVM Target Machine -----*- tablegen -*-----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the top level entry point for the EVM target. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Target-independent interfaces +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "EVMRegisterInfo.td" + + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "EVMInstrInfo.td" + +def EVMInstrInfo : InstrInfo; + + +//===----------------------------------------------------------------------===// +// Assembly Printers +//===----------------------------------------------------------------------===// + +def EVMAsmWriter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; +} + + +def : ProcessorModel<"evm", NoSchedModel, []>; + +//===----------------------------------------------------------------------===// +// Target Declaration +//===----------------------------------------------------------------------===// + +def EVM : Target { + let InstructionSet = EVMInstrInfo; + let AssemblyWriters = [EVMAsmWriter]; +} diff --git a/llvm/lib/Target/EVM/EVMArgumentMove.cpp b/llvm/lib/Target/EVM/EVMArgumentMove.cpp new file mode 100644 index 000000000000..7aac4b7bc93c --- /dev/null +++ b/llvm/lib/Target/EVM/EVMArgumentMove.cpp @@ -0,0 +1,87 @@ +//===---------- EVMArgumentMove.cpp - Argument instruction moving ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file moves ARGUMENT instructions after ScheduleDAG scheduling. +// +// Arguments are really live-in registers, however, since we use virtual +// registers and LLVM doesn't support live-in virtual registers, we're +// currently making do with ARGUMENT instructions which are placed at the top +// of the entry block. The trick is to get them to *stay* at the top of the +// entry block. +// +// The ARGUMENTS physical register keeps these instructions pinned in place +// during liveness-aware CodeGen passes, however one thing which does not +// respect this is the ScheduleDAG scheduler. This pass is therefore run +// immediately after that. +// +// This is all hopefully a temporary solution until we find a better solution +// for describing the live-in nature of arguments. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-argument-move" + +namespace { +class EVMArgumentMove final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + EVMArgumentMove() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "EVM Argument Move"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addPreserved(); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char EVMArgumentMove::ID = 0; +INITIALIZE_PASS(EVMArgumentMove, DEBUG_TYPE, + "Move ARGUMENT instructions for EVM", false, false) + +FunctionPass *llvm::createEVMArgumentMove() { return new EVMArgumentMove(); } + +bool EVMArgumentMove::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Argument Move **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + bool Changed = false; + MachineBasicBlock &EntryMBB = MF.front(); + + // Look for the first NonArg instruction. + const auto InsertPt = + std::find_if_not(EntryMBB.begin(), EntryMBB.end(), [](auto &MI) { + return EVM::ARGUMENT == MI.getOpcode(); + }); + + // Now move any argument instructions later in the block + // to before our first NonArg instruction. + for (MachineInstr &MI : llvm::make_range(InsertPt, EntryMBB.end())) { + if (EVM::ARGUMENT == MI.getOpcode()) { + EntryMBB.insert(InsertPt, MI.removeFromParent()); + Changed = true; + } + } + + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp new file mode 100644 index 000000000000..168dcf273ed7 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -0,0 +1,81 @@ +//===-------- EVMAsmPrinter.cpp - EVM LLVM assembly writer ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to the EVM assembly language. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCInstLower.h" +#include "EVMTargetMachine.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/TargetRegistry.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +namespace { +class EVMAsmPrinter : public AsmPrinter { + // For each register class we need the mapping from a virtual register + // to its number. + // TODO: Once stackification is implemented this should be removed. + using VRegMap = DenseMap; + using VRegRCMap = DenseMap; + VRegRCMap VRegMapping; + +public: + EVMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)) {} + + StringRef getPassName() const override { return "EVM Assembly "; } + + void emitInstruction(const MachineInstr *MI) override; + + void emitFunctionEntryLabel() override; +}; +} // end of anonymous namespace + +void EVMAsmPrinter::emitFunctionEntryLabel() { + AsmPrinter::emitFunctionEntryLabel(); + + VRegMapping.clear(); + + // Go through all virtual registers of the MF to establish the mapping + // between the global virtual register number and the per-class virtual + // register number (though we have only one GPR register class). + // We use the per-class virtual register number in the asm output. + // TODO: This is a temporary solution while EVM-backend outputs virtual + // registers. Once stackification is implemented this should be removed. + const MachineRegisterInfo &MRI = MF->getRegInfo(); + const unsigned NumVRs = MRI.getNumVirtRegs(); + for (unsigned I = 0; I < NumVRs; I++) { + const Register Vr = Register::index2VirtReg(I); + const TargetRegisterClass *RC = MRI.getRegClass(Vr); + DenseMap &VRegMap = VRegMapping[RC]; + const unsigned N = VRegMap.size(); + VRegMap.insert(std::make_pair(Vr, N + 1)); + } +} + +void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { + EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping, + MF->getRegInfo()); + + MCInst TmpInst; + MCInstLowering.Lower(MI, TmpInst); + EmitToStreamer(*OutStreamer, TmpInst); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMAsmPrinter() { + const RegisterAsmPrinter X(getTheEVMTarget()); +} diff --git a/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp b/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp new file mode 100644 index 000000000000..9a0f636f1f5c --- /dev/null +++ b/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp @@ -0,0 +1,127 @@ +//===------ EVMCodegenPrepare.cpp - EVM CodeGen Prepare ---------*- 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 pass replaces general memory transfer intrinsics with +// EVM specific ones, which are custom lowered on ISel. This is required to +// overcome limitations of SelectionDAG::getMemcpy()/getMemmove() that breaks +// an assertion when a memory length is an immediate valued whose bit size is +// more than 64 bits. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/Pass.h" + +#include "EVM.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-codegen-prepare" + +namespace llvm { +FunctionPass *createEVMCodegenPrepare(); + +} // namespace llvm + +namespace { +struct EVMCodegenPrepare : public FunctionPass { +public: + static char ID; + EVMCodegenPrepare() : FunctionPass(ID) { + initializeEVMCodegenPreparePass(*PassRegistry::getPassRegistry()); + } + bool runOnFunction(Function &F) override; + + StringRef getPassName() const override { + return "Final transformations before code generation"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + FunctionPass::getAnalysisUsage(AU); + } + + void processMemTransfer(MemTransferInst *M); +}; +} // namespace + +char EVMCodegenPrepare::ID = 0; + +INITIALIZE_PASS(EVMCodegenPrepare, "evm-codegen-prepare", + "Final transformations before code generation", false, false) + +void EVMCodegenPrepare::processMemTransfer(MemTransferInst *M) { + // See if the source could be modified by this memmove potentially. + LLVM_DEBUG(dbgs() << "EVM codegenprepare: Replace:" << *M + << " with the target instinsic\n"); + unsigned SrcAS = M->getSourceAddressSpace(); + assert(M->getDestAddressSpace() == EVMAS::AS_HEAP); + + // If the length type is not i256, zext it. + Value *Len = M->getLength(); + Type *LenTy = Len->getType(); + Type *Int256Ty = Type::getInt256Ty(M->getContext()); + assert(LenTy->getIntegerBitWidth() <= Int256Ty->getIntegerBitWidth()); + if (LenTy != Int256Ty) { + if (LenTy->getIntegerBitWidth() < Int256Ty->getIntegerBitWidth()) { + IRBuilder<> Builder(M); + // We cannot use here M->setLength(), as it checks that new type of + // 'Length' is the same, so we use a base functionality of Intrinsics. + // It may look a bit hacky, but should be OK. + M->setArgOperand(2, Builder.CreateZExt(Len, Int256Ty)); + } + } + + assert((SrcAS == EVMAS::AS_HEAP && isa(M)) || + ((SrcAS == EVMAS::AS_CALL_DATA || SrcAS == EVMAS::AS_RETURN_DATA || + SrcAS == EVMAS::AS_CODE) && + isa(M) || + isa(M))); + + Intrinsic::ID IntrID = Intrinsic::not_intrinsic; + switch (SrcAS) { + default: + llvm_unreachable("Unexpected source address space of memcpy/memset"); + break; + case EVMAS::AS_HEAP: + IntrID = Intrinsic::evm_memmoveas1as1; + break; + case EVMAS::AS_CALL_DATA: + IntrID = Intrinsic::evm_memcpyas1as2; + break; + case EVMAS::AS_RETURN_DATA: + IntrID = Intrinsic::evm_memcpyas1as3; + break; + case EVMAS::AS_CODE: + IntrID = Intrinsic::evm_memcpyas1as4; + break; + } + M->setCalledFunction(Intrinsic::getDeclaration(M->getModule(), IntrID)); +} + +bool EVMCodegenPrepare::runOnFunction(Function &F) { + bool Changed = false; + for (auto &BB : F) { + for (auto &I : BB) { + if (auto *M = dyn_cast(&I)) { + processMemTransfer(M); + Changed = true; + } + } + } + + return Changed; +} + +FunctionPass *llvm::createEVMCodegenPreparePass() { + return new EVMCodegenPrepare(); +} diff --git a/llvm/lib/Target/EVM/EVMFrameLowering.cpp b/llvm/lib/Target/EVM/EVMFrameLowering.cpp new file mode 100644 index 000000000000..95fda58e365a --- /dev/null +++ b/llvm/lib/Target/EVM/EVMFrameLowering.cpp @@ -0,0 +1,23 @@ +//===-------- EVMFrameLowering.cpp - EVM Frame Information ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the EVM implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "EVMFrameLowering.h" + +using namespace llvm; + +bool EVMFrameLowering::hasFP(const MachineFunction &MF) const { return false; } + +void EVMFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const {} + +void EVMFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const {} diff --git a/llvm/lib/Target/EVM/EVMFrameLowering.h b/llvm/lib/Target/EVM/EVMFrameLowering.h new file mode 100644 index 000000000000..2be3491fd3de --- /dev/null +++ b/llvm/lib/Target/EVM/EVMFrameLowering.h @@ -0,0 +1,35 @@ +//==----- EVMFrameLowering.h - Define frame lowering for EVM --*- C++ -*----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMFRAMELOWERING_H +#define LLVM_LIB_TARGET_EVM_EVMFRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class EVMFrameLowering final : public TargetFrameLowering { +protected: +public: + explicit EVMFrameLowering() + : TargetFrameLowering(TargetFrameLowering::StackGrowsUp, + /*StackAl=*/Align(32), + /*LAO=*/0, + /*TransAl=*/Align(32)) {} + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + bool hasFP(const MachineFunction &MF) const override; +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMFRAMELOWERING_H diff --git a/llvm/lib/Target/EVM/EVMISD.def b/llvm/lib/Target/EVM/EVMISD.def new file mode 100644 index 000000000000..b2e3af72cfe2 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISD.def @@ -0,0 +1,23 @@ +//----------------- EVMISD.def - EVM ISD ---------------------------*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the various EVM ISD node types. +// +//===----------------------------------------------------------------------===// + +// NOTE: NO INCLUDE GUARD DESIRED! + +HANDLE_NODETYPE(FCALL) +HANDLE_NODETYPE(RET) +HANDLE_NODETYPE(ARGUMENT) +HANDLE_NODETYPE(SIGNEXTEND) +HANDLE_NODETYPE(TARGET_ADDR_WRAPPER) +HANDLE_NODETYPE(MEMCPY_CALL_DATA) +HANDLE_NODETYPE(MEMCPY_CODE) +HANDLE_NODETYPE(MEMCPY_HEAP) +HANDLE_NODETYPE(MEMCPY_RETURN_DATA) diff --git a/llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp b/llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp new file mode 100644 index 000000000000..ff138174ff96 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISelDAGToDAG.cpp @@ -0,0 +1,102 @@ +//===-------- EVMISelDAGToDAG.cpp - A dag to dag inst selector for EVM ----===// +// +// 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 defines an instruction selector for the EVM target. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-isel" +#define PASS_NAME "EVM DAG->DAG Pattern Instruction Selection" + +// EVMDAGToDAGISel - EVM specific code to select EVM machine +// instructions for SelectionDAG operations. + +namespace { +class EVMDAGToDAGISel final : public SelectionDAGISel { +public: + EVMDAGToDAGISel(EVMTargetMachine &TM, CodeGenOptLevel OptLevel) + : SelectionDAGISel(TM, OptLevel) {} + +private: + + // Include the pieces autogenerated from the target description. +#include "EVMGenDAGISel.inc" + + // Main method to transform nodes into machine nodes. + void Select(SDNode *N) override; +}; + +class EVMDAGToDAGISelLegacy : public SelectionDAGISelLegacy { +public: + static char ID; + explicit EVMDAGToDAGISelLegacy(EVMTargetMachine &TM, CodeGenOptLevel OptLevel) + : SelectionDAGISelLegacy( + ID, std::make_unique(TM, OptLevel)) {} +}; +} // end anonymous namespace + +char EVMDAGToDAGISelLegacy::ID; + +void EVMDAGToDAGISel::Select(SDNode *Node) { + const SDLoc DL(Node); + + // If we have a custom node, we already have selected! + if (Node->isMachineOpcode()) { + LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); + Node->setNodeId(-1); + return; + } + + switch (Node->getOpcode()) { + case EVMISD::FCALL: { + // FCALL has both variable operands and variable results, but ISel only + // supports one or the other. Split calls into two nodes glued together, one + // for the operands and one for the results. These two nodes will be + // recombined in a custom inserter hook into a single MachineInstr. + SmallVector Ops; + for (size_t I = 1; I < Node->getNumOperands(); ++I) { + SDValue Op = Node->getOperand(I); + if (I == 1 && Op->getOpcode() == EVMISD::TARGET_ADDR_WRAPPER) + Op = Op->getOperand(0); + Ops.push_back(Op); + } + + // Add the chain last + Ops.push_back(Node->getOperand(0)); + MachineSDNode *CallParams = + CurDAG->getMachineNode(EVM::CALL_PARAMS, DL, MVT::Glue, Ops); + + SDValue Link(CallParams, 0); + MachineSDNode *CallResults = + CurDAG->getMachineNode(EVM::CALL_RESULTS, DL, Node->getVTList(), Link); + ReplaceNode(Node, CallResults); + return; + } + + default: + break; + } + + // Select the default instruction + SelectCode(Node); +} + +// createEVMISelDag - This pass converts a legalized DAG into a +// EVM-specific DAG, ready for instruction scheduling. +FunctionPass *llvm::createEVMISelDag(EVMTargetMachine &TM, + CodeGenOptLevel OptLevel) { + return new EVMDAGToDAGISelLegacy(TM, OptLevel); +} diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp new file mode 100644 index 000000000000..80bb2c8c2479 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -0,0 +1,610 @@ +//===-------- EVMISelLowering.cpp - EVM DAG Lowering Implementation ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the EVMTargetLowering class. +// +//===----------------------------------------------------------------------===// + +#include "EVMISelLowering.h" +#include "EVM.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/IntrinsicsEVM.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-lower" + +EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, + const EVMSubtarget &STI) + : TargetLowering(TM), Subtarget(&STI) { + + // Booleans always contain 0 or 1. + setBooleanContents(ZeroOrOneBooleanContent); + + // Set up the register classes. + addRegisterClass(MVT::i256, &EVM::GPRRegClass); + + // Compute derived properties from the register classes + computeRegisterProperties(STI.getRegisterInfo()); + + // Provide all sorts of operation actions + setStackPointerRegisterToSaveRestore(EVM::SP); + + // By default, expand all i256bit operations + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, MVT::i256, Expand); + + // Legal operations + setOperationAction({ISD::ADD, ISD::SUB, ISD::MUL, ISD::AND, ISD::OR, ISD::XOR, + ISD::SHL, ISD::SRL, ISD::SRA, ISD::SDIV, ISD::UDIV, + ISD::UREM, ISD::SREM, ISD::SETCC, ISD::SELECT, + ISD::FrameIndex}, + MVT::i256, Legal); + + for (auto CC : {ISD::SETULT, ISD::SETUGT, ISD::SETLT, ISD::SETGT, ISD::SETGE, + ISD::SETUGE, ISD::SETLE, ISD::SETULE, ISD::SETEQ, ISD::SETNE}) + setCondCodeAction(CC, MVT::i256, Legal); + + // Don't use constant pools. + // TODO: Probably this needs to be relaxed in the future. + setOperationAction(ISD::Constant, MVT::i256, Legal); + + // Sign-extension of a boolean value requires expansion, as we cannot use + // EVM::SIGNEXTEND instruction here. + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + + for (const MVT VT : + {MVT::i1, MVT::i8, MVT::i16, MVT::i32, MVT::i64, MVT::i128}) { + setOperationAction(ISD::MERGE_VALUES, VT, Promote); + setTruncStoreAction(MVT::i256, VT, Custom); + } + + setOperationAction(ISD::UMUL_LOHI, MVT::i256, Custom); + + // Custom lowering of extended loads. + for (const MVT VT : MVT::integer_valuetypes()) { + setLoadExtAction(ISD::SEXTLOAD, MVT::i256, VT, Custom); + setLoadExtAction(ISD::ZEXTLOAD, MVT::i256, VT, Custom); + setLoadExtAction(ISD::EXTLOAD, MVT::i256, VT, Custom); + } + + // Custom lowering operations. + setOperationAction({ISD::GlobalAddress, ISD::LOAD, ISD::STORE}, MVT::i256, + Custom); + + setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); + + setJumpIsExpensive(false); + setMaximumJumpTableSize(0); +} + +const char *EVMTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (static_cast(Opcode)) { + case EVMISD::FIRST_NUMBER: + break; +#define HANDLE_NODETYPE(NODE) \ + case EVMISD::NODE: \ + return "EVMISD::" #NODE; +#include "EVMISD.def" +#undef HANDLE_NODETYPE + } + return nullptr; +} + +//===----------------------------------------------------------------------===// +// EVM Lowering private implementation. +//===----------------------------------------------------------------------===// + +static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *msg) { + MachineFunction &MF = DAG.getMachineFunction(); + DAG.getContext()->diagnose( + DiagnosticInfoUnsupported(MF.getFunction(), msg, DL.getDebugLoc())); +} + +SDValue EVMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { + const SDLoc DL(Op); + switch (Op.getOpcode()) { + default: + llvm_unreachable("Unimplemented operation lowering"); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::LOAD: + return LowerLOAD(Op, DAG); + case ISD::STORE: + return LowerSTORE(Op, DAG); + case ISD::UMUL_LOHI: + return LowerUMUL_LOHI(Op, DAG); + case ISD::INTRINSIC_VOID: + return LowerINTRINSIC_VOID(Op, DAG); + } +} + +SDValue EVMTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + const SDLoc DL(Op); + const auto *GA = cast(Op); + const EVT VT = Op.getValueType(); + assert(GA->getTargetFlags() == 0 && + "Unexpected target flags on generic GlobalAddressSDNode"); + + return DAG.getNode( + EVMISD::TARGET_ADDR_WRAPPER, DL, VT, + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); +} + +SDValue EVMTargetLowering::LowerLOAD(SDValue Op, SelectionDAG &DAG) const { + const SDLoc DL(Op); + auto *Load = cast(Op); + + const SDValue BasePtr = Load->getBasePtr(); + const SDValue Chain = Load->getChain(); + const MachinePointerInfo &PInfo = Load->getPointerInfo(); + + const EVT MemVT = Load->getMemoryVT(); + const unsigned MemVTSize = MemVT.getSizeInBits(); + if (MemVT == MVT::i256) + return {}; + + auto ExtType = Load->getExtensionType(); + + assert(Op->getValueType(0) == MVT::i256 && "Unexpected load type"); + assert(ExtType != ISD::NON_EXTLOAD && "Expected extended LOAD"); + assert(MemVT.isScalarInteger() && "Expected scalar load"); + assert(MemVTSize < 256 && "Expected < 256-bits sized loads"); + + LLVM_DEBUG(errs() << "Special handling of extended LOAD node:\n"; + Op.dump(&DAG)); + + // As the EVM architecture has only 256-bits load, additional handling + // is required to load smaller types. + // In the EVM architecture, values located on stack are right-aligned. Values + // located in memory are left-aligned. + + // A small load is implemented as follows: + // 1. L = load 256 bits starting from the pointer + // 2. Shift the value to the right + // V = V >> (256 - MemVTSize) + // 3. Sign-expand the value for SEXTLOAD + + const SDValue LoadValue = + DAG.getLoad(MVT::i256, DL, Chain, BasePtr, PInfo, Load->getAlign()); + + SDValue Res = DAG.getNode(ISD::SRL, DL, MVT::i256, LoadValue, + DAG.getConstant(256 - MemVTSize, DL, MVT::i256)); + + if (ExtType == ISD::SEXTLOAD) + Res = DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, MVT::i256, Res, + DAG.getValueType(MemVT)); + + return DAG.getMergeValues({Res, LoadValue.getValue(1)}, DL); +} + +SDValue EVMTargetLowering::LowerSTORE(SDValue Op, SelectionDAG &DAG) const { + const SDLoc DL(Op); + auto *Store = cast(Op); + + const SDValue BasePtr = Store->getBasePtr(); + SDValue Chain = Store->getChain(); + const MachinePointerInfo &PInfo = Store->getPointerInfo(); + + const EVT MemVT = Store->getMemoryVT(); + const unsigned MemVTSize = MemVT.getSizeInBits(); + assert(MemVT.isScalarInteger() && "Expected a scalar store"); + if (MemVT == MVT::i256 || MemVT == MVT::i8) + return {}; + + assert(MemVTSize < 256 && "Expected < 256-bits sized stores"); + + LLVM_DEBUG(errs() << "Special handling of STORE node:\n"; Op.dump(&DAG)); + + // As the EVM architecture has only 256-bits stores, additional handling + // is required to store smaller types. + // In the EVM architecture, values located on stack are right-aligned. Values + // located in memory are left-aligned. + // The i8 store is handled a special way, as EVM has MSTORE8 instruction + // for this case. + + // A small store is implemented as follows: + // 1. L = load 256 bits starting from the pointer + // 2. Clear the MSB memory bits that will be overwritten + // L = L << MemVTSize + // L = L >> MemVTSize + // 3. Zero-expand the value being stored + // 4. Shift the value to the left + // V = V << (256 - MemVTSize) + // 5. S = or L, V + // 6. store i256 S + + // Load 256 bits starting from the pointer. + SDValue OrigValue = DAG.getExtLoad( + ISD::NON_EXTLOAD, DL, MVT::i256, Chain, BasePtr, PInfo, MVT::i256, + Store->getAlign(), MachineMemOperand::MOLoad, Store->getAAInfo()); + Chain = OrigValue.getValue(1); + + // Clear LSB bits of the memory word that will be overwritten. + OrigValue = DAG.getNode(ISD::SRL, DL, MVT::i256, OrigValue, + DAG.getConstant(MemVTSize, DL, MVT::i256)); + OrigValue = DAG.getNode(ISD::SHL, DL, MVT::i256, OrigValue, + DAG.getConstant(MemVTSize, DL, MVT::i256)); + + const SDValue ZextValue = + DAG.getZeroExtendInReg(Store->getValue(), DL, MVT::i256); + + const SDValue StoreValue = + DAG.getNode(ISD::SHL, DL, MVT::i256, ZextValue, + DAG.getConstant(256 - MemVTSize, DL, MVT::i256)); + + const SDValue OR = DAG.getNode(ISD::OR, DL, MVT::i256, StoreValue, OrigValue); + + return DAG.getStore(Chain, DL, OR, BasePtr, PInfo); +} + +void EVMTargetLowering::ReplaceNodeResults(SDNode *N, + SmallVectorImpl &Results, + SelectionDAG &DAG) const { + LowerOperationWrapper(N, Results, DAG); +} + +// TODO: CRP-1567. Consider removing UMUL_LOHI primitive. +SDValue EVMTargetLowering::LowerUMUL_LOHI(SDValue Op, SelectionDAG &DAG) const { + EVT VT = Op.getValueType(); + SDLoc DL(Op); + std::array Ops; + + // We'll expand the multiplication by brute force because we have no other + // options. This is a trivially-generalized version of the code from + // Hacker's Delight (itself derived from Knuth's Algorithm M from section + // 4.3.1). + // This is similar to what we have in the ExpandIntRes_MUL() from + // LegalizeIntegerTypes.cpp. + + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + + unsigned Bits = VT.getSizeInBits(); + unsigned HalfBits = Bits >> 1; + SDValue Mask = DAG.getConstant(APInt::getLowBitsSet(Bits, HalfBits), DL, VT); + SDValue LL = DAG.getNode(ISD::AND, DL, VT, LHS, Mask); + SDValue RL = DAG.getNode(ISD::AND, DL, VT, RHS, Mask); + + SDValue T = DAG.getNode(ISD::MUL, DL, VT, LL, RL); + SDValue TL = DAG.getNode(ISD::AND, DL, VT, T, Mask); + + SDValue Shift = DAG.getShiftAmountConstant(HalfBits, VT, DL); + SDValue TH = DAG.getNode(ISD::SRL, DL, VT, T, Shift); + SDValue LH = DAG.getNode(ISD::SRL, DL, VT, LHS, Shift); + SDValue RH = DAG.getNode(ISD::SRL, DL, VT, RHS, Shift); + + SDValue U = + DAG.getNode(ISD::ADD, DL, VT, DAG.getNode(ISD::MUL, DL, VT, LH, RL), TH); + SDValue UL = DAG.getNode(ISD::AND, DL, VT, U, Mask); + SDValue UH = DAG.getNode(ISD::SRL, DL, VT, U, Shift); + + SDValue V = + DAG.getNode(ISD::ADD, DL, VT, DAG.getNode(ISD::MUL, DL, VT, LL, RH), UL); + SDValue VH = DAG.getNode(ISD::SRL, DL, VT, V, Shift); + + Ops[1] = DAG.getNode(ISD::ADD, DL, VT, DAG.getNode(ISD::MUL, DL, VT, LH, RH), + DAG.getNode(ISD::ADD, DL, VT, UH, VH)); + + Ops[0] = DAG.getNode(ISD::ADD, DL, VT, TL, + DAG.getNode(ISD::SHL, DL, VT, V, Shift)); + + return DAG.getMergeValues(Ops, DL); +} + +SDValue EVMTargetLowering::LowerINTRINSIC_VOID(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntNo = + cast( + Op.getOperand(Op.getOperand(0).getValueType() == MVT::Other)) + ->getZExtValue(); + + SDLoc DL(Op); + unsigned MemOpISD = 0; + switch (IntNo) { + default: + return {}; + case Intrinsic::evm_memmoveas1as1: + MemOpISD = EVMISD::MEMCPY_HEAP; + break; + case Intrinsic::evm_memcpyas1as2: + MemOpISD = EVMISD::MEMCPY_CALL_DATA; + break; + case Intrinsic::evm_memcpyas1as3: + MemOpISD = EVMISD::MEMCPY_RETURN_DATA; + break; + case Intrinsic::evm_memcpyas1as4: + MemOpISD = EVMISD::MEMCPY_CODE; + break; + } + return DAG.getNode(MemOpISD, DL, MVT::Other, Op.getOperand(0), + Op.getOperand(2), Op.getOperand(3), Op.getOperand(4)); +} + +//===----------------------------------------------------------------------===// +// Calling Convention implementation. +//===----------------------------------------------------------------------===// + +// Test whether the given calling convention is supported. +static bool callingConvSupported(CallingConv::ID CallConv) { + // TODO: EVM currently doesn't distinguish between different calling + // conventions. + return CallConv == CallingConv::C || CallConv == CallingConv::Fast || + CallConv == CallingConv::Cold; +} + +SDValue EVMTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + if (!callingConvSupported(CallConv)) + fail(DL, DAG, "EVM doesn't support non-C calling conventions"); + if (IsVarArg) + fail(DL, DAG, "VarArg is not supported yet"); + + MachineFunction &MF = DAG.getMachineFunction(); + + // Set up the incoming ARGUMENTS value, which serves to represent the liveness + // of the incoming values before they're represented by virtual registers. + MF.getRegInfo().addLiveIn(EVM::ARGUMENTS); + + for (const ISD::InputArg &In : Ins) { + if (In.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca arguments"); + if (In.Flags.isNest()) + fail(DL, DAG, "EVM hasn't implemented nest arguments"); + if (In.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs arguments"); + if (In.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last arguments"); + if (In.Flags.isByVal()) + fail(DL, DAG, "EVM hasn't implemented by val arguments"); + + // As EVM has no physical registers, we use ARGUMENT instruction to emulate + // live-in registers. + InVals.push_back(In.Used ? DAG.getNode(EVMISD::ARGUMENT, DL, In.VT, + DAG.getTargetConstant(InVals.size(), + DL, MVT::i32)) + : DAG.getUNDEF(In.VT)); + } + + return Chain; +} + +SDValue EVMTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + const SDLoc DL = CLI.DL; + const SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + const MachineFunction &MF = DAG.getMachineFunction(); + auto Layout = MF.getDataLayout(); + + const CallingConv::ID CallConv = CLI.CallConv; + if (!callingConvSupported(CallConv)) + fail(DL, DAG, + "EVM doesn't support language-specific or target-specific " + "calling conventions yet"); + if (CLI.IsPatchPoint) + fail(DL, DAG, "EVM doesn't support patch point yet"); + + // TODO: add support of tail call optimization + CLI.IsTailCall = false; + + // Ignore the fact EVM doesn't support varargs to able to compile + // more target-independent LIT tests. Anyway this assert is still + // present in LowerFormalArguments. + // if (CLI.IsVarArg) + // fail(DL, DAG, "EVM hasn't implemented variable arguments"); + + const SmallVectorImpl &Ins = CLI.Ins; + const SmallVectorImpl &Outs = CLI.Outs; + SmallVectorImpl &OutVals = CLI.OutVals; + + for (const auto &Out : Outs) { + if (Out.Flags.isNest()) + fail(DL, DAG, "EVM hasn't implemented nest arguments"); + if (Out.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca arguments"); + if (Out.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs arguments"); + if (Out.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last arguments"); + if (Out.Flags.isByVal()) + fail(DL, DAG, "EVM hasn't implemented byval arguments"); + } + + if (Callee->getOpcode() == ISD::GlobalAddress) { + auto *GA = cast(Callee); + Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, + getPointerTy(DAG.getDataLayout()), + GA->getOffset()); + Callee = DAG.getNode(EVMISD::TARGET_ADDR_WRAPPER, DL, + getPointerTy(DAG.getDataLayout()), Callee); + } else if (Callee->getOpcode() == ISD::ExternalSymbol) { + auto *ES = cast(Callee); + Callee = + DAG.getTargetExternalSymbol(ES->getSymbol(), Callee.getValueType()); + Callee = DAG.getNode(EVMISD::TARGET_ADDR_WRAPPER, DL, + getPointerTy(DAG.getDataLayout()), Callee); + } + + // Compute the operands for the CALLn node. + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add all fixed arguments. + Ops.append(OutVals.begin(), OutVals.end()); + + SmallVector InTys; + for (const auto &In : Ins) { + assert(!In.Flags.isByVal() && "byval is not valid for return values"); + assert(!In.Flags.isNest() && "nest is not valid for return values"); + if (In.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca return values"); + if (In.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs return values"); + if (In.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last return values"); + // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in + // registers. + InTys.push_back(In.VT); + } + + InTys.push_back(MVT::Other); + const SDVTList InTyList = DAG.getVTList(InTys); + const SDValue Res = DAG.getNode(EVMISD::FCALL, DL, InTyList, Ops); + + for (size_t I = 0; I < Ins.size(); ++I) + InVals.push_back(Res.getValue(I)); + + // Return the chain + return Res.getValue(Ins.size()); +} + +SDValue +EVMTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool /*IsVarArg*/, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + assert((Outs.size() <= 1) && "EVM can only return up to one value"); + if (!callingConvSupported(CallConv)) + fail(DL, DAG, "EVM doesn't support non-C calling conventions"); + + SmallVector RetOps(1, Chain); + RetOps.append(OutVals.begin(), OutVals.end()); + Chain = DAG.getNode(EVMISD::RET, DL, MVT::Other, RetOps); + + // Record the number and types of the return values. + for (const ISD::OutputArg &Out : Outs) { + assert(!Out.Flags.isByVal() && "byval is not valid for return values"); + assert(!Out.Flags.isNest() && "nest is not valid for return values"); + assert(Out.IsFixed && "non-fixed return value is not valid"); + if (Out.Flags.isInAlloca()) + fail(DL, DAG, "EVM hasn't implemented inalloca results"); + if (Out.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "EVM hasn't implemented cons regs results"); + if (Out.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "EVM hasn't implemented cons regs last results"); + } + + return Chain; +} + +bool EVMTargetLowering::CanLowerReturn( + CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, + const SmallVectorImpl &Outs, + LLVMContext & /*Context*/) const { + return true; +} + +static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults, + const DebugLoc &DL, + MachineBasicBlock *BB, + const TargetInstrInfo &TII) { + MachineInstr &CallParams = *CallResults.getPrevNode(); + assert(CallParams.getOpcode() == EVM::CALL_PARAMS); + assert(CallResults.getOpcode() == EVM::CALL_RESULTS); + + MachineFunction &MF = *BB->getParent(); + const MCInstrDesc &MCID = TII.get(EVM::FCALL); + MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); + + for (auto Def : CallResults.defs()) + MIB.add(Def); + + for (auto Use : CallParams.explicit_uses()) + MIB.add(Use); + + BB->insert(CallResults.getIterator(), MIB); + CallParams.eraseFromParent(); + CallResults.eraseFromParent(); + + return BB; +} + +MachineBasicBlock * +EVMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); + const DebugLoc &DL = MI.getDebugLoc(); + + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unexpected instr type to insert"); + case EVM::SELECT: + return emitSelect(MI, BB); + case EVM::CALL_RESULTS: + return LowerCallResults(MI, DL, BB, TII); + } +} + +MachineBasicBlock *EVMTargetLowering::emitSelect(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = BB->getParent()->getSubtarget().getInstrInfo(); + const DebugLoc &DL = MI.getDebugLoc(); + // To "insert" a SELECT instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on and the + // true/false values to select between. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + const MachineFunction::iterator It = ++BB->getIterator(); + + // ThisMBB: + // ... + // TrueVal = ... + // setcc $cond, $2, $1 + // JUMPI SinkMBB, $cond + // fallthrough --> FHMBB + MachineBasicBlock *ThisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *FHMBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *SinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, FHMBB); + F->insert(It, SinkMBB); + + // Transfer the remainder of BB and its successor edges to SinkMBB. + SinkMBB->splice(SinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + SinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(FHMBB); + BB->addSuccessor(SinkMBB); + + BuildMI(BB, DL, TII->get(EVM::JUMPI)) + .addMBB(SinkMBB) + .addReg(MI.getOperand(1).getReg()); + + // FHMBB: + // %FalseValue = ... + // # fallthrough to SinkMBB + BB = FHMBB; + + // Update machine-CFG edges + BB->addSuccessor(SinkMBB); + + // SinkMBB: + // %Result = phi [ %TrueValue, ThisMBB ], [ %FalseValue, FHMBB ] + // ... + BB = SinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII->get(EVM::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(2).getReg()) + .addMBB(ThisMBB) + .addReg(MI.getOperand(3).getReg()) + .addMBB(FHMBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h new file mode 100644 index 000000000000..92d066c0b05e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -0,0 +1,153 @@ +//----------------- EVMISelLowering.h - EVM DAG Lowering Interface -*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that EVM uses to lower LLVM +// code into a selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMISELLOWERING_H +#define LLVM_LIB_TARGET_EVM_EVMISELLOWERING_H + +#include "EVM.h" +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { + +namespace EVMISD { + +enum NodeType : unsigned { + FIRST_NUMBER = ISD::BUILTIN_OP_END, +#define HANDLE_NODETYPE(NODE) NODE, +#include "EVMISD.def" +#undef HANDLE_NODETYPE +}; + +} // namespace EVMISD + +class EVMSubtarget; + +class EVMTargetLowering final : public TargetLowering { +public: + EVMTargetLowering(const TargetMachine &TM, const EVMSubtarget &STI); + + /// getTargetNodeName - This method returns the name of a target specific + /// DAG node. + const char *getTargetNodeName(unsigned Opcode) const override; + + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override { + return MVT::i256; + } + + /// Return true if it is profitable to move this shift by a constant amount + /// through its operand, adjusting any immediate operands as necessary to + /// preserve semantics. This transformation may not be desirable if it + /// disrupts a particularly auspicious target-specific tree (e.g. bitfield + /// extraction in AArch64). By default, it returns true. + /// For EVM this may result in the creation of a big immediate, + /// which is not profitable. + bool isDesirableToCommuteWithShift(const SDNode *N, + CombineLevel Level) const override { + return false; + } + + /// Return true if integer divide is usually cheaper than a sequence of + /// several shifts, adds, and multiplies for this target. + /// The definition of "cheaper" may depend on whether we're optimizing + /// for speed or for size. + bool isIntDivCheap(EVT VT, AttributeList Attr) const override { return true; } + + /// There are two ways to clear extreme bits (either low or high): + /// Mask: x & (-1 << y) (the instcombine canonical form) + /// Shifts: x >> y << y + /// Return true if the variant with 2 variable shifts is preferred. + /// Return false if there is no preference. + bool shouldFoldMaskToVariableShiftPair(SDValue X) const override { + return true; + } + + /// Return true if creating a shift of the type by the given + /// amount is not profitable. + bool shouldAvoidTransformToShift(EVT VT, unsigned Amount) const override { + return true; + } + + /// Return true if it is profitable to fold a pair of shifts into a mask. + /// This is usually true on most targets. But some targets, like Thumb1, + /// have immediate shift instructions, but no immediate "and" instruction; + /// this makes the fold unprofitable. + /// For EVM this may result in the creation of a big immediate, + /// which is not profitable. + bool shouldFoldConstantShiftPairToMask(const SDNode *N, + CombineLevel Level) const override { + return false; + } + + /// Determines the optimal series of memory ops to replace the + /// memset / memcpy. Return true if the number of memory ops is below the + /// threshold (Limit). Note that this is always the case when Limit is ~0. + /// It returns the types of the sequence of memory ops to perform + /// memset / memcpy by reference. + bool + findOptimalMemOpLowering(std::vector &MemOps, unsigned Limit, + const MemOp &Op, unsigned DstAS, unsigned SrcAS, + const AttributeList &FuncAttributes) const override { + // Don't expand memcpy into scalar loads/stores, as it is mapped 1-to-1 to + // the corresponding EVM instruction (MCOPY, CALLDATACOPY, RETURNDATACOPY, + // or CODECOPY). + return false; + } + +private: + const EVMSubtarget *Subtarget; + + void ReplaceNodeResults(SDNode *N, SmallVectorImpl &Results, + SelectionDAG &DAG) const override; + + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerLOAD(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerSTORE(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerUMUL_LOHI(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool IsVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &dl, + SelectionDAG &DAG) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const override; + + MachineBasicBlock *emitSelect(MachineInstr &MI, MachineBasicBlock *BB) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMISELLOWERING_H diff --git a/llvm/lib/Target/EVM/EVMInstrFormats.td b/llvm/lib/Target/EVM/EVMInstrFormats.td new file mode 100644 index 000000000000..43becd9a2513 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrFormats.td @@ -0,0 +1,61 @@ +//=--------- EVMInstrFormats.td - EVM Instr. Formats -*- tablegen -*---------=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the EVM instruction formats in TableGen format.n +// +//===----------------------------------------------------------------------===// + +class ASList lst> { + list List = lst; +} + +// The 'AS' structure is defined in IntrinsicsEVM.td +// For each address space define a list containing the only address space +// to override the AddressSpaces property of the PatFrag. +def AS_stack : ASList<[AS.STACK]>; +def AS_heap : ASList<[AS.HEAP]>; +def AS_call_data : ASList<[AS.CALL_DATA]>; +def AS_return_data : ASList<[AS.RETURN_DATA]>; +def AS_storage : ASList<[AS.STORAGE]>; +def AS_tstorage : ASList<[AS.TSTORAGE]>; + +// EVM general instruction format. +class EVMInst inst, string asmstr, int cost> + : Instruction { + bits<8> Inst = inst; // Instruction encoding. + int GasCost = cost; + let Namespace = "EVM"; + let Pattern = []; + let AsmString = asmstr; +} + +// Normal instructions. Default instantiation of a EVMInst. +class NI pattern, + string asmstr = "", bits<8> inst = 0, int cost = 0> + : EVMInst { + dag OutOperandList = oops; + dag InOperandList = iops; + let Pattern = pattern; + let GasCost = cost; + let Defs = [ARGUMENTS]; +} + +// TODO: When implementing instructions stackification, this class +// will be extendent to define both register and stack-based instructions. +// As of now, all the instructions should be derived from it, which implies +// definition of only register-based ones. +multiclass I pattern_r, + string asmstr_r = "", bits<8> inst = 0, int cost = 0> { + def "" : NI; +} + +class EVMPseudo pattern> + : NI { + let isPseudo = 1; + let isCodeGenOnly = 1; +} diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp new file mode 100644 index 000000000000..2fe98317cb44 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -0,0 +1,204 @@ +//===------------------ EVMInstrInfo.cpp - EVM Instruction Information ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the EVM implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "EVMInstrInfo.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-instr-info" + +#define GET_INSTRINFO_CTOR_DTOR +#include "EVMGenInstrInfo.inc" + +EVMInstrInfo::EVMInstrInfo() + : EVMGenInstrInfo(EVM::ADJCALLSTACKDOWN, EVM::ADJCALLSTACKUP), RI() {} + +void EVMInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + const DebugLoc &DL, MCRegister DestReg, + MCRegister SrcReg, bool KillSrc) const { + // This method is called by post-RA expansion, which expects only + // phys registers to exist. However we expect only virtual here. + assert(Register::isVirtualRegister(DestReg) && + "Unexpected physical register"); + +#ifndef NDEBUG + auto &MRI = MBB.getParent()->getRegInfo(); + assert((MRI.getRegClass(DestReg) == &EVM::GPRRegClass) && + "Unexpected register class"); +#endif // NDEBUG + + BuildMI(MBB, I, DL, get(EVM::COPY_I256), DestReg) + .addReg(SrcReg, KillSrc ? RegState::Kill : 0); +} + +// Branch analysis. +bool EVMInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + LLVM_DEBUG(dbgs() << "Analyzing branches of " << printMBBReference(MBB) + << '\n'); + + // Assume this is a fall-through block and update that information later. + TBB = nullptr; + FBB = nullptr; + Cond.clear(); + + // Iterate backwards and analyze all terminators. + MachineBasicBlock::reverse_iterator I = MBB.rbegin(), E = MBB.rend(); + while (I != E) { + if (I->isUnconditionalBranch()) { + // There should be no other branches after the unconditional branch. + assert(!TBB && !FBB && Cond.empty() && "Unreachable branch found"); + TBB = I->getOperand(0).getMBB(); + + // Clean things up, if we're allowed to. + if (AllowModify) { + // There should be no instructions after the unconditional branch. + assert(I == MBB.rbegin()); + + // Delete the branch itself, if its target is the fall-through block. + if (MBB.isLayoutSuccessor(TBB)) { + LLVM_DEBUG(dbgs() << "Removing fall-through branch: "; I->dump()); + I->eraseFromParent(); + I = MBB.rbegin(); + TBB = nullptr; // Fall-through case. + continue; + } + } + } else if (I->isConditionalBranch()) { + // There can't be several conditional branches in a single block just now. + assert(Cond.empty() && "Several conditional branches?"); + + // Set FBB to the destination of the previously encountered unconditional + // branch (if there was any). + FBB = TBB; + TBB = I->getOperand(0).getMBB(); + + // Put the "use" of the condition into Cond[0]. + const MachineOperand &UseMO = I->getOperand(1); + Cond.push_back(UseMO); + + // reverseBranch needs the instruction which feeds the branch, but only + // supports comparisons. See if we can find one. + for (MachineBasicBlock::reverse_iterator CI = I; CI != E; ++CI) { + // If it is the right comparison, put its result into Cond[1]. + // TODO: This info is required for branch reversing, but this + // is not yet implemented. + if (CI->isCompare()) { + const MachineOperand &DefMO = CI->getOperand(0); + if (DefMO.getReg() == UseMO.getReg()) + Cond.push_back(DefMO); + // Only give it one shot, this should be enough. + break; + } + } + } else if (I->isTerminator()) { + // Return, indirect branch, fall-through, or some other unrecognized + // terminator. Give up. + LLVM_DEBUG(dbgs() << "Unrecognized terminator: "; I->dump()); + return true; + } else if (!I->isDebugValue()) { + // This is an ordinary instruction, meaning there are no terminators left + // to process. Finish the analysis. + break; + } + + ++I; + } + + // Check that there are no unaccounted terminators left. + assert(std::none_of(I, E, [](MachineBasicBlock::reverse_iterator I) { + return I->isTerminator(); + })); + + // If we didn't bail out earlier, the analysis was successful. + return false; +} + +unsigned EVMInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "Code size not handled"); + + LLVM_DEBUG(dbgs() << "Removing branches out of " << printMBBReference(MBB) + << '\n'); + + unsigned Count = 0; + // Only remove branches from the end of the MBB. + for (auto I = MBB.rbegin(); I != MBB.rend() && I->isBranch(); ++Count) { + +#ifndef NDEBUG + if (I->isUnconditionalBranch()) + assert(!Count && "Malformed basic block: unconditional branch is not the " + "last instruction in the block"); +#endif // NDEBUG + + LLVM_DEBUG(dbgs() << "Removing branch: "; I->dump()); + MBB.erase(&MBB.back()); + I = MBB.rbegin(); + } + return Count; +} + +unsigned EVMInstrInfo::insertBranch(MachineBasicBlock &MBB, + MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + const DebugLoc &DL, int *BytesAdded) const { + assert(!BytesAdded && "Code is size not handled"); + + // The number of instructions inserted. + unsigned InstrCount = 0; + + const bool IsUncondBranch = Cond.empty(); + const bool IsCondBranch = + (Cond.size() == 1 && Cond[0].isReg()) || + (Cond.size() == 2 && Cond[0].isReg() && Cond[1].isReg()); + + // Insert a branch to the "true" destination. + assert(TBB && "A branch must have a destination"); + if (IsUncondBranch) + BuildMI(&MBB, DL, get(EVM::JUMP)).addMBB(TBB); + else if (IsCondBranch) + BuildMI(&MBB, DL, get(EVM::JUMPI)).addMBB(TBB).add(Cond[0]); + else + llvm_unreachable("Unexpected branching condition"); + + ++InstrCount; + + // If there is also a "false" destination, insert another branch. + if (FBB) { + assert(!Cond.empty() && "Unconditional branch can't have two destinations"); + BuildMI(&MBB, DL, get(EVM::JUMP)).addMBB(FBB); + ++InstrCount; + } + + return InstrCount; +} + +bool EVMInstrInfo::reverseBranchCondition( + SmallVectorImpl &Cond) const { + // TODO: CPR-1557. Try to add support for branch reversing. The main problem + // is that it may require insertion of additional instructions in the BB. + // For example, + // + // NE $3, $2, $1 + // + // should be transformed into + // + // EQ $3, $2, $1 + // ISZERO $4, $3 + + return true; +} diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h new file mode 100644 index 000000000000..ca559c83ca2e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -0,0 +1,55 @@ +//=---------- EVMInstrInfo.h - EVM Instruction Information -*- C++ -*--------=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the EVM implementation of the +// TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMINSTRINFO_H +#define LLVM_LIB_TARGET_EVM_EVMINSTRINFO_H + +#include "EVMRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "EVMGenInstrInfo.inc" + +namespace llvm { + +class EVMInstrInfo final : public EVMGenInstrInfo { + const EVMRegisterInfo RI; + +public: + explicit EVMInstrInfo(); + + const EVMRegisterInfo &getRegisterInfo() const { return RI; } + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, + bool KillSrc) const override; + + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const override; + + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, int *BytesAdded) const override; + + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMINSTRINFO_H diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td new file mode 100644 index 000000000000..de95ef63d294 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -0,0 +1,740 @@ +//===-------- EVMInstrInfo.td - EVM Instruction defs -------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the EVM instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +include "EVMInstrFormats.td" + +//===----------------------------------------------------------------------===// +// EVM-specific DAG Node Types. +//===----------------------------------------------------------------------===// + +def SDT_EVMCallSeqStart + : SDCallSeqStart<[SDTCisVT<0, i256>, SDTCisVT<1, i256>]>; + +def SDT_EVMCallSeqEnd + : SDCallSeqEnd<[SDTCisVT<0, i256>, SDTCisVT<1, i256>]>; + +def SDT_EVMArgument + : SDTypeProfile<1, 1, [SDTCisVT<1, i256>]>; + +def SDT_EVMRet + : SDTypeProfile<0, -1, []>; + +def SDT_EVMSignextend + : SDTypeProfile<1, 2, [SDTCisSameAs<0, 1>, SDTCisVT<1, i256>]>; + +def SDT_EVMTargetAddrWrapper + : SDTypeProfile<1, 1, [SDTCisPtrTy<0>]>; + +def SDT_EVMMemcpy + : SDTypeProfile<0, 3, [SDTCisPtrTy<0>, SDTCisPtrTy<1>, SDTCisInt<2>]>; + +//===----------------------------------------------------------------------===// +// EVM-specific DAG Nodes. +//===----------------------------------------------------------------------===// + +def EVMargument + : SDNode<"EVMISD::ARGUMENT", SDT_EVMArgument>; + +def EVMcallseq_start + : SDNode<"ISD::CALLSEQ_START", SDT_EVMCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; + +def EVMcallseq_end + : SDNode<"ISD::CALLSEQ_END", SDT_EVMCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; + +def EVMret + : SDNode<"EVMISD::RET", SDT_EVMRet, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def EVMSignextend + : SDNode<"EVMISD::SIGNEXTEND", SDT_EVMSignextend>; + +def EVMTargetAddrWrapper + : SDNode<"EVMISD::TARGET_ADDR_WRAPPER", SDT_EVMTargetAddrWrapper>; + +def EVMMemcpy_call_data + : SDNode<"EVMISD::MEMCPY_CALL_DATA", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +def EVMMemcpy_return_data + : SDNode<"EVMISD::MEMCPY_RETURN_DATA", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +def EVMMemcpy_code + : SDNode<"EVMISD::MEMCPY_CODE", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +def EVMMemcpy_heap + : SDNode<"EVMISD::MEMCPY_HEAP", SDT_EVMMemcpy, + [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; + +//===----------------------------------------------------------------------===// +// EVM Operand Definitions. +//===----------------------------------------------------------------------===// + +let OperandType = "OPERAND_IMMEDIATE" in +def i256imm : Operand; + +def neg_imm128 : Operand, IntImmLeaf; + +def imm128 : Operand, IntImmLeaf; + +def jmptarget : Operand; + +//===----------------------------------------------------------------------===// +// Custom DAG Selection Operations. +//===----------------------------------------------------------------------===// + +def negate_imm : SDNodeXFormgetAPIntValue(); + APInt pos = neg; + pos.negate(); + return CurDAG->getTargetConstant(pos, SDLoc(N), MVT::i256); +}]>; + + +//===----------------------------------------------------------------------===// +// Pattern fragments for memory instructions. +//===----------------------------------------------------------------------===// + +// Load pattern fragments +foreach AS = ["stack", "heap", "call_data", "storage", "tstorage"] in { + let AddressSpaces = !cast("AS_"#AS).List in { + def load_#AS : PatFrag<(ops node:$ptr), (load node:$ptr)> { + let IsLoad = 1; + } + } +} + +// Store patterns for Stack and Memory +foreach AS = ["stack", "heap", "storage", "tstorage"] in { + let AddressSpaces = !cast("AS_"#AS).List in { + def store_#AS : PatFrag<(ops node:$val, node:$ptr), + (store node:$val, node:$ptr)> { + let IsStore = 1; + } + } +} + +let AddressSpaces = AS_heap.List in +def truncstorei8_heap : PatFrag<(ops node:$val, node:$ptr), + (truncstore node:$val, node:$ptr)> { + let IsStore = 1; + let MemoryVT = i8; +} + + +//===----------------------------------------------------------------------===// +// EVM Instructions list. +//===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// Pseudo instructions. +//===----------------------------------------------------------------------===// + +let usesCustomInserter = 1 in +def SELECT + : EVMPseudo<(outs GPR:$dst), (ins GPR:$cond, GPR:$lhs, GPR:$rhs), + [(set GPR:$dst, (select GPR:$cond, GPR:$lhs, GPR:$rhs))]>; + + +//===----------------------------------------------------------------------===// +// Additional instructions. +//===----------------------------------------------------------------------===// + +// Call sequence markers. +let isCodeGenOnly = 1, Defs = [SP], Uses = [SP] in { +def ADJCALLSTACKDOWN + : NI<(outs), (ins i256imm:$amt1, i256imm:$amt2), + [(EVMcallseq_start timm:$amt1, timm:$amt2)], + "#ADJCALLSTACKDOWN $amt1 $amt2">; + +def ADJCALLSTACKUP + : NI<(outs), (ins i256imm:$amt1, i256imm:$amt2), + [(EVMcallseq_end timm:$amt1, timm:$amt2)], + "#ADJCALLSTACKUP $amt1 $amt2">; +} + +let hasSideEffects = 1, Uses = [ARGUMENTS] in +def ARGUMENT + : NI<(outs GPR:$res), (ins i256imm:$argno), + [(set GPR:$res, (EVMargument timm:$argno))], + "ARGUMENT $res, $argno">; + +// This is not real EVM instruction. It should be eliminted while +// stackification. +let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in +defm CONST_I256 + : I<(outs GPR:$res), (ins i256imm:$imm), [(set GPR:$res, imm:$imm)], + "CONST_I256 $res, $imm">; + +// This is not real EVM instruction. It should be eliminted while +// stackification. +let isAsCheapAsAMove = 1 in +defm COPY_I256 + : I<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; + +def : Pat<(i256 (EVMTargetAddrWrapper tglobaladdr:$addr)), + (CONST_I256 tglobaladdr:$addr)>; +def : Pat<(i256 (EVMTargetAddrWrapper texternalsym:$addr)), + (CONST_I256 texternalsym:$addr)>; + +let Uses = [SP], isCall = 1 in { + +// CALL should take both variadic arguments and produce variadic results, but +// this is not possible to model directly. Instead, we select calls to a +// CALL_PARAMS taking variadic arguments linked with a CALL_RESULTS that handles +// producing the call's variadic results. We recombine the two in a custom +// inserter hook after DAG ISel, so passes over MachineInstrs will only ever +// observe CALL nodes with all of the expected variadic uses and defs. +let isPseudo = 1 in +def CALL_PARAMS + : NI<(outs), (ins jmptarget:$callee, variable_ops), [], + "CALL_PARAMS\t$callee">; + +let variadicOpsAreDefs = 1, usesCustomInserter = 1, isPseudo = 1 in +def CALL_RESULTS + : NI<(outs), (ins variable_ops), [], "CALL_RESULTS">; + +// Note that instructions with variable_ops have custom printers in +// EVMInstPrinter.cpp. + +let variadicOpsAreDefs = 1 in +def FCALL + : NI<(outs), (ins jmptarget:$callee, variable_ops), [], + "FCALL\t$callee">; +} // Uses = [SP], isCall = 1 + + +//===----------------------------------------------------------------------===// +// EVM arithmetic instructions. +//===----------------------------------------------------------------------===// + +multiclass BinaryInst inst, int cost> + : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), + [(set GPR:$dst, (node GPR:$lhs, GPR:$rhs))], + opcodeStr#" $dst, $lhs, $rhs", inst, cost>; + +let isCommutable = 1 in { + defm ADD : BinaryInst; + defm MUL : BinaryInst; +} +defm SUB : BinaryInst; +defm DIV : BinaryInst; +defm SDIV : BinaryInst; +defm MOD : BinaryInst; +defm SMOD : BinaryInst; + +defm ADDMOD + : I<(outs GPR:$dst), (ins GPR:$add_op1, GPR:$add_op2, GPR:$denom), + [(set GPR:$dst, + (int_evm_addmod GPR:$add_op1, GPR:$add_op2, GPR:$denom))], + "ADDMOD $dst, $add_op1, $add_op2, $denom", 0x08, 8>; + +defm MULMOD + : I<(outs GPR:$dst), (ins GPR:$mul_op1, GPR:$mul_op2, GPR:$denom), + [(set GPR:$dst, + (int_evm_mulmod GPR:$mul_op1, GPR:$mul_op2, GPR:$denom))], + "MULMOD $dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; + +defm EXP + : I<(outs GPR:$dst), (ins GPR:$base, GPR:$exp), + [(set GPR:$dst, (int_evm_exp GPR:$base, GPR:$exp))], + "EXP $dst, $base, $exp", 0x0A, 10>; + +defm SIGNEXTEND + : I<(outs GPR:$dst), (ins GPR:$size, GPR:$src), + [(set GPR:$dst, (int_evm_signextend GPR:$size, GPR:$src))], + "SIGNEXTEND $dst, $size, $src", 0x0B, 5>; + +// The first operand of SIGNEXTEND is the type size in bytes of +// the value being extendent minus one. +def : Pat<(sext_inreg GPR:$src, i8), (SIGNEXTEND (CONST_I256 0), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i16), (SIGNEXTEND (CONST_I256 1), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i32), (SIGNEXTEND (CONST_I256 3), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i64), (SIGNEXTEND (CONST_I256 7), GPR:$src)>; +def : Pat<(sext_inreg GPR:$src, i128), (SIGNEXTEND (CONST_I256 15), GPR:$src)>; + +// Reverse some operations with negative immediate operands to reduce bit-size +// of the immediate encoding. +// TODO: It needs to be discussed starting which bit-size this transformation +// is deems profitable. +def : Pat<(neg_imm128:$imm), + (SUB (CONST_I256 0), (CONST_I256 (negate_imm imm:$imm)))>; +def : Pat<(add GPR:$res, neg_imm128:$imm), + (SUB GPR:$res, (CONST_I256 (negate_imm imm:$imm)))>; +def : Pat<(sub GPR:$res, neg_imm128:$imm), + (ADD GPR:$res, (CONST_I256 (negate_imm imm:$imm)))>; + +def : Pat<(int_evm_div GPR:$op1, GPR:$op2), (DIV GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_sdiv GPR:$op1, GPR:$op2), (SDIV GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_mod GPR:$op1, GPR:$op2), (MOD GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_smod GPR:$op1, GPR:$op2), (SMOD GPR:$op1, GPR:$op2)>; + + +//===----------------------------------------------------------------------===// +// EVM comparison instructions. +//===----------------------------------------------------------------------===// + +multiclass ComparisonInst inst, int cost> + : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), + [(set GPR:$dst, (setcc GPR:$lhs, GPR:$rhs, cond))], + opcodeStr#" $dst, $lhs, $rhs", inst, cost>; + +let isCompare = 1 in { +defm ULT : ComparisonInst; +defm UGT : ComparisonInst; +defm LT : ComparisonInst; +defm GT : ComparisonInst; +let isCommutable = 1 in +defm EQ : ComparisonInst; +defm ISZERO + : I<(outs GPR:$dst), (ins GPR:$src), + [(set GPR:$dst, (setcc GPR:$src, 0, SETEQ))], + "ISZERO $dst, $src", 0x15, 3>; +} // isCompare = 1 + +// Patterns for comparison operations that have no +// corresponding machine instructions. +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETNE), (ISZERO (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETGE), (ISZERO (LT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETLE), (ISZERO (GT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETULE), + (ISZERO (UGT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETUGE), + (ISZERO (ULT GPR:$rs0, GPR:$rs1))>; + + +//===----------------------------------------------------------------------===// +// EVM bitwise instructions. +//===----------------------------------------------------------------------===// + +let isCommutable = 1 in { + defm AND : BinaryInst; + defm OR : BinaryInst; + defm XOR : BinaryInst; +} +defm NOT + : I<(outs GPR:$dst), (ins GPR:$src), [(set GPR:$dst, (not GPR:$src))], + "NOT $dst, $src", 0x19, 3>; + +let mayLoad = 1 in +defm SHA3 + : I<(outs GPR:$dst), (ins GPR:$offset, GPR:$size), + [(set GPR:$dst, (int_evm_sha3 GPR:$offset, GPR:$size))], + "SHA3 $dst, $offset, $size", 0x20, 30>; + +defm BYTE + : I<(outs GPR:$dst), (ins GPR:$idx, GPR:$val), + [(set GPR:$dst, (int_evm_byte GPR:$idx, GPR:$val))], + "BYTE $dst, $idx, $val", 0x1a, 3>; + + +//===----------------------------------------------------------------------===// +// EVM shift instructions. +//===----------------------------------------------------------------------===// + +defm SHL : BinaryInst; +defm SHR : BinaryInst; +defm SAR : BinaryInst; + +def : Pat<(int_evm_shl GPR:$op1, GPR:$op2), (SHL GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_shr GPR:$op1, GPR:$op2), (SHR GPR:$op1, GPR:$op2)>; +def : Pat<(int_evm_sar GPR:$op1, GPR:$op2), (SAR GPR:$op1, GPR:$op2)>; + + +//===----------------------------------------------------------------------===// +// EVM control instructions. +//===----------------------------------------------------------------------===// + +let isBranch = 1, isTerminator = 1 in { +// The condition operand is a boolean value which EVM represents as i256. +defm JUMPI + : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], + "JUMPI $dst, $cond", 0x56, 10>; + +let isBarrier = 1 in +defm JUMP + : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP $dst", 0x57, 10>; +} // isBranch = 1, isTerminator = 1 + +// This isn't really a control flow instruction, but it should be used to mark +// destination of jump instructions. +defm : I<(outs), (ins), [], "JUMPDEST", 0x57, 10>; + +let isBarrier = 1, isTerminator = 1, isReturn = 1 in +def RET : NI<(outs), (ins variable_ops), [(EVMret)], "RET">; + + +//===----------------------------------------------------------------------===// +// EVM memory/storage instructions. +//===----------------------------------------------------------------------===// + +let mayLoad = 1 in { + defm MLOAD + : I<(outs GPR:$dst), (ins GPR:$offset), + [(set GPR:$dst, (load_heap GPR:$offset))], + "MLOAD $dst, $offset", 0x51, 3>; + + defm SLOAD + : I<(outs GPR:$dst), (ins GPR:$key), + [(set GPR:$dst, (load_storage GPR:$key))], + "SLOAD $dst, $key", 0x54, 100>; + + defm TLOAD + : I<(outs GPR:$dst), (ins GPR:$key), + [(set GPR:$dst, (load_tstorage GPR:$key))], + "TLOAD $dst, $key", 0x5c, 100>; +} + +let mayStore = 1 in { + defm MSTORE + : I<(outs), (ins GPR:$offset, GPR:$val), + [(store_heap GPR:$val, GPR:$offset)], + "MSTORE $offset, $val", 0x52, 3>; + + defm MSTORE8 + : I<(outs), (ins GPR:$offset, GPR:$val), + [(int_evm_mstore8 GPR:$offset, GPR:$val)], + "MSTORE8 $offset, $val", 0x53, 3>; + + defm SSTORE + : I<(outs), (ins GPR:$key, GPR:$val), [(store_storage GPR:$val, GPR:$key)], + "SSTORE $key, $val", 0x55, 100>; + + defm TSTORE + : I<(outs), (ins GPR:$key, GPR:$val), [(store_tstorage GPR:$val, GPR:$key)], + "TSTORE $key, $val", 0x5d, 100>; +} + +let mayStore = 1, mayLoad = 1 in +defm MCOPY + : I<(outs), (ins GPR:$dst, GPR:$src, GPR:$size), [], + "MCOPY $dst, $src, $size", 0x5E, 3>; + +def : Pat<(EVMMemcpy_heap GPR:$dst, GPR:$src, GPR:$size), + (MCOPY GPR:$dst, GPR:$src, GPR:$size)>; + +let hasSideEffects = 1 in { + defm PC : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_pc))], + "PC $dst", 0x58, 2>; + + defm GAS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gas))], + "GAS $dst", 0x5A, 2>; +} + +defm MSIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_msize))], + "MSIZE $dst", 0x59, 2>; + +// The i8 store is handled a speciall way, as EVM has a dedicated instruction +// for this. +def : Pat<(truncstorei8_heap GPR:$val, GPR:$off), (MSTORE8 GPR:$off, GPR:$val)>; + +// Instructions for the stack manipulation. These are not real EVM instructions. +// They are used to model 'alloca' before stackification and should be +// eliminated at that stage. + +def to_tframeindex : SDNodeXFormgetTargetFrameIndex(N->getIndex(), MVT::i256); +}]>; + +def TargetFI: OutPatFrag<(ops node:$fi), (i256 (to_tframeindex $fi))>; + +def add_like: PatFrags<(ops node:$lhs, node:$rhs), + [(add $lhs, $rhs), (or $lhs, $rhs)], [{ + return N->getOpcode() == ISD::ADD || isOrEquivalentToAdd(N); +}]>; + +let mayLoad = 1 in +def STACK_LOAD + : NI<(outs GPR:$dst), (ins GPR:$fi), [], "STACK_LOAD $dst, $fi">; + +let mayStore = 1, hasSideEffects = 1 in + def STACK_STORE + : NI<(outs), (ins GPR:$fi, GPR:$val), [], "STACK_STORE $fi, $val">; + +def : Pat<(i256 frameindex:$fi), (TargetFI $fi)>; +def : Pat<(load_stack frameindex:$fi), (STACK_LOAD (TargetFI $fi))>; +def : Pat<(load_stack (add_like frameindex:$fi, GPR:$off)), + (STACK_LOAD (ADD (TargetFI $fi), GPR:$off))>; +def : Pat<(store_stack GPR:$val, frameindex:$fi), + (STACK_STORE (TargetFI $fi), GPR:$val)>; +def : Pat<(store_stack GPR:$val, (add_like frameindex:$fi, GPR:$off)), + (STACK_STORE (ADD (TargetFI $fi), GPR:$off), GPR:$val)>; + +// The following patterns shouldn't be used for lowering a real code +// generated by FE. We add them only to be able to compile target independent +// LIT tests. +def : Pat<(load_stack GPR:$off), (STACK_LOAD GPR:$off)>; +def : Pat<(store_stack GPR:$val, GPR:$off), (STACK_STORE GPR:$off, GPR:$val)>; +def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, GPR:$val)>; + + +//===----------------------------------------------------------------------===// +// EVM instructions for retrieval values from context. +//===----------------------------------------------------------------------===// + +defm ADDRESS + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_address))], + "ADDRESS $dst", 0x30, 2>; + +defm BALANCE + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_balance GPR:$addr))], + "BALANCE $dst, $addr", 0x31, 100>; + +defm ORIGIN + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], + "ORIGIN $dst", 0x32, 2>; + +defm CALLER + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_caller))], + "CALLER $dst", 0x32, 2>; + +defm CALLVALUE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_callvalue))], + "CALLVALUE $dst", 0x34, 2>; + +defm CALLDATALOAD + : I<(outs GPR:$dst), (ins GPR:$off), + [(set GPR:$dst, (int_evm_calldataload GPR:$off))], + "CALLDATALOAD $dst, $off", 0x35, 2>; + +def : Pat<(load_call_data GPR:$off), (CALLDATALOAD GPR:$off)>; + +defm CALLDATASIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_calldatasize))], + "CALLDATASIZE $dst", 0x36, 2>; + +let mayStore = 1, hasSideEffects = 1 in +defm CALLDATACOPY + : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], + "CALLDATACOPY $dst_off, $src_off, $size", 0x37, 3>; + +def : Pat<(EVMMemcpy_call_data GPR:$dst, GPR:$src, GPR:$size), + (CALLDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; + +defm CODESIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], + "CODESIZE $dst", 0x38, 2>; + +let mayStore = 1, hasSideEffects = 1 in +defm CODECOPY + : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], + "CODECOPY $dst_off, $src_off, $size", 0x39, 3>; + +def : Pat<(EVMMemcpy_code GPR:$dst, GPR:$src, GPR:$size), + (CODECOPY GPR:$dst, GPR:$src, GPR:$size)>; + +defm GASPRICE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], + "GASPRICE $dst", 0x3A, 2>; + +defm EXTCODESIZE + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_extcodesize GPR:$addr))], + "EXTCODESIZE $dst, $addr", 0x3B, 100>; + +let mayStore = 1, hasSideEffects = 1 in +defm EXTCODECOPY + : I<(outs), (ins GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size), + [(int_evm_extcodecopy GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size)], + "EXTCODECOPY $addr, $dst_off, $src_off, $size", 0x3C, 100>; + +defm RETURNDATASIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_returndatasize))], + "RETURNDATASIZE $dst", 0x3D, 2>; + +let mayStore = 1, hasSideEffects = 1 in +defm RETURNDATACOPY + : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], + "RETURNDATACOPY $dst_off, $src_off, $size", 0x3E, 3>; + +def : Pat<(EVMMemcpy_return_data GPR:$dst, GPR:$src, GPR:$size), + (RETURNDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; + +defm EXTCODEHASH + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_extcodehash GPR:$addr))], + "EXTCODEHASH $dst, $addr", 0x3F, 100>; + +defm BLOCKHASH + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_blockhash GPR:$addr))], + "BLOCKHASH $dst, $addr", 0x40, 20>; + +defm COINBASE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], + "COINBASE $dst", 0x41, 2>; + +defm TIMESTAMP + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], + "TIMESTAMP $dst", 0x42, 2>; + +defm NUMBER + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], + "NUMBER $dst", 0x43, 2>; + +defm DIFFICULTY + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], + "DIFFICULTY $dst", 0x44, 2>; + +defm GASLIMIT + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], + "GASLIMIT $dst", 0x45, 2>; + +defm CHAINID + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], + "CHAINID $dst", 0x46, 2>; + +let hasSideEffects = 1 in +defm SELFBALANCE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_selfbalance))], + "SELFBALANCE $dst", 0x47, 5>; + +defm BASEFEE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], + "BASEFEE $dst", 0x48, 2>; + +defm BLOBHASH + : I<(outs GPR:$dst), (ins GPR:$index), + [(set GPR:$dst, (int_evm_blobhash GPR:$index))], + "BLOBHASH $dst, $index", 0x49, 3>; + +defm BLOBBASEFEE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], + "BLOBBASEFEE $dst", 0x4A, 2>; + + +//===----------------------------------------------------------------------===// +// EVM instructions for logging. +//===----------------------------------------------------------------------===// + +let mayLoad = 1, hasSideEffects = 1 in { + defm LOG0 + : I<(outs), (ins GPR:$offset, GPR:$size), + [(int_evm_log0 GPR:$offset, GPR:$size)], + "LOG0 $offset, $size", 0xA0, 375>; + + defm LOG1 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1), + [(int_evm_log1 GPR:$offset, GPR:$size, GPR:$t1)], + "LOG1 $offset, $size, $t1", 0xA1, 750>; + + defm LOG2 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2), + [(int_evm_log2 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2)], + "LOG2 $offset, $size, $t1, $t2", 0xA2, 1125>; + + defm LOG3 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3), + [(int_evm_log3 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3)], + "LOG3 $offset, $size, $t1, $t2, $t3", 0xA3, 1500>; + + defm LOG4 + : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, GPR:$t4), + [(int_evm_log4 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, + GPR:$t4)], + "LOG4 $offset, $size, $t1, $t2, $t3, $t4", 0xA4, 1875>; +} + +//===----------------------------------------------------------------------===// +// EVM system instructions. +//===----------------------------------------------------------------------===// + +let mayLoad = 1, hasSideEffects = 1 in +defm CREATE + : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size), + [(set GPR:$dst, (int_evm_create GPR:$value, GPR:$offset, GPR:$size))], + "CREATE $dst, $value, $offset, $size", 0xF0, 32000>; + +let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { + defm CALL + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_call GPR:$gas, GPR:$addr, GPR:$value, + GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "CALL $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + 0xF1, 100>; + + defm CALLCODE + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_callcode GPR:$gas, GPR:$addr, GPR:$value, + GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "CALLCODE $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + 0xF2, 100>; + + defm DELEGATECALL + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, + GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_delegatecall GPR:$gas, GPR:$addr, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "DELEGATECALL $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + 0xF4, 100>; + + defm STATICCALL + : I<(outs GPR:$dst), (ins GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, + GPR:$ret_off, GPR:$ret_size), + [(set GPR:$dst, (int_evm_staticcall GPR:$gas, GPR:$addr, GPR:$arg_off, + GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], + "STATICCALL $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + 0xFA, 100>; +} + +let mayLoad = 1, hasSideEffects = 1 in +defm CREATE2 + : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size, GPR:$salt), + [(set GPR:$dst, (int_evm_create2 GPR:$value, GPR:$offset, GPR:$size, + GPR:$salt))], + "CREATE2 $dst, $value, $offset, $size, $salt", 0xF5, 32000>; + + +//===----------------------------------------------------------------------===// +// EVM instructions to return with error. +//===----------------------------------------------------------------------===// + +let isTerminator = 1, isBarrier = 1 in { + defm REVERT + : I<(outs), (ins GPR:$offset, GPR:$size), + [(int_evm_revert GPR:$offset, GPR:$size)], + "REVERT $offset, $size", 0xFD, 0>; + + defm RETURN + : I<(outs), (ins GPR:$offset, GPR:$size), + [(int_evm_return GPR:$offset, GPR:$size)], + "RETURN $offset, $size", 0xF3, 0>; +} + +let hasSideEffects = 1 in { + defm SELFDESTRUCT + : I<(outs), (ins GPR:$addr), [(int_evm_selfdestruct GPR:$addr)], + "SELFDESTRUCT $addr", 0xFF, 5000>; + + defm STOP : I<(outs), (ins), [(int_evm_stop)], "STOP", 0x00, 0>; + + defm INVALID : I<(outs), (ins), [(int_evm_invalid)], "INVALID", 0xFE, 0>; +} + + +//===----------------------------------------------------------------------===// +// EVM instructions for stack manipulation. +//===----------------------------------------------------------------------===// +defm POP : I<(outs), (ins GPR:$val), [(int_evm_pop GPR:$val)], + "POP $val", 0x50, 2>; diff --git a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp new file mode 100644 index 000000000000..65c5226da1c2 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp @@ -0,0 +1,113 @@ +//===----- EVMLinkRuntime.cpp - inject runtime library into the module ---===// +// +// 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 pas links stdlib (evm-stdlib.ll) and internalize their contents. +// EVM doesn't have a proper linker and all programs consist of a single module. +// The pass links the the necessary modules into the program module. +// It's called at the beginning of optimization pipeline. The pass links +// the context of evm-stdlib.ll and internalize its content, after that +// global DCE is expected to be run to remove all unused functions. +// +//============================================================================// + +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Linker/Linker.h" +#include "llvm/Pass.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Transforms/IPO/Internalize.h" +#include "llvm/Transforms/Scalar.h" + +#include + +#include "EVM.h" + +#define DEBUG_TYPE "evm-link-runtime" + +using namespace llvm; + +namespace llvm { +ModulePass *createEVMLinkRuntimePass(); +void initializeEVMLinkRuntimePass(PassRegistry &); +} // namespace llvm + +static ExitOnError ExitOnErr; + +namespace { +/// Link std and runtime libraries into the module. +/// At the moment front ends work only with single source programs. +struct EVMLinkRuntime final : public ModulePass { +public: + static char ID; + EVMLinkRuntime() : ModulePass(ID) {} + bool runOnModule(Module &M) override; + + StringRef getPassName() const override { + return "Link runtime library into the module"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + } +}; +} // namespace + +char EVMLinkRuntime::ID = 0; + +INITIALIZE_PASS(EVMLinkRuntime, "evm-link-runtime", + "Link standard and runtime library into the module", false, + false) + +static const char *STDLIB_DATA = +#include "EVMStdLib.inc" + ; + +static bool EVMLinkRuntimeImpl(Module &M, const char *ModuleToLink) { + Linker L(M); + LLVMContext &C = M.getContext(); + unsigned Flags = Linker::Flags::None; + + std::unique_ptr Buffer = + MemoryBuffer::getMemBuffer(ModuleToLink); + SMDiagnostic Err; + std::unique_ptr RTM = parseIR(*Buffer, Err, C); + if (!RTM) { + Err.print("Unable to parse evm-stdlib.ll", errs()); + exit(1); + } + bool LinkErr = false; + LinkErr = L.linkInModule( + std::move(RTM), Flags, [](Module &M, const StringSet<> &GVS) { + internalizeModule(M, [&GVS](const GlobalValue &GV) { + // Keep original symbols as they are + return !GV.hasName() || (GVS.count(GV.getName()) == 0); + }); + }); + if (LinkErr) { + errs() << "Can't link EVM runtime or stdlib \n"; + exit(1); + } + return true; +} + +bool EVMLinkRuntime::runOnModule(Module &M) { + return EVMLinkRuntimeImpl(M, STDLIB_DATA); +} + +ModulePass *llvm::createEVMLinkRuntimePass() { return new EVMLinkRuntime(); } + +PreservedAnalyses EVMLinkRuntimePass::run(Module &M, + ModuleAnalysisManager &AM) { + EVMLinkRuntimeImpl(M, STDLIB_DATA); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp b/llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp new file mode 100644 index 000000000000..c5cbd3f69f97 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMLowerIntrinsics.cpp @@ -0,0 +1,89 @@ +//===----- EVMLowerIntrinsics.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 +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMSubtarget.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/Transforms/Utils/LowerMemIntrinsics.h" + +#define DEBUG_TYPE "evm-lower-intrinsics" + +using namespace llvm; + +namespace { + +class EVMLowerIntrinsics : public ModulePass { +public: + static char ID; + + EVMLowerIntrinsics() : ModulePass(ID) {} + + bool runOnModule(Module &M) override; + bool expandMemIntrinsicUses(Function &F); + StringRef getPassName() const override { return "EVM Lower Intrinsics"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + } +}; +} // namespace + +char EVMLowerIntrinsics::ID = 0; + +INITIALIZE_PASS(EVMLowerIntrinsics, DEBUG_TYPE, "Lower intrinsics", false, + false) + +bool EVMLowerIntrinsics::expandMemIntrinsicUses(Function &F) { + const Intrinsic::ID ID = F.getIntrinsicID(); + bool Changed = false; + + for (auto I = F.user_begin(), E = F.user_end(); I != E;) { + auto *Inst = cast(*I); + ++I; + + if (ID == Intrinsic::memset) { + auto *Memset = cast(Inst); + expandMemSetAsLoop(Memset); + Changed = true; + Memset->eraseFromParent(); + } + } + + return Changed; +} + +bool EVMLowerIntrinsics::runOnModule(Module &M) { + bool Changed = false; + + for (Function &F : M) { + if (!F.isDeclaration()) + continue; + + switch (F.getIntrinsicID()) { + case Intrinsic::memset: + if (expandMemIntrinsicUses(F)) + Changed = true; + break; + + default: + break; + } + } + + return Changed; +} + +ModulePass *llvm::createEVMLowerIntrinsicsPass() { + return new EVMLowerIntrinsics(); +} diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp new file mode 100644 index 000000000000..06b73ed27d78 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -0,0 +1,147 @@ +//===----- EVMMCInstLower.cpp - Convert EVM MachineInstr to an MCInst -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower EVM MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCInstLower.h" +#include "MCTargetDesc/EVMMCExpr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" + +using namespace llvm; + +#define GET_REGINFO_HEADER +#include "EVMGenRegisterInfo.inc" + +MCSymbol * +EVMMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case 0: + break; + } + + return Printer.getSymbol(MO.getGlobal()); +} + +MCSymbol * +EVMMCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case 0: + break; + } + + return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); +} + +MCOperand EVMMCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case 0: + break; + } + + if (MO.getOffset()) + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + return MCOperand::createExpr(Expr); +} + +void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { + OutMI.setOpcode(MI->getOpcode()); + const MCInstrDesc &Desc = MI->getDesc(); + for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) { + const MachineOperand &MO = MI->getOperand(I); + MCOperand MCOp; + switch (MO.getType()) { + default: + MI->print(errs()); + llvm_unreachable("Unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + continue; + + MCOp = MCOperand::createReg(EncodeVReg(MO.getReg())); + break; + case MachineOperand::MO_Immediate: + MCOp = MCOperand::createImm(MO.getImm()); + break; + case MachineOperand::MO_CImmediate: { + const APInt &CImmVal = MO.getCImm()->getValue(); + // Check for the max number of significant bits - 64, otherwise + // the assertion in getZExtValue() is failed. + if (CImmVal.getSignificantBits() <= 64 && CImmVal.isNonNegative()) { + MCOp = MCOperand::createImm(MO.getCImm()->getZExtValue()); + } else { + // To avoid a memory leak, initial size of the SmallString should be + // chosen enough for the entire string. Otherwise, its internal memory + // will be reallocated into the generic heap but not into the Ctx + // arena and thus never deallocated. + auto *Str = new (Ctx) SmallString<80>(); + CImmVal.toStringUnsigned(*Str); + MCOp = MCOperand::createExpr(EVMCImmMCExpr::create(*Str, Ctx)); + } + } break; + case MachineOperand::MO_MachineBasicBlock: + MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx)); + break; + case MachineOperand::MO_GlobalAddress: + MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); + break; + case MachineOperand::MO_ExternalSymbol: + MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); + break; + } + + OutMI.addOperand(MCOp); + } + if (Desc.variadicOpsAreDefs()) + OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs())); +} + +unsigned EVMMCInstLower::EncodeVReg(unsigned Reg) { + if (Register::isVirtualRegister(Reg)) { + const TargetRegisterClass *RC = MRI.getRegClass(Reg); + const VRegRCMap::const_iterator I = VRegMapping.find(RC); + assert(I != VRegMapping.end() && "Bad register class"); + const DenseMap &RegMap = I->second; + + const VRegMap::const_iterator VI = RegMap.find(Reg); + assert(VI != RegMap.end() && "Bad virtual register"); + const unsigned RegNum = VI->second; + unsigned Ret = 0; + if (RC == &EVM::GPRRegClass) + Ret = (1 << 28); + else + report_fatal_error("Unexpected register class"); + + // Insert the vreg number + Ret |= (RegNum & 0x0FFFFFFF); + return Ret; + } + + // Some special-use registers (at least SP) are actually physical registers. + // Encode this as the register class ID of 0 and the real register ID. + return Reg & 0x0FFFFFFF; +} diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.h b/llvm/lib/Target/EVM/EVMMCInstLower.h new file mode 100644 index 000000000000..3f4dbc84da90 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMCInstLower.h @@ -0,0 +1,59 @@ +//===----- EVMMCInstLower.h - Lower MachineInstr to MCInst ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMMCINSTLOWER_H +#define LLVM_LIB_TARGET_EVM_EVMMCINSTLOWER_H + +#include "llvm/ADT/DenseMap.h" + +namespace llvm { +class AsmPrinter; +class MCContext; +class MCInst; +class MCOperand; +class MCSymbol; +class MachineInstr; +class MachineOperand; +class MachineRegisterInfo; +class TargetRegisterClass; + +/// EVMMCInstLower - This class is used to lower an MachineInstr +/// into an MCInst. +class LLVM_LIBRARY_VISIBILITY EVMMCInstLower { + // TODO: Once stackification is implemented this should be removed, + // see the comments in EVMAsmPrinter. + typedef DenseMap VRegMap; + typedef DenseMap VRegRCMap; + + MCContext &Ctx; + AsmPrinter &Printer; + const VRegRCMap &VRegMapping; + const MachineRegisterInfo &MRI; + +public: + EVMMCInstLower(MCContext &Ctx, AsmPrinter &Printer, + const VRegRCMap &VRegMapping, const MachineRegisterInfo &MRI) + : Ctx(Ctx), Printer(Printer), VRegMapping(VRegMapping), MRI(MRI) {} + + void Lower(const MachineInstr *MI, MCInst &OutMI); + +private: + // Encodes the register class in the upper 4 bits along with the register + // number in 28 lower bits. + // Must be kept in sync with EVMInstPrinter::printRegName. + // TODO: this can be removed once stackification is implemented. + unsigned EncodeVReg(unsigned Reg); + + MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMMCINSTLOWER_H diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.cpp b/llvm/lib/Target/EVM/EVMRegisterInfo.cpp new file mode 100644 index 000000000000..80890a08a622 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.cpp @@ -0,0 +1,86 @@ +//===------- EVMRegisterInfo.cpp - EVM Register Information ---*- C++ -*---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the EVM implementation of the +// TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "EVMFrameLowering.h" +#include "EVMInstrInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-reg-info" + +#define GET_REGINFO_TARGET_DESC +#include "EVMGenRegisterInfo.inc" + +EVMRegisterInfo::EVMRegisterInfo() : EVMGenRegisterInfo(0) {} + +const MCPhysReg * +EVMRegisterInfo::getCalleeSavedRegs(const MachineFunction *) const { + static const std::array CalleeSavedRegs = {0}; + return CalleeSavedRegs.data(); +} + +BitVector +EVMRegisterInfo::getReservedRegs(const MachineFunction & /*MF*/) const { + BitVector Reserved(getNumRegs()); + Reserved.set(EVM::SP); + return Reserved; +} + +bool EVMRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + assert(SPAdj == 0); + MachineInstr &MI = *II; + + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const int64_t FrameOffset = MFI.getObjectOffset(FrameIndex); + const auto *TII = MF.getSubtarget().getInstrInfo(); + + assert(FrameOffset >= 0 && "FrameOffset < 0"); + assert(FrameOffset < static_cast(MFI.getStackSize()) && + "FrameOffset overflows stack size"); + assert(MFI.getObjectSize(FrameIndex) != 0 && + "We assume that variable-sized objects have already been lowered, " + "and don't use FrameIndex operands."); + + const Register FrameRegister = getFrameRegister(MF); + Register FIRegOperand = FrameRegister; + if (FrameOffset > 0) { + FIRegOperand = MRI.createVirtualRegister(&EVM::GPRRegClass); + const Register OffsetReg = MRI.createVirtualRegister(&EVM::GPRRegClass); + BuildMI(MBB, MI, II->getDebugLoc(), TII->get(EVM::CONST_I256), OffsetReg) + .addImm(FrameOffset); + BuildMI(MBB, MI, II->getDebugLoc(), TII->get(EVM::ADD), FIRegOperand) + .addReg(FrameRegister) + .addReg(OffsetReg); + } + + MI.getOperand(FIOperandNum).ChangeToRegister(FIRegOperand, /*isDef=*/false); + return true; +} + +Register EVMRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + return EVM::SP; +} + +const TargetRegisterClass * +EVMRegisterInfo::getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const { + return &EVM::GPRRegClass; +} diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.h b/llvm/lib/Target/EVM/EVMRegisterInfo.h new file mode 100644 index 000000000000..5d5bf15ac375 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.h @@ -0,0 +1,44 @@ +//===----- EVMRegisterInfo.h - EVM Register Information Impl -*- C++ -*---====// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the EVM implementation of the EVMRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMREGISTERINFO_H +#define LLVM_LIB_TARGET_EVM_EVMREGISTERINFO_H + +#define GET_REGINFO_HEADER +#include "EVMGenRegisterInfo.inc" + +namespace llvm { + +class MachineFunction; +class RegScavenger; + +class EVMRegisterInfo final : public EVMGenRegisterInfo { +public: + explicit EVMRegisterInfo(); + + // Code Generation virtual methods. + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + BitVector getReservedRegs(const MachineFunction &MF) const override; + bool eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + Register getFrameRegister(const MachineFunction &MF) const override; + + const TargetRegisterClass * + getPointerRegClass(const MachineFunction &MF, + unsigned Kind = 0) const override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMREGISTERINFO_H diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.td b/llvm/lib/Target/EVM/EVMRegisterInfo.td new file mode 100644 index 000000000000..001321a4ce6e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.td @@ -0,0 +1,39 @@ +//===---EVMRegisterInfo.td - Describe the EVM Registers -*- tablegen -*----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the EVM register classes and some nominal +// physical registers. +// +//===----------------------------------------------------------------------===// + +class EVMReg : Register { + let Namespace = "EVM"; +} + +//===----------------------------------------------------------------------===// +// Registers +//===----------------------------------------------------------------------===// + +// Special registers used as the frame and stack pointer. +def SP : EVMReg<"%SP">; + +// The register allocation framework requires register classes have at least +// one register, so we define one for the integer register class since +// we otherwise don't need a physical register in this class. +def GPR256 : EVMReg<"%reg">; + +// The incoming arguments "register". This is an opaque entity which serves to +// order the ARGUMENT instructions that are emulating live-in registers and +// must not be scheduled below other instructions. +def ARGUMENTS : EVMReg<"ARGUMENTS">; + +//===----------------------------------------------------------------------===// +// Register classes +//===----------------------------------------------------------------------===// + +def GPR : RegisterClass<"EVM", [i256], 256, (add GPR256, SP)>; diff --git a/llvm/lib/Target/EVM/EVMSubtarget.cpp b/llvm/lib/Target/EVM/EVMSubtarget.cpp new file mode 100644 index 000000000000..40f3917c6813 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSubtarget.cpp @@ -0,0 +1,27 @@ +//===-------- EVMSubtarget.cpp - EVM Subtarget Information ---*- C++ -*----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the EVM specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "EVMSubtarget.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "EVMGenSubtargetInfo.inc" + +EVMSubtarget::EVMSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM) + : EVMGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS), TLInfo(TM, *this) {} + +bool EVMSubtarget::useAA() const { return true; } diff --git a/llvm/lib/Target/EVM/EVMSubtarget.h b/llvm/lib/Target/EVM/EVMSubtarget.h new file mode 100644 index 000000000000..b161f90ee29f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSubtarget.h @@ -0,0 +1,62 @@ +//===------ EVMSubtarget.h - Define Subtarget for the EVM ----*- 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 TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSUBTARGET_H +#define LLVM_LIB_TARGET_EVM_EVMSUBTARGET_H + +#include "EVMFrameLowering.h" +#include "EVMISelLowering.h" +#include "EVMInstrInfo.h" +#include "EVMRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" + +#define GET_SUBTARGETINFO_ENUM +#define GET_SUBTARGETINFO_HEADER +#include "EVMGenSubtargetInfo.inc" + +namespace llvm { +class StringRef; + +class EVMSubtarget final : public EVMGenSubtargetInfo { +private: + EVMFrameLowering FrameLowering; + EVMInstrInfo InstrInfo; + EVMTargetLowering TLInfo; + +public: + // This constructor initializes the data members to match that + // of the specified triple. + EVMSubtarget(const Triple &TT, const std::string &CPU, const std::string &FS, + const TargetMachine &TM); + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS); + + const TargetFrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const EVMInstrInfo *getInstrInfo() const override { return &InstrInfo; } + + const TargetRegisterInfo *getRegisterInfo() const override { + return &InstrInfo.getRegisterInfo(); + } + const EVMTargetLowering *getTargetLowering() const override { + return &TLInfo; + } + + bool useAA() const override; +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSUBTARGET_H diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp new file mode 100644 index 000000000000..1ae15e8a71c9 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -0,0 +1,148 @@ +//===------ EVMTargetMachine.cpp - Define TargetMachine for EVM -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Top-level implementation for the EVM target. +// +//===----------------------------------------------------------------------===// + +#include "EVMTargetMachine.h" +#include "EVM.h" +#include "EVMTargetTransformInfo.h" +#include "TargetInfo/EVMTargetInfo.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" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Transforms/IPO/GlobalDCE.h" + +using namespace llvm; + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { + // Register the target. + const RegisterTargetMachine X(getTheEVMTarget()); + auto &PR = *PassRegistry::getPassRegistry(); + initializeEVMCodegenPreparePass(PR); + initializeEVMLinkRuntimePass(PR); + initializeEVMLowerIntrinsicsPass(PR); +} + +static std::string computeDataLayout() { + return "E-p:256:256-i256:256:256-S256-a:256:256"; +} + +static Reloc::Model getEffectiveRelocModel(std::optional RM) { + if (!RM) + return Reloc::Static; + return *RM; +} + +EVMTargetMachine::EVMTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + std::optional RM, + std::optional CM, + CodeGenOptLevel OL, bool JIT) + : LLVMTargetMachine(T, computeDataLayout(), TT, CPU, FS, Options, + getEffectiveRelocModel(RM), + getEffectiveCodeModel(CM, CodeModel::Small), OL), + TLOF(std::make_unique()), + Subtarget(TT, std::string(CPU), std::string(FS), *this) { + setRequiresStructuredCFG(true); + initAsmInfo(); +} + +TargetTransformInfo +EVMTargetMachine::getTargetTransformInfo(const Function &F) const { + return TargetTransformInfo(EVMTTIImpl(this, F)); +} + +void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { + PB.registerPipelineStartEPCallback( + [](ModulePassManager &PM, OptimizationLevel Level) { + PM.addPass(EVMLinkRuntimePass()); + PM.addPass(GlobalDCEPass()); + }); +} + +namespace { +/// EVM Code Generator Pass Configuration Options. +class EVMPassConfig final : public TargetPassConfig { +public: + EVMPassConfig(EVMTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + EVMTargetMachine &getEVMTargetMachine() const { + return getTM(); + } + + // EVM target - is a virtual stack machine that requires stackification + // of the instructions instead of allocating registers for their operands. + FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; } + + // No reg alloc + bool addRegAssignAndRewriteFast() override { return false; } + + // No reg alloc + bool addRegAssignAndRewriteOptimized() override { return false; } + + void addCodeGenPrepare() override; + void addIRPasses() override; + bool addGCPasses() override { return false; } + bool addInstSelector() override; + void addPostRegAlloc() override; +}; +} // namespace + +void EVMPassConfig::addIRPasses() { + addPass(createEVMLowerIntrinsicsPass()); + TargetPassConfig::addIRPasses(); +} + +void EVMPassConfig::addCodeGenPrepare() { + addPass(createEVMCodegenPreparePass()); + TargetPassConfig::addCodeGenPrepare(); +} + +bool EVMPassConfig::addInstSelector() { + (void)TargetPassConfig::addInstSelector(); + // Install an instruction selector. + addPass(createEVMISelDag(getEVMTargetMachine(), getOptLevel())); + // Run the argument-move pass immediately after the ScheduleDAG scheduler + // so that we can fix up the ARGUMENT instructions before anything else + // sees them in the wrong place. + addPass(createEVMArgumentMove()); + return false; +} + +void EVMPassConfig::addPostRegAlloc() { + // TODO: The following CodeGen passes don't currently support code containing + // virtual registers. Consider removing their restrictions and re-enabling + // them. These functions all require the NoVRegs property. + disablePass(&MachineLateInstrsCleanupID); + disablePass(&MachineCopyPropagationID); + disablePass(&PostRAMachineSinkingID); + disablePass(&PostRASchedulerID); + disablePass(&FuncletLayoutID); + disablePass(&StackMapLivenessID); + disablePass(&LiveDebugValuesID); + disablePass(&PatchableFunctionID); + disablePass(&ShrinkWrapID); + + // TODO: This pass is disabled in WebAssembly, as it hurts code size because + // it can generate irreducible control flow. Check if this also true for EVM? + disablePass(&MachineBlockPlacementID); + + TargetPassConfig::addPostRegAlloc(); +} + +TargetPassConfig *EVMTargetMachine::createPassConfig(PassManagerBase &PM) { + return new EVMPassConfig(*this, PM); +} diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.h b/llvm/lib/Target/EVM/EVMTargetMachine.h new file mode 100644 index 000000000000..08ecdd9ed7d0 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetMachine.h @@ -0,0 +1,56 @@ +//===---- EVMTargetMachine.h - Define TargetMachine for EVM -*- 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 TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMTARGETMACHINE_H +#define LLVM_LIB_TARGET_EVM_EVMTARGETMACHINE_H + +#include "EVMSubtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +/// EVMTargetMachine +/// +class EVMTargetMachine final : public LLVMTargetMachine { + std::unique_ptr TLOF; + EVMSubtarget Subtarget; + +public: + EVMTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + std::optional RM, + std::optional CM, CodeGenOptLevel OL, + bool JIT); + + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + + const EVMSubtarget *getSubtargetImpl(const Function &F) const override { + return &Subtarget; + } + + TargetTransformInfo getTargetTransformInfo(const Function &F) const override; + + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } + + // For EVM target this should return false to avoid assertion fails in some + // post-RA passes that require NoVreg property. + // For example, PrologEpilogInserter. + bool usesPhysRegsForValues() const override { return false; } + + void registerPassBuilderCallbacks(PassBuilder &PB) override; +}; // EVMTargetMachine. + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMTARGETMACHINE_H diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp b/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp new file mode 100644 index 000000000000..0890e6816eb4 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp @@ -0,0 +1,60 @@ +//===----------- EVMTargetTransformInfo.cpp - EVM-specific TTI -*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the EVM-specific TargetTransformInfo +// implementation. +// +//===----------------------------------------------------------------------===// + +#include "EVMTargetTransformInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "evmtti" + +unsigned EVMTTIImpl::getAssumedAddrSpace(const Value *V) const { + const auto *LD = dyn_cast(V); + if (!LD) + return 0; + + return LD->getPointerAddressSpace(); +} + +InstructionCost EVMTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, + TTI::TargetCostKind CostKind, + unsigned Index, Value *, + Value *) { + InstructionCost Cost = BasicTTIImplBase::getVectorInstrCost( + Opcode, Val, CostKind, Index, nullptr, nullptr); + return Cost + 25 * TargetTransformInfo::TCC_Expensive; +} + +void EVMTTIImpl::getUnrollingPreferences(Loop *L, ScalarEvolution &SE, + TTI::UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE) { + BaseT::getUnrollingPreferences(L, SE, UP, ORE); + + // Only allow unrolling small loops. + UP.Threshold = 4; + UP.MaxIterationsCountToAnalyze = 4; + + // Disable runtime, partial unrolling and unrolling using + // trip count upper bound. + UP.Partial = UP.Runtime = UP.UpperBound = false; + UP.PartialThreshold = 0; + + // Avoid unrolling when optimizing for size. + UP.OptSizeThreshold = 0; + UP.PartialOptSizeThreshold = 0; +} + +bool EVMTTIImpl::isLSRCostLess(const TargetTransformInfo::LSRCost &C1, + const TargetTransformInfo::LSRCost &C2) const { + return C1.NumRegs < C2.NumRegs; +} + +bool EVMTTIImpl::isNumRegsMajorCostOfLSR() const { return true; } diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h new file mode 100644 index 000000000000..1ffe2724a5d9 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h @@ -0,0 +1,123 @@ +//===---------- EVMTargetTransformInfo.h - EVM-specific TTI -*- C++ -*-----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file a TargetTransformInfo::Concept conforming object specific +// to the EVM target machine. +// +// It uses the target's detailed information to provide more precise answers to +// certain TTI queries, while letting the target independent and default TTI +// implementations handle the rest. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMTARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_EVM_EVMTARGETTRANSFORMINFO_H + +#include "EVMTargetMachine.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/IR/Module.h" + +namespace llvm { + +class EVMTTIImpl final : public BasicTTIImplBase { + using BaseT = BasicTTIImplBase; + using TTI = TargetTransformInfo; + friend BaseT; + + const EVMSubtarget *ST; + const EVMTargetLowering *TLI; + + const EVMSubtarget *getST() const { return ST; } + const EVMTargetLowering *getTLI() const { return TLI; } + +public: + enum SyncVMRegisterClass { Vector /* Unsupported */, GPR }; + + EVMTTIImpl(const EVMTargetMachine *TM, const Function &F) + : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), + TLI(ST->getTargetLowering()) {} + + bool allowsMisalignedMemoryAccesses(LLVMContext &Context, unsigned BitWidth, + unsigned AddressSpace = 0, + Align Alignment = Align(1), + unsigned *Fast = nullptr) const { + return true; + } + + unsigned getAssumedAddrSpace(const Value *V) const; + + unsigned getNumberOfRegisters(unsigned ClassID) const { + return ClassID == Vector ? 0 : 1; + } + + TypeSize getRegisterBitWidth(TargetTransformInfo::RegisterKind RK) const { + assert(RK == TargetTransformInfo::RGK_Scalar && + "Vector registers aren't supported"); + return TypeSize::getFixed(256); + } + + unsigned getRegisterClassForType(bool IsVector, Type *Ty = nullptr) const { + if (IsVector) + return Vector; + return GPR; + } + + const char *getRegisterClassName(unsigned ClassID) const { + if (ClassID == GPR) { + return "EVM::GPR"; + } + llvm_unreachable("unknown register class"); + } + + InstructionCost getVectorInstrCost(unsigned Opcode, Type *Val, + TTI::TargetCostKind CostKind, + unsigned Index = -1, Value *Op0 = nullptr, + Value *Op1 = nullptr); + InstructionCost getVectorInstrCost(const Instruction &I, Type *Val, + TTI::TargetCostKind CostKind, + unsigned Index = -1) { + return getVectorInstrCost(I.getOpcode(), Val, CostKind, Index); + } + + Type * + getMemcpyLoopLoweringType(LLVMContext &Context, Value *Length, + unsigned SrcAddrSpace, unsigned DestAddrSpace, + unsigned SrcAlign, unsigned DestAlign, + std::optional AtomicElementSize) const { + return IntegerType::get(Context, 256); + } + + void getMemcpyLoopResidualLoweringType( + SmallVectorImpl &OpsOut, LLVMContext &Context, + unsigned RemainingBytes, unsigned SrcAddrSpace, unsigned DestAddrSpace, + unsigned SrcAlign, unsigned DestAlign, + std::optional AtomicCpySize) const { + assert(RemainingBytes < 32); + OpsOut.push_back(Type::getIntNTy(Context, RemainingBytes * 8)); + } + + // TODO: The value is copied from SyncVM, needs to be checked. + unsigned getInliningThresholdMultiplier() const { return 11; } + + void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, + TTI::UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE); + + /// Return true if LSR cost of C1 is lower than C1. + bool isLSRCostLess(const TargetTransformInfo::LSRCost &C1, + const TargetTransformInfo::LSRCost &C2) const; + + /// Return true if LSR major cost is number of registers. Targets which + /// implement their own isLSRCostLess and unset number of registers as major + /// cost should return false, otherwise return true. + bool isNumRegsMajorCostOfLSR() const; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt new file mode 100644 index 000000000000..8a3356f4e7e2 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,18 @@ +add_llvm_component_library(LLVMEVMDesc + EVMAsmBackend.cpp + EVMELFObjectWriter.cpp + EVMInstPrinter.cpp + EVMMCAsmInfo.cpp + EVMMCCodeEmitter.cpp + EVMMCExpr.cpp + EVMMCTargetDesc.cpp + EVMTargetStreamer.cpp + + LINK_COMPONENTS + MC + EVMInfo + Support + + ADD_TO_COMPONENT + EVM + ) diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp new file mode 100644 index 000000000000..43c485845314 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp @@ -0,0 +1,80 @@ +//===-------- EVMAsmBackend.cpp - EVM Assembler Backend -----*- 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 implements the EVMAsmBackend class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixupKindInfo.h" + +using namespace llvm; + +namespace { +class EVMAsmBackend final : public MCAsmBackend { + uint8_t OSABI; + +public: + EVMAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI) + : MCAsmBackend(llvm::endianness::little), OSABI(OSABI) {} + ~EVMAsmBackend() override {} + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override; + + std::unique_ptr + createObjectTargetWriter() const override { + return createEVMELFObjectWriter(OSABI); + } + + bool fixupNeedsRelaxation(const MCFixup &Fixup, + uint64_t Value) const override { + return false; + } + + bool fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, + const MCFixup &Fixup, bool Resolved, + uint64_t Value, + const MCRelaxableFragment *DF, + const bool WasForced) const override { + return false; + } + + unsigned getNumFixupKinds() const override { return 0; } + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override { + return false; + } + + bool writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const override { + return false; + } +}; + +void EVMAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, + MutableArrayRef Data, uint64_t Value, + bool IsResolved, + const MCSubtargetInfo *STI) const { + return; +} + +} // end anonymous namespace + +MCAsmBackend *llvm::createEVMMCAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options) { + return new EVMAsmBackend(STI, ELF::ELFOSABI_STANDALONE); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp new file mode 100644 index 000000000000..50a696cd1d4e --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp @@ -0,0 +1,43 @@ +//===-------- EVMELFObjectWriter.cpp - EVM ELF Writer ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file handles EVM-specific object emission, converting LLVM's +// internal fixups into the appropriate relocations. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/MC/MCELFObjectWriter.h" + +using namespace llvm; + +namespace { +class EVMELFObjectWriter final : public MCELFObjectTargetWriter { +public: + EVMELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_NONE, + /*HasRelocationAddend*/ true) {} + + ~EVMELFObjectWriter() override {} + +protected: + 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"); + } + } +}; +} // end of anonymous namespace + +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 new file mode 100644 index 000000000000..9f2b26f4fcc3 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h @@ -0,0 +1,31 @@ +//===-------- EVMFixupKinds.h - EVM Specific Fixup Entries ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMFIXUPKINDS_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +#undef EVM + +namespace llvm { +namespace EVM { + +// This table must be in the same order of +// MCFixupKindInfo Infos[EVM::NumTargetFixupKinds] +// in EVMAsmBackend.cpp. +// +enum Fixups { + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; +} // end namespace EVM +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMFIXUPKINDS_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp new file mode 100644 index 000000000000..195c1b5c8481 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp @@ -0,0 +1,88 @@ +//===----- EVMInstPrinter.cpp - Convert EVM MCInst to assembly syntax -----===// +// +// 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 class prints an EVM MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "EVMInstPrinter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "EVMGenAsmWriter.inc" + +void EVMInstPrinter::printRegName(raw_ostream &OS, MCRegister RegNo) const { + // Decode the virtual register. This must be kept in sync with + // EVMMCInstLower::encodeVirtualRegister. + const unsigned RCId = (RegNo >> 28); + switch (RCId) { + default: + report_fatal_error("Bad virtual register encoding"); + case 0: + // This is actually a physical register, so defer to the autogenerated + // register printer. + OS << EVMInstPrinter::getRegisterName(RegNo); + return; + case 1: + OS << "$"; + break; + } + + const unsigned VReg = RegNo & 0x0FFFFFFF; + OS << VReg; +} + +void EVMInstPrinter::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, const MCSubtargetInfo &STI, + raw_ostream &O) { + // Print the instruction (this uses the AsmStrings from the .td files). + printInstruction(MI, Address, O); + + // Print any additional variadic operands. + const MCInstrDesc &Desc = MII.get(MI->getOpcode()); + if (Desc.isVariadic()) { + if ((Desc.getNumOperands() == 0 && MI->getNumOperands() > 0) || + Desc.variadicOpsAreDefs()) + O << " "; + unsigned Start = Desc.getNumOperands(); + unsigned NumVariadicDefs = 0; + if (Desc.variadicOpsAreDefs()) { + // The number of variadic defs is encoded in an immediate by MCInstLower + NumVariadicDefs = MI->getOperand(0).getImm(); + Start = 1; + } + bool NeedsComma = Desc.getNumOperands() > 0 && !Desc.variadicOpsAreDefs(); + for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) { + if (NeedsComma) + O << ", "; + printOperand(MI, I, O, I - Start < NumVariadicDefs); + NeedsComma = true; + } + } + + // Print any added annotation. + printAnnotation(O, Annot); +} + +void EVMInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O, bool IsVariadicDef) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + printRegName(O, Op.getReg()); + } else if (Op.isImm()) { + O << Op.getImm(); + } else { + Op.getExpr()->print(O, &MAI); + } +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h new file mode 100644 index 000000000000..a7f70ed5b5dc --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.h @@ -0,0 +1,40 @@ +//=------ EVMInstPrinter.h - Convert EVM MCInst to assembly syntax -*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This class prints a EVM MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMINSTPRINTER_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMINSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { +class EVMInstPrinter final : public MCInstPrinter { +public: + EVMInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + void printRegName(raw_ostream &OS, MCRegister RegNo) const override; + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &O) override; + + // Used by tblegen code. + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O, + bool IsVariadicDef = false); + + // Autogenerated by tblgen. + std::pair getMnemonic(const MCInst *MI) override; + void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O); + static const char *getRegisterName(MCRegister RegNo); +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMINSTPRINTER_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp new file mode 100644 index 000000000000..cab4152201e8 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp @@ -0,0 +1,31 @@ +//===-------- EVMMCAsmInfo.cpp - EVM asm properties ------*- C++ -*--------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations of the EVMMCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCAsmInfo.h" +#include "llvm/TargetParser/Triple.h" + +using namespace llvm; + +EVMMCAsmInfo::EVMMCAsmInfo(const Triple &TheTriple) { + IsLittleEndian = false; + HasFunctionAlignment = false; + HasDotTypeDotSizeDirective = false; + HasFourStringsDotFile = false; + PrivateGlobalPrefix = "."; + PrivateLabelPrefix = "."; + AlignmentIsInBytes = true; + PrependSymbolRefWithAt = true; + CommentString = ";"; + SupportsDebugInformation = true; +} + +bool EVMMCAsmInfo::shouldOmitSectionDirective(StringRef) const { return true; } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h new file mode 100644 index 000000000000..0a4df279354b --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h @@ -0,0 +1,29 @@ +//===------ EVMMCAsmInfo.h - EVM asm properties --------------*- C++ -*----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the EVMMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCASMINFO_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { +class Triple; + +class EVMMCAsmInfo final : public MCAsmInfoELF { +public: + explicit EVMMCAsmInfo(const Triple &TT); + bool shouldOmitSectionDirective(StringRef) const override; +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCASMINFO_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp new file mode 100644 index 000000000000..3f593b9deb66 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -0,0 +1,49 @@ +//===-- EVMMCCodeEmitter.cpp - Convert EVM code to machine code -*- 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 implements the EVMMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "mccodeemitter" + +namespace llvm { + +class EVMMCCodeEmitter final : public MCCodeEmitter { + // Implementation generated by tablegen. + uint64_t getBinaryCodeForInstr(const MCInst &MI, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + void encodeInstruction(const MCInst &MI, SmallVectorImpl &CB, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + +public: + EVMMCCodeEmitter(MCContext &ctx, MCInstrInfo const &MCII) {} +}; + +void EVMMCCodeEmitter::encodeInstruction(const MCInst &MI, + SmallVectorImpl &CB, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const {} + +MCCodeEmitter *createEVMMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) { + return new EVMMCCodeEmitter(Ctx, MCII); +} + +#include "EVMGenMCCodeEmitter.inc" + +} // end of namespace llvm diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp new file mode 100644 index 000000000000..2931f9b45b26 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp @@ -0,0 +1,27 @@ +//===----- EVMMCExpr.cpp - EVM specific MC expression classes -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Define EVM-specific MC classes. +// +//===----------------------------------------------------------------------===// + +#include "EVMMCExpr.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-mcexpr" + +const EVMCImmMCExpr *EVMCImmMCExpr::create(const StringRef &Data, + MCContext &Ctx) { + return new (Ctx) EVMCImmMCExpr(Data); +} + +void EVMCImmMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + OS << Data; +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h new file mode 100644 index 000000000000..a75ce042ca39 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h @@ -0,0 +1,49 @@ +//===-------- EVMMCExpr.h - EVM specific MC expression classes --*- 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 +// +//===----------------------------------------------------------------------===// +// +// Define EVM specific MC expression classes +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCEXPR_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCEXPR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCExpr.h" + +namespace llvm { + +class EVMCImmMCExpr final : public MCTargetExpr { +private: + StringRef Data; + + explicit EVMCImmMCExpr(const StringRef &Data) : Data(Data) {} + +public: + static const EVMCImmMCExpr *create(const StringRef &Data, MCContext &Ctx); + + /// getOpcode - Get the kind of this expression. + StringRef getString() const { return Data; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAssembler *Asm, + const MCFixup *Fixup) const override { + return false; + } + + void visitUsedExpr(MCStreamer &Streamer) const override{}; + + MCFragment *findAssociatedFragment() const override { return nullptr; } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCEXPR_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp new file mode 100644 index 000000000000..0292734bb47f --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -0,0 +1,115 @@ +//===----- EVMMCTargetDesc.cpp - EVM Target Descriptions -*- C++ -*--------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides EVM specific target descriptions. +// +//===----------------------------------------------------------------------===// +// +#include "EVMMCTargetDesc.h" +#include "EVMInstPrinter.h" +#include "EVMMCAsmInfo.h" +#include "EVMTargetStreamer.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +#define GET_INSTRINFO_MC_DESC +#include "EVMGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "EVMGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "EVMGenRegisterInfo.inc" + +static MCInstrInfo *createEVMMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitEVMMCInstrInfo(X); + return X; +} + +static MCRegisterInfo *createEVMMCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitEVMMCRegisterInfo(X, 0); + return X; +} + +static MCInstPrinter *createEVMMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + if (SyntaxVariant == 0) + return new EVMInstPrinter(MAI, MII, MRI); + return nullptr; +} + +static MCAsmInfo *createEVMMCAsmInfo(const MCRegisterInfo & /*MRI*/, + const Triple &TT, + const MCTargetOptions & /*Options*/) { + return new EVMMCAsmInfo(TT); +} + +static MCSubtargetInfo *createEVMMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + return createEVMMCSubtargetInfoImpl(TT, CPU, /*TuneCPU*/ CPU, FS); +} + +static MCTargetStreamer * +createEVMObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo & /*STI*/) { + return new EVMTargetObjStreamer(S); +} + +static MCTargetStreamer * +createEVMAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream & /*OS*/, + MCInstPrinter * /*InstPrint*/) { + return new EVMTargetAsmStreamer(S); +} + +static MCTargetStreamer *createEVMNullTargetStreamer(MCStreamer &S) { + return new EVMTargetStreamer(S); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTargetMC() { + Target &T = getTheEVMTarget(); + + // Register the MC asm info. + const RegisterMCAsmInfoFn X(T, createEVMMCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(T, createEVMMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(T, createEVMMCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(T, createEVMMCSubtargetInfo); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(T, createEVMMCInstPrinter); + + // Register the MC code emitter. + TargetRegistry::RegisterMCCodeEmitter(T, createEVMMCCodeEmitter); + + // Register the ASM Backend. + TargetRegistry::RegisterMCAsmBackend(T, createEVMMCAsmBackend); + + // Register the object target streamer. + TargetRegistry::RegisterObjectTargetStreamer(T, + createEVMObjectTargetStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(T, createEVMAsmTargetStreamer); + + // Register the null target streamer. + TargetRegistry::RegisterNullTargetStreamer(T, createEVMNullTargetStreamer); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h new file mode 100644 index 000000000000..639ae49357b1 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h @@ -0,0 +1,51 @@ +//===----- EVMMCTargetDesc.h - EVM Target Descriptions ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides EVM specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H + +#include "llvm/Support/DataTypes.h" +#include + +namespace llvm { +class Target; +class MCAsmBackend; +class MCCodeEmitter; +class MCInstrInfo; +class MCSubtargetInfo; +class MCRegisterInfo; +class MCContext; +class MCTargetOptions; +class MCObjectTargetWriter; + +/// Creates a machine code emitter for EVM. +MCCodeEmitter *createEVMMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx); + +MCAsmBackend *createEVMMCAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); + +std::unique_ptr createEVMELFObjectWriter(uint8_t OSABI); + +} // namespace llvm + +// Defines symbolic names for EVM registers. +// This defines a mapping from register name to register number. +#define GET_REGINFO_ENUM +#include "EVMGenRegisterInfo.inc" + +// Defines symbolic names for the EVM instructions. +#define GET_INSTRINFO_ENUM +#define GET_INSTRINFO_MC_HELPER_DECLS +#include "EVMGenInstrInfo.inc" + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp new file mode 100644 index 000000000000..fcbd764a556e --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp @@ -0,0 +1,31 @@ +//===------- EVMTargetStreamer.cpp - EVMTargetStreamer class --*- 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 implements the EVMTargetStreamer class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMTargetStreamer.h" + +using namespace llvm; + +// EVMTargetStreamer implemenations + +EVMTargetStreamer::EVMTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} + +EVMTargetStreamer::~EVMTargetStreamer() = default; + +EVMTargetObjStreamer::EVMTargetObjStreamer(MCStreamer &S) + : EVMTargetStreamer(S) {} + +EVMTargetObjStreamer::~EVMTargetObjStreamer() = default; + +EVMTargetAsmStreamer::EVMTargetAsmStreamer(MCStreamer &S) + : EVMTargetStreamer(S) {} + +EVMTargetAsmStreamer::~EVMTargetAsmStreamer() = default; diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h new file mode 100644 index 000000000000..c13e7aef6503 --- /dev/null +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h @@ -0,0 +1,41 @@ +//===--------- EVMTargetStreamer.h - EVMTargetStreamer class --*- 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 EVMTargetStreamer class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMTARGETSTREAMER_H +#define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMTARGETSTREAMER_H +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +// EVM streamer interface to support EVM assembly directives +class EVMTargetStreamer : public MCTargetStreamer { +public: + EVMTargetStreamer(MCStreamer &S); + ~EVMTargetStreamer() override; +}; + +/// This part is for ASCII assembly output +class EVMTargetAsmStreamer final : public EVMTargetStreamer { +public: + EVMTargetAsmStreamer(MCStreamer &S); + ~EVMTargetAsmStreamer() override; +}; + +// This part is for EVM object output +class EVMTargetObjStreamer final : public EVMTargetStreamer { +public: + EVMTargetObjStreamer(MCStreamer &S); + ~EVMTargetObjStreamer() override; +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMTARGETSTREAMER_H diff --git a/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt b/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt new file mode 100644 index 000000000000..3c293647e05c --- /dev/null +++ b/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt @@ -0,0 +1,10 @@ +add_llvm_component_library(LLVMEVMInfo + EVMTargetInfo.cpp + + LINK_COMPONENTS + MC + Support + + ADD_TO_COMPONENT + EVM + ) diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp new file mode 100644 index 000000000000..aa6518a11b3e --- /dev/null +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp @@ -0,0 +1,26 @@ +//===------ EVMTargetInfo.cpp - EVM Target Implementation ----*- 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 registers the EVM target. +// +//===----------------------------------------------------------------------===// + +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +using namespace llvm; + +Target &llvm::getTheEVMTarget() { + static Target TheEVMTarget; + return TheEVMTarget; +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTargetInfo() { + const RegisterTarget X( + getTheEVMTarget(), "evm", + "Ethereum Virtual Machine [experimental] (256-bit big-endian)", "EVM"); +} diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h new file mode 100644 index 000000000000..fbd317381831 --- /dev/null +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h @@ -0,0 +1,24 @@ +//===-------- EVMTargetInfo.h - EVM Target Implementation -------*- 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 registers the EVM target. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H +#define LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H + +namespace llvm { + +class Target; + +Target &getTheEVMTarget(); + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H diff --git a/llvm/lib/Target/EVM/evm-stdlib.ll b/llvm/lib/Target/EVM/evm-stdlib.ll new file mode 100644 index 000000000000..6f7eca7d3aa5 --- /dev/null +++ b/llvm/lib/Target/EVM/evm-stdlib.ll @@ -0,0 +1,84 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @__addmod(i256 %arg1, i256 %arg2, i256 %modulo) #0 { + %res = call i256 @llvm.evm.addmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) #0 { + %res = call i256 @llvm.evm.mulmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @__signextend(i256 %bytesize, i256 %val) #0 { + %res = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + ret i256 %res +} + +define i256 @__exp(i256 %base, i256 %exp) #0 { + %res = call i256 @llvm.evm.exp(i256 %base, i256 %exp) + ret i256 %res +} + +define i256 @__byte(i256 %index, i256 %val) #0 { + %res = call i256 @llvm.evm.byte(i256 %index, i256 %val) + ret i256 %res +} + +define i256 @__sdiv(i256 %a, i256 %b) #0 { + %res = call i256 @llvm.evm.sdiv(i256 %a, i256 %b) + ret i256 %res +} + +define i256 @__div(i256 %a, i256 %b) #0 { + %res = call i256 @llvm.evm.div(i256 %a, i256 %b) + ret i256 %res +} + +define i256 @__smod(i256 %val, i256 %mod) #0 { + %res = call i256 @llvm.evm.smod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @__mod(i256 %val, i256 %mod) #0 { + %res = call i256 @llvm.evm.mod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @__shl(i256 %shift, i256 %val) #0 { + %res = call i256 @llvm.evm.shl(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @__shr(i256 %shift, i256 %val) #0 { + %res = call i256 @llvm.evm.shr(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @__sar(i256 %shift, i256 %val) #0 { + %res = call i256 @llvm.evm.sar(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @__sha3(ptr addrspace(1) %offset, i256 %len, i1 %unused) #1 { + %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %len) + ret i256 %res +} + +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.signextend(i256, i256) +declare i256 @llvm.evm.exp(i256, i256) +declare i256 @llvm.evm.byte(i256, i256) +declare i256 @llvm.evm.sdiv(i256, i256) +declare i256 @llvm.evm.div(i256, i256) +declare i256 @llvm.evm.mod(i256, i256) +declare i256 @llvm.evm.smod(i256, i256) +declare i256 @llvm.evm.shl(i256, i256) +declare i256 @llvm.evm.shr(i256, i256) +declare i256 @llvm.evm.sar(i256, i256) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) + +attributes #0 = { alwaysinline mustprogress nofree norecurse nosync nounwind readnone willreturn } +attributes #1 = { alwaysinline argmemonly readonly nofree null_pointer_is_valid } diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp index 21d6c74b5956..d5af903167be 100644 --- a/llvm/lib/TargetParser/Triple.cpp +++ b/llvm/lib/TargetParser/Triple.cpp @@ -86,6 +86,9 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case x86_64: return "x86_64"; case xcore: return "xcore"; case xtensa: return "xtensa"; +// EVM local begin + case evm: return "evm"; +// EVM local end } llvm_unreachable("Invalid ArchType!"); @@ -233,6 +236,9 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case dxil: return "dx"; case xtensa: return "xtensa"; + // EVM local begin + case evm: return "evm"; + // EVM local end } } @@ -461,6 +467,9 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("loongarch64", loongarch64) .Case("dxil", dxil) .Case("xtensa", xtensa) + // EVM local begin + .Case("evm", evm) + // EVM local end .Default(UnknownArch); } @@ -609,6 +618,9 @@ static Triple::ArchType parseArch(StringRef ArchName) { "dxilv1.4", "dxilv1.5", "dxilv1.6", "dxilv1.7", "dxilv1.8", Triple::dxil) .Case("xtensa", Triple::xtensa) + // EVM local begin + .Case("evm", Triple::evm) + // EVM local end .Default(Triple::UnknownArch); // Some architectures require special parsing logic just to compute the @@ -945,6 +957,9 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::ve: case Triple::xcore: case Triple::xtensa: + // EVM local begin + case Triple::evm: + // EVM local end return Triple::ELF; case Triple::ppc64: @@ -1663,6 +1678,11 @@ unsigned Triple::getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::wasm64: case llvm::Triple::x86_64: return 64; + + // EVM local begin + case llvm::Triple::evm: + return 256; + // EVM local end } llvm_unreachable("Invalid architecture value"); } @@ -1690,6 +1710,9 @@ Triple Triple::get32BitArchVariant() const { case Triple::msp430: case Triple::systemz: case Triple::ve: + // EVM local begin + case Triple::evm: + // EVM local end T.setArch(UnknownArch); break; @@ -1780,6 +1803,9 @@ Triple Triple::get64BitArchVariant() const { case Triple::tcele: case Triple::xcore: case Triple::xtensa: + // EVM local begin + case Triple::evm: + // EVM local end T.setArch(UnknownArch); break; @@ -1883,6 +1909,9 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::ve: case Triple::csky: case Triple::xtensa: + // EVM local begin + case Triple::evm: + // EVM local end // ARM is intentionally unsupported here, changing the architecture would // drop any arch suffixes. diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 7b419d0f098b..c8865162ae5b 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -1611,7 +1611,7 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, SmallSet &Changed) { AttributeInferer AI; - if (!DisableNoUnwindInference) + if (false && !DisableNoUnwindInference) // Request to infer nounwind attribute for all the functions in the SCC if // every callsite within the SCC is not throwing (except for calls to // functions within the SCC). Note that nounwind attribute suffers from diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp index 0ee1afa76a82..8ead63ab2e99 100644 --- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp +++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp @@ -567,8 +567,13 @@ bool LoopIdiomRecognize::runOnLoopBlock( // Look for a single store or sets of stores with a common base, which can be // optimized into a memset (memset_pattern). The latter most commonly happens // with structs and handunrolled loops. + // EVM local begin + // TODO: Support memset +#if 0 for (auto &SL : StoreRefsForMemset) MadeChange |= processLoopStores(SL.second, BECount, ForMemset::Yes); +#endif + // EVM local end for (auto &SL : StoreRefsForMemsetPattern) MadeChange |= processLoopStores(SL.second, BECount, ForMemset::No); @@ -579,8 +584,13 @@ bool LoopIdiomRecognize::runOnLoopBlock( MadeChange |= processLoopMemIntrinsic( BB, &LoopIdiomRecognize::processLoopMemCpy, BECount); + // EVM local begin + // TODO: Support memset +#if 0 MadeChange |= processLoopMemIntrinsic( BB, &LoopIdiomRecognize::processLoopMemSet, BECount); +#endif + // EVM local end return MadeChange; } diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index dd82832e2b80..9f34a7d84885 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -71,6 +71,9 @@ #include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -7149,9 +7152,13 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) { // switch expression itself can still be restricted as a result of inlining or // CVP. Therefore, only apply this transformation during late stages of the // optimisation pipeline. - if (Options.ConvertSwitchToLookupTable && - SwitchToLookupTable(SI, Builder, DTU, DL, TTI)) - return requestResimplify(); + // EVM local begin + // TODO: CPR-940 Support const arrays. + if (!Triple(BB->getModule()->getTargetTriple()).isEVM()) + if (Options.ConvertSwitchToLookupTable && + SwitchToLookupTable(SI, Builder, DTU, DL, TTI)) + return requestResimplify(); + // EVM local end if (simplifySwitchOfPowersOfTwo(SI, Builder, DL, TTI)) return requestResimplify(); diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp index 89c8c5bf0895..d8319af363a8 100644 --- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -127,6 +127,21 @@ static bool callHasFP128Argument(const CallInst *CI) { }); } +// EVM local begin +// On EVM target, we allow sizes up to 256bits because we allow i256 +// datatype, but it is not always the case that LLVM infra can accept i256 +// values. So here when we are trying to annotate memory location info according +// for intrinsics, we simply just bail out if the size is larger than 64bits +// because the size attributes are assuming an i64 type size information. +static bool isInt64Constant(Value *Size) { + if (ConstantInt *LenC = dyn_cast(Size)) { + if (LenC->getValue().getActiveBits() > 64) + return false; + } + return true; +} +// EVM local end + // Convert the entire string Str representing an integer in Base, up to // the terminating nul if present, to a constant according to the rules // of strtoul[l] or, when AsSigned is set, of strtol[l]. On success @@ -1622,6 +1637,10 @@ Value *LibCallSimplifier::optimizeBCmp(CallInst *CI, IRBuilderBase &B) { Value *LibCallSimplifier::optimizeMemCpy(CallInst *CI, IRBuilderBase &B) { Value *Size = CI->getArgOperand(2); + // EVM local begin + if (!isInt64Constant(Size)) + return nullptr; + // EVM local end annotateNonNullAndDereferenceable(CI, {0, 1}, Size, DL); if (isa(CI)) return nullptr; @@ -1688,6 +1707,10 @@ Value *LibCallSimplifier::optimizeMemPCpy(CallInst *CI, IRBuilderBase &B) { Value *LibCallSimplifier::optimizeMemMove(CallInst *CI, IRBuilderBase &B) { Value *Size = CI->getArgOperand(2); + // EVM local begin + if (!isInt64Constant(Size)) + return nullptr; + // EVM local end annotateNonNullAndDereferenceable(CI, {0, 1}, Size, DL); if (isa(CI)) return nullptr; @@ -1701,6 +1724,10 @@ Value *LibCallSimplifier::optimizeMemMove(CallInst *CI, IRBuilderBase &B) { Value *LibCallSimplifier::optimizeMemSet(CallInst *CI, IRBuilderBase &B) { Value *Size = CI->getArgOperand(2); + // EVM local begin + if (!isInt64Constant(Size)) + return nullptr; + // EVM local end annotateNonNullAndDereferenceable(CI, 0, Size, DL); if (isa(CI)) return nullptr; diff --git a/llvm/test/Analysis/GlobalsModRef/memset-escape.ll b/llvm/test/Analysis/GlobalsModRef/memset-escape.ll index a84987c3bc69..5a899e263ead 100644 --- a/llvm/test/Analysis/GlobalsModRef/memset-escape.ll +++ b/llvm/test/Analysis/GlobalsModRef/memset-escape.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -O1 -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.10.0" diff --git a/llvm/test/CodeGen/EVM/add.ll b/llvm/test/CodeGen/EVM/add.ll new file mode 100644 index 000000000000..4af8c0c52f05 --- /dev/null +++ b/llvm/test/CodeGen/EVM/add.ll @@ -0,0 +1,34 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @addrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @addrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = add i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @addrri(i256 %rs1) nounwind { +; CHECK-LABEL: @addrri +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C1:\$[0-9]+]], 18446744073709551616 +; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[C1]] + + %res = add i256 %rs1, 18446744073709551616 ; 65-bits + ret i256 %res +} + +define i256 @subrri(i256 %rs1) nounwind { +; CHECK-LABEL: @subrri +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C1:\$[0-9]+]], 1 +; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[C1]] + + %res = sub i256 %rs1, -1 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/aext.ll b/llvm/test/CodeGen/EVM/aext.ll new file mode 100644 index 000000000000..9dbb26ffa408 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aext.ll @@ -0,0 +1,14 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i8 @aexti8(i8 %rs1) nounwind { +; CHECK-LABEL: @aexti8 +; CHECK: ARGUMENT [[IN:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C:\$[0-9]+]], 1 +; CHECK: ADD {{.*}}, [[IN]], [[C]] + + %res = add i8 %rs1, 1 + ret i8 %res +} diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll new file mode 100644 index 000000000000..d141fb6901a0 --- /dev/null +++ b/llvm/test/CodeGen/EVM/br.ll @@ -0,0 +1,50 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: diamond + + %cmp = icmp eq i256 %rs1, %rs2 +; CHECK: EQ +; CHECK: ISZERO +; CHECK: JUMPI + br i1 %cmp, label %true_bb, label %false_bb + +true_bb: + %mul = mul i256 %rs1, %rs1 + br label %end_bb + +false_bb: + %add = add i256 %rs1, %rs2 + br label %end_bb + +end_bb: + %res = phi i256 [%mul, %true_bb], [%add, %false_bb] + ret i256 %res +} + + +define i256 @loop(i256 %p1) nounwind { +; CHECK-LABEL: loop +entry: + br label %loop.cond + +loop.cond: + %i = phi i256 [0, %entry], [%i.next, %loop.body] + %res = phi i256 [0, %entry], [%res.next, %loop.body] +; CHECK: EQ +; CHECK: JUMPI + %cond = icmp ne i256 %i, %p1 + br i1 %cond, label %loop.body, label %loop.exit + +loop.body: + %i.next = add i256 %i, 1 + %res.next = add i256 %res, %i +; CHECK: JUMP + br label %loop.cond + +loop.exit: + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/call.ll b/llvm/test/CodeGen/EVM/call.ll new file mode 100644 index 000000000000..7cdcaaa419a6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/call.ll @@ -0,0 +1,51 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @foo(i256) +declare void @foo2(i256) + +define i256 @call(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: @call +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ADD [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK: FCALL 1 [[RES1:\$[0-9]+]], @foo, [[TMP1]] + + %sum = add i256 %a, %b + %res = call i256 @foo(i256 %sum) + ret i256 %res +} + +define void @call2(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: @call2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ADD [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK: FCALL 0 @foo2, [[TMP1]] + + %sum = add i256 %a, %b + call void @foo2(i256 %sum) + ret void +} + +define void @call3_indir(void (i256)* %callee) nounwind { +; CHECK-LABEL: @call3_indir +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C1:\$[0-9]+]], 10 +; CHECK: FCALL 0 [[IN1]], [[C1]] + + call void %callee(i256 10) + ret void +} + +define i256 @call4_indir(i256 (i256)* %callee) nounwind { +; CHECK-LABEL: @call4_indir +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C1:\$[0-9]+]], 10 +; CHECK: FCALL 1 [[RES1:\$[0-9]+]], [[IN1]], [[C1]] + + %res = call i256 %callee(i256 10) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/div.ll b/llvm/test/CodeGen/EVM/div.ll new file mode 100644 index 000000000000..cccbdac7ff77 --- /dev/null +++ b/llvm/test/CodeGen/EVM/div.ll @@ -0,0 +1,32 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @udivrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @udivrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: DIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = udiv i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @sdivrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @sdivrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SDIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = sdiv i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @sdivrri(i256 %rs1) nounwind { +; CHECK-LABEL: @sdivrri +; CHECK: CONST_I256 [[TMP:\$[0-9]+]], 0 + + %res = sdiv i256 %rs1, 0 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/frameidx.ll b/llvm/test/CodeGen/EVM/frameidx.ll new file mode 100644 index 000000000000..e6bec71dc3c9 --- /dev/null +++ b/llvm/test/CodeGen/EVM/frameidx.ll @@ -0,0 +1,60 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @alloca() nounwind { +; CHECK-LABEL: alloca: +; CHECK: STACK_LOAD [[RES:\$[0-9]+]], %SP + + %var = alloca i256, align 1 + %rv = load i256, ptr %var + ret i256 %rv +} + +define i256 @alloca2() nounwind { +; CHECK-LABEL: alloca2: +; CHECK: CONST_I256 [[FILL:\$[0-9]+]], 4096 +; CHECK: ADD [[PTR:\$[0-9]+]], %SP, [[FILL]] +; CHECK: STACK_LOAD [[RES:\$[0-9]+]], [[PTR]] + + %fill = alloca i256, i32 128 + %var = alloca i256, align 1 + %rv = load i256, ptr %var + ret i256 %rv +} + +define void @alloca3(i256 %val) nounwind { +; CHECK-LABEL: alloca3: +; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 +; CHECK: STACK_STORE %SP, [[VAL]] + + %fill = alloca i256, align 1 + store i256 %val, ptr %fill + ret void +} + +define i256 @alloca4() nounwind { +; CHECK-LABEL: alloca4: +; CHECK: CONST_I256 [[OFF:\$[0-9]+]], 64 +; CHECK: ADD [[PTR:\$[0-9]+]], %SP, [[OFF]] +; CHECK: STACK_LOAD [[RES:\$[0-9]+]], [[PTR]] + + %alloca_ptr = alloca i256, i32 128 + %elm = getelementptr i256, ptr %alloca_ptr, i256 2 + %rv = load i256, ptr %elm + ret i256 %rv +} + +define void @alloca5(i256 %val) nounwind { +; CHECK-LABEL: alloca5: +; CHECK: ARGUMENT [[ARG1:\$[0-9]]], 0 +; CHECK: CONST_I256 [[OFF:\$[0-9]+]], 64 +; CHECK: ADD [[PTR:\$[0-9]+]], %SP, [[OFF]] +; CHECK: STACK_STORE [[PTR]], [[RES:\$[0-9]+]] + + %alloca_ptr = alloca i256, i32 128 + %elm = getelementptr i256, ptr %alloca_ptr, i256 2 + store i256 %val, ptr %elm + ret void +} diff --git a/llvm/test/CodeGen/EVM/globals.ll b/llvm/test/CodeGen/EVM/globals.ll new file mode 100644 index 000000000000..b075f04aa261 --- /dev/null +++ b/llvm/test/CodeGen/EVM/globals.ll @@ -0,0 +1,89 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +%struct.w = type { i32, i256 } + +@val = addrspace(1) global i256 0 +@val2 = addrspace(1) global i256 42 +@val3 = addrspace(1) global i256 0 +@val.arr = addrspace(1) global [4 x i256] zeroinitializer +@val2.arr = addrspace(1) global [4 x i256] [i256 1, i256 2, i256 3, i256 4] +@as_ptr = addrspace(1) global ptr zeroinitializer +@w = external dso_local local_unnamed_addr addrspace(1) global %struct.w, align 1 + +define i256 @load.stelem() { +; CHECK-LABEL: load.stelem +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @w+32 +; CHECK: MLOAD [[TMP:\$[0-9]+]], [[ADDR]] + + %elem = getelementptr inbounds %struct.w, ptr addrspace(1) @w, i32 0, i32 1 + %load = load i256, ptr addrspace(1) %elem + ret i256 %load +} + +define i256 @load.elem() nounwind { +; CHECK-LABEL: load.elem +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @val +; CHECK: MLOAD [[TMP:\$[0-9]+]], [[ADDR]] + + %res = load i256, i256 addrspace(1)* @val + ret i256 %res +} + +define void @store.elem(i256 %val) nounwind { +; CHECK-LABEL: store.elem +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @val +; CHECK: MSTORE [[ADDR]], {{.*}} + + store i256 %val, i256 addrspace(1)* @val + ret void +} + +define i256 @load.fromarray(i256 %i) nounwind { +; CHECK-LABEL: load.fromarray +; CHECK: ARGUMENT [[IDX:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C:\$[0-9]+]], 5 +; CHECK: SHL [[SHL:\$[0-9]+]], [[IDX]], [[C]] +; CHECK: CONST_I256 [[TMP:\$[0-9]+]], @val2.arr +; CHECK: ADD [[ADDR:\$[0-9]+]], [[TMP]], [[SHL]] +; CHECK: MLOAD [[RES:\$[0-9]+]], [[ADDR]] + + %elem = getelementptr [4 x i256], [4 x i256] addrspace(1)* @val2.arr, i256 0, i256 %i + %res = load i256, i256 addrspace(1)* %elem + ret i256 %res +} + +define void @store.toarray(i256 %val, i256 %i) nounwind { +; CHECK-LABEL: store.toarray +; CHECK: ARGUMENT [[IDX:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C:\$[0-9]+]], 5 +; CHECK: SHL [[SHL:\$[0-9]+]], [[IDX]], [[C]] +; CHECK: CONST_I256 [[TMP:\$[0-9]+]], @val.arr +; CHECK: ADD [[ADDR:\$[0-9]+]], [[TMP]], [[SHL]] +; CHECK: MSTORE [[ADDR]], [[VAL]] + + %elem = getelementptr [4 x i256], [4 x i256] addrspace(1)* @val.arr, i256 0, i256 %i + store i256 %val, i256 addrspace(1)* %elem + ret void +} + +define ptr @load.ptr() nounwind { +; CHECK-LABEL: load.ptr +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @as_ptr +; CHECK: MLOAD [[TMP:\$[0-9]+]], [[ADDR]] + + %res = load ptr, ptr addrspace(1) @as_ptr + ret ptr %res +} + +define void @store.ptr(ptr addrspace(1) %val) nounwind { +; CHECK-LABEL: store.ptr +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @as_ptr +; CHECK: MSTORE [[ADDR]], {{.*}} + + store ptr addrspace(1) %val, ptr addrspace(1) @as_ptr + ret void +} diff --git a/llvm/test/CodeGen/EVM/icmp.ll b/llvm/test/CodeGen/EVM/icmp.ll new file mode 100644 index 000000000000..64819212f6c8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/icmp.ll @@ -0,0 +1,130 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @icmp_eq(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_eq: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : EQ [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp eq i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_big_imm_eq(i256 %a) nounwind { +; CHECK-LABEL: icmp_big_imm_eq: +; CHECK : CONST_I256 [[C1:\$[0-9]+]], [[[0-9]+]] +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : EQ [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp eq i256 %a, 43576122634770472758325941782982599838796957244005075818703754470792663924736 + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ne(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ne: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : EQ [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp ne i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ugt(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ugt: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : GT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp ugt i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_uge(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_uge: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : LT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp uge i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ult(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ult: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : LT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp ult i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_ule(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_ule: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : GT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp ule i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_sgt(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_sgt: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SGT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp sgt i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_sge(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_sge: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SLT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp sge i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_slt(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_slt: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SLT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %cmp = icmp slt i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} + +define i256 @icmp_sle(i256 %a, i256 %b) nounwind { +; CHECK-LABEL: icmp_sle: +; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK : SGT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK : ISZERO [[RES:\$[0-9]+]], [[TMP1]] + + %cmp = icmp sle i256 %a, %b + %res = zext i1 %cmp to i256 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/intrinsic.ll b/llvm/test/CodeGen/EVM/intrinsic.ll new file mode 100644 index 000000000000..27d13c7115b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/intrinsic.ll @@ -0,0 +1,581 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @sdiv(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @sdiv +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SDIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.sdiv(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @div(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @div +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: DIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.div(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @smod(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @smod +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.smod(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @mod(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @mod +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.mod(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @shl(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @shl +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SHL [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.shl(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @shr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @shr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SHR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.shr(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @sar(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @sar +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SAR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.sar(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: @addmod +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ADDMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] + + %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + ret i256 %res +} + +define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: @mulmod +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MULMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] + + %res = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) + ret i256 %res +} + +define i256 @exp(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @exp +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: EXP [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { +; CHECK-LABEL: @sha3 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SHA3 [[RES1:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + ret i256 %res +} + +define i256 @signextend(i256 %bytesize, i256 %val) nounwind { +; CHECK-LABEL: @signextend +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SIGNEXTEND [[RES1:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + ret i256 %res +} + +define i256 @byte(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @byte +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: BYTE [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) + ret i256 %res +} + +define i256 @pc() nounwind { +; CHECK-LABEL: @pc +; CHECK: PC [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.pc() + ret i256 %res +} + +define i256 @msize() nounwind { +; CHECK-LABEL: @msize +; CHECK: MSIZE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.msize() + ret i256 %res +} + +define i256 @address() nounwind { +; CHECK-LABEL: @address +; CHECK: ADDRESS [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.address() + ret i256 %res +} + +define i256 @origin() nounwind { +; CHECK-LABEL: @origin +; CHECK: ORIGIN [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.origin() + ret i256 %res +} + +define i256 @caller() nounwind { +; CHECK-LABEL: @caller +; CHECK: CALLER [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.caller() + ret i256 %res +} + +define i256 @balance(i256 %rs1) nounwind { +; CHECK-LABEL: @balance +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: BALANCE [[RES1:\$[0-9]+]], [[IN1]] + + %res = call i256 @llvm.evm.balance(i256 %rs1) + ret i256 %res +} + +define i256 @calldatasize() nounwind { +; CHECK-LABEL: @calldatasize +; CHECK: CALLDATASIZE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.calldatasize() + ret i256 %res +} + +define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { +; CHECK-LABEL: @calldataload +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CALLDATALOAD [[RES1:\$[0-9]+]], [[IN1]] + + %res = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) + ret i256 %res +} + +define i256 @callvalue() nounwind { +; CHECK-LABEL: @callvalue +; CHECK: CALLVALUE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.callvalue() + ret i256 %res +} + +define i256 @codesize() nounwind { +; CHECK-LABEL: @codesize +; CHECK: CODESIZE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.codesize() + ret i256 %res +} + +define i256 @gasprice() nounwind { +; CHECK-LABEL: @gasprice +; CHECK: GASPRICE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.gasprice() + ret i256 %res +} + +define i256 @extcodesize(i256 %rs1) nounwind { +; CHECK-LABEL: @extcodesize +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: EXTCODESIZE [[RES1:\$[0-9]+]], [[IN1]] + + %res = call i256 @llvm.evm.extcodesize(i256 %rs1) + ret i256 %res +} + +define void @extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) nounwind { +; CHECK-LABEL: @extcodecopy +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: EXTCODECOPY [[IN1]], [[IN2]], [[IN3]], [[IN4]] + + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) + ret void +} + +define i256 @extcodehash(i256 %rs1) nounwind { +; CHECK-LABEL: @extcodehash +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: EXTCODEHASH [[RES1:\$[0-9]+]], [[IN1]] + + %res = call i256 @llvm.evm.extcodehash(i256 %rs1) + ret i256 %res +} + +define i256 @returndatasize() nounwind { +; CHECK-LABEL: @returndatasize +; CHECK: RETURNDATASIZE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.returndatasize() + ret i256 %res +} + +define i256 @blockhash(i256 %rs1) nounwind { +; CHECK-LABEL: @blockhash +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: BLOCKHASH [[RES1:\$[0-9]+]], [[IN1]] + + %res = call i256 @llvm.evm.blockhash(i256 %rs1) + ret i256 %res +} + +define i256 @blobhash(i256 %rs1) nounwind { +; CHECK-LABEL: @blobhash +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: BLOBHASH [[RES1:\$[0-9]+]], [[IN1]] + + %res = call i256 @llvm.evm.blobhash(i256 %rs1) + ret i256 %res +} + +define i256 @coinbase() nounwind { +; CHECK-LABEL: @coinbase +; CHECK: COINBASE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.coinbase() + ret i256 %res +} + +define i256 @timestamp() nounwind { +; CHECK-LABEL: @timestamp +; CHECK: TIMESTAMP [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.timestamp() + ret i256 %res +} + +define i256 @number() nounwind { +; CHECK-LABEL: @number +; CHECK: NUMBER [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.number() + ret i256 %res +} + +define i256 @difficulty() nounwind { +; CHECK-LABEL: @difficulty +; CHECK: DIFFICULTY [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.difficulty() + ret i256 %res +} + +define i256 @gaslimit() nounwind { +; CHECK-LABEL: @gaslimit +; CHECK: GASLIMIT [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.gaslimit() + ret i256 %res +} + +define i256 @chainid() nounwind { +; CHECK-LABEL: @chainid +; CHECK: CHAINID [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.chainid() + ret i256 %res +} + +define i256 @selfbalance() nounwind { +; CHECK-LABEL: @selfbalance +; CHECK: SELFBALANCE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.selfbalance() + ret i256 %res +} + +define i256 @basefee() nounwind { +; CHECK-LABEL: @basefee +; CHECK: BASEFEE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.basefee() + ret i256 %res +} + +define i256 @blobbasefee() nounwind { +; CHECK-LABEL: @blobbasefee +; CHECK: BLOBBASEFEE [[RES1:\$[0-9]+]] + + %res = call i256 @llvm.evm.blobbasefee() + ret i256 %res +} + +define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { +; CHECK-LABEL: @log0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: LOG0 [[IN1]], [[IN2]] + + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + ret void +} + +define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { +; CHECK-LABEL: @log1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: LOG1 [[IN1]], [[IN2]], [[IN3]] + + call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) + ret void +} + +define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { +; CHECK-LABEL: @log2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: LOG2 [[IN1]], [[IN2]], [[IN3]], [[IN4]] + + call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) + ret void +} + +define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { +; CHECK-LABEL: @log3 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: LOG3 [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]] + + call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) + ret void +} + +define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { +; CHECK-LABEL: @log4 +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: LOG4 [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] + + call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) + ret void +} + +define i256 @create(i256 %val, ptr addrspace(1) %off, i256 %size) nounwind { +; CHECK-LABEL: @create +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CREATE [[RES1:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] + + %ret = call i256 @llvm.evm.create(i256 %val, ptr addrspace(1) %off, i256 %size) + ret i256 %ret +} + +define i256 @call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { +; CHECK-LABEL: @call +; CHECK: ARGUMENT [[IN7:\$[0-9]+]], 6 +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]], [[IN7]] + + %ret = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) + ret i256 %ret +} + +define i256 @delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { +; CHECK-LABEL: @delegatecall +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: DELEGATECALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] + + %ret = call i256 @llvm.evm.delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace (1) %ret_off, i256 %ret_size) + ret i256 %ret +} + +define i256 @create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) nounwind { +; CHECK-LABEL: @create2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CREATE2 [[RES1:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]] + + %ret = call i256 @llvm.evm.create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) + ret i256 %ret +} + +define i256 @staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { +; CHECK-LABEL: @staticcall +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: STATICCALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] + + %ret = call i256 @llvm.evm.staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) + ret i256 %ret +} + +define void @selfdestruct(i256 %addr) nounwind { +; CHECK-LABEL: @selfdestruct +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SELFDESTRUCT [[IN1]] + + call void @llvm.evm.selfdestruct(i256 %addr) + ret void +} + +define void @return(ptr addrspace(1) %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @return +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: RETURN [[IN1]], [[IN2]] + + call void @llvm.evm.return(ptr addrspace(1) %rs1, i256 %rs2) + ret void +} + +define void @revert(ptr addrspace(1) %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @revert +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: REVERT [[IN1]], [[IN2]] + + call void @llvm.evm.revert(ptr addrspace(1) %rs1, i256 %rs2) + ret void +} + +define void @invalid() nounwind { +; CHECK-LABEL: @invalid +; CHECK: INVALID + + call void @llvm.evm.invalid() + ret void +} + +define void @pop(i256 %val) nounwind { +; CHECK-LABEL: @pop +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: POP [[IN1]] + + call void @llvm.evm.pop(i256 %val) + ret void +} + +declare i256 @llvm.evm.sdiv(i256, i256) +declare i256 @llvm.evm.div(i256, i256) +declare i256 @llvm.evm.mod(i256, i256) +declare i256 @llvm.evm.smod(i256, i256) +declare i256 @llvm.evm.shl(i256, i256) +declare i256 @llvm.evm.shr(i256, i256) +declare i256 @llvm.evm.sar(i256, i256) +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.exp(i256, i256) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) +declare i256 @llvm.evm.signextend(i256, i256) +declare i256 @llvm.evm.byte(i256, i256) +declare i256 @llvm.evm.pc() +declare i256 @llvm.evm.msize() +declare i256 @llvm.evm.address() +declare i256 @llvm.evm.origin() +declare i256 @llvm.evm.caller() +declare i256 @llvm.evm.balance(i256) +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.calldataload(ptr addrspace(2)) +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.codesize() +declare i256 @llvm.evm.gasprice() +declare i256 @llvm.evm.extcodesize(i256) +declare void @llvm.evm.extcodecopy(i256, ptr addrspace(1), ptr addrspace(4), i256) +declare i256 @llvm.evm.extcodehash(i256) +declare i256 @llvm.evm.blockhash(i256) +declare i256 @llvm.evm.blobhash(i256) +declare i256 @llvm.evm.returndatasize() +declare i256 @llvm.evm.coinbase() +declare i256 @llvm.evm.timestamp() +declare i256 @llvm.evm.number() +declare i256 @llvm.evm.difficulty() +declare i256 @llvm.evm.gaslimit() +declare i256 @llvm.evm.chainid() +declare i256 @llvm.evm.selfbalance() +declare i256 @llvm.evm.basefee() +declare i256 @llvm.evm.blobbasefee() +declare void @llvm.evm.log0(ptr addrspace(1), i256) +declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) +declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) +declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) +declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) +declare i256 @llvm.evm.create(i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.delegatecall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.create2(i256, ptr addrspace(1), i256, i256) +declare i256 @llvm.evm.staticcall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare void @llvm.evm.selfdestruct(i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) +declare void @llvm.evm.revert(ptr addrspace(1), i256) +declare void @llvm.evm.invalid() +declare void @llvm.evm.pop(i256) diff --git a/llvm/test/CodeGen/EVM/lit.local.cfg b/llvm/test/CodeGen/EVM/lit.local.cfg new file mode 100644 index 000000000000..505218ffec16 --- /dev/null +++ b/llvm/test/CodeGen/EVM/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'EVM' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll new file mode 100644 index 000000000000..34a55daa25e3 --- /dev/null +++ b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll @@ -0,0 +1,15 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@ptr = private unnamed_addr addrspace(1) global i256 0 + +define i1 @simplify_setcc() { +; CHECK-LABEL: simplify_setcc +; CHECK-NOT: @ptr+31 + %val = load i256, ptr addrspace(1) @ptr, align 32 + %val.and = and i256 %val, 2 + %cmp = icmp eq i256 %val.and, 0 + ret i1 %cmp +} diff --git a/llvm/test/CodeGen/EVM/logical.ll b/llvm/test/CodeGen/EVM/logical.ll new file mode 100644 index 000000000000..feccb239f44d --- /dev/null +++ b/llvm/test/CodeGen/EVM/logical.ll @@ -0,0 +1,43 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @andrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @andrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: AND [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = and i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @orrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @orrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: OR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = or i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @xorrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @xorrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: XOR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = xor i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @notrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @notrrr +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: NOT [[TMP:\$[0-9]+]], [[IN1]] + + %res = xor i256 %rs1, -1 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/mem_call_data.ll b/llvm/test/CodeGen/EVM/mem_call_data.ll new file mode 100644 index 000000000000..998ccf483899 --- /dev/null +++ b/llvm/test/CodeGen/EVM/mem_call_data.ll @@ -0,0 +1,13 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @mload(ptr addrspace(2) %offset) nounwind { +; CHECK-LABEL: @mload +; CHECK: ARGUMENT [[OFF:\$[0-9]+]], 0 +; CHECK: CALLDATALOAD [[RES1:\$[0-9]+]], [[OFF]] + + %val = load i256, ptr addrspace(2) %offset, align 32 + ret i256 %val +} diff --git a/llvm/test/CodeGen/EVM/memintrinsics-opt.ll b/llvm/test/CodeGen/EVM/memintrinsics-opt.ll new file mode 100644 index 000000000000..e650f6f3372f --- /dev/null +++ b/llvm/test/CodeGen/EVM/memintrinsics-opt.ll @@ -0,0 +1,13 @@ +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.memcpy.p0.p0.i256(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i256, i1 immarg) + +define fastcc void @huge-copysize0(ptr %dest, ptr %src) { +; CHECK-LABEL: huge-copysize0 +; CHECK: tail call void @llvm.memcpy.p0.p0.i256(ptr align 1 %dest, ptr align 1 %src, i256 81129638414606681695789005144064, i1 false) + call void @llvm.memcpy.p0.p0.i256(ptr %dest, ptr %src, i256 81129638414606681695789005144064, i1 false) + ret void +} diff --git a/llvm/test/CodeGen/EVM/memintrinsics.ll b/llvm/test/CodeGen/EVM/memintrinsics.ll new file mode 100644 index 000000000000..8972b095a92b --- /dev/null +++ b/llvm/test/CodeGen/EVM/memintrinsics.ll @@ -0,0 +1,126 @@ +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p2.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(2) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p3.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(3) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p4.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(4) nocapture readonly, i256, i1 immarg) +declare void @llvm.memmove.p1.p1.i8(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i8, i1 immarg) + +define fastcc void @memmove-imm8(ptr addrspace(1) %dest, ptr addrspace(1) %src) { +; CHECK-LABEL: memmove-imm8 +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i8(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 77, i1 false) + ret void +} + +define fastcc void @memmove-imm8-arg(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 %size) { +; CHECK-LABEL: memmove-imm8-arg +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i8(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 %size, i1 false) + ret void +} + +define fastcc void @huge-copysize0(ptr addrspace(1) %dest, ptr addrspace(1) %src) { +; CHECK-LABEL: huge-copysize0 +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144064, i1 false) + ret void +} + +define fastcc void @huge-copysize1(ptr addrspace(1) %dest, ptr addrspace(1) %src) { +; CHECK-LABEL: huge-copysize1 +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144065, i1 false) + ret void +} + +define fastcc void @huge-movesize1(ptr addrspace(1) %dest, ptr addrspace(1) %src) { +; CHECK-LABEL: huge-movesize1 +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144065, i1 false) + ret void +} + +define fastcc void @normal-known-size(ptr addrspace(1) %dest, ptr addrspace(1) %src) { +; CHECK-LABEL: normal-known-size +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 1024, i1 false) + ret void +} + +define fastcc void @normal-known-size-2(ptr addrspace(1) %dest, ptr addrspace(1) %src) { +; CHECK-LABEL: normal-known-size-2 +; CHECK: MCOPY + + call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 1060, i1 false) + ret void +} + +define fastcc void @calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { +; CHECK-LABEL: calldata_to_heap +; CHECK: CALLDATACOPY + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @move_calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { +; CHECK-LABEL: move_calldata_to_heap +; CHECK: CALLDATACOPY + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @calldata_to_heap_csize(ptr addrspace(1) %dest, ptr addrspace(2) %src) { +; CHECK-LABEL: calldata_to_heap_csize +; CHECK: CALLDATACOPY + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 42, i1 false) + ret void +} + +define fastcc void @returndata_to_heap(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len) { +; CHECK-LABEL: returndata_to_heap +; CHECK: RETURNDATACOPY + + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @move_returndata_to_heap(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len) { +; CHECK-LABEL: move_returndata_to_heap +; CHECK: RETURNDATACOPY + + call void @llvm.memmove.p1.p3.i256(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @code_to_heap(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len) { +; CHECK-LABEL: code_to_heap +; CHECK: CODECOPY + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len, i1 false) + ret void +} + +define fastcc void @move_code_to_heap(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len) { +; CHECK-LABEL: move_code_to_heap +; CHECK: CODECOPY + + call void @llvm.memmove.p1.p4.i256(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len, i1 false) + ret void +} diff --git a/llvm/test/CodeGen/EVM/memory.ll b/llvm/test/CodeGen/EVM/memory.ll new file mode 100644 index 000000000000..ecaf175e3610 --- /dev/null +++ b/llvm/test/CodeGen/EVM/memory.ll @@ -0,0 +1,35 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @mstore8(ptr addrspace(1) %offset, i256 %val) nounwind { +; CHECK-LABEL: @mstore8 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MSTORE8 [[IN1]], [[IN2]] + + call void @llvm.evm.mstore8(ptr addrspace(1) %offset, i256 %val) + ret void +} + +define void @mstore(ptr addrspace(1) %offset, i256 %val) nounwind { +; CHECK-LABEL: @mstore +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MSTORE [[IN1]], [[IN2]] + + store i256 %val, ptr addrspace(1) %offset, align 32 + ret void +} + +define i256 @mload(ptr addrspace(1) %offset) nounwind { +; CHECK-LABEL: @mload +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MLOAD [[RES1:\$[0-9]+]], [[IN1]] + + %val = load i256, ptr addrspace(1) %offset, align 32 + ret i256 %val +} + +declare void @llvm.evm.mstore8(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/mod.ll b/llvm/test/CodeGen/EVM/mod.ll new file mode 100644 index 000000000000..68dd697be437 --- /dev/null +++ b/llvm/test/CodeGen/EVM/mod.ll @@ -0,0 +1,32 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @umodrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @umodrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = urem i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @smodrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @smodrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = srem i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @smodrri(i256 %rs1) nounwind { +; CHECK-LABEL: @smodrri +; CHECK: CONST_I256 [[TMP:\$[0-9]+]], 0 + + %res = srem i256 %rs1, 0 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll new file mode 100644 index 000000000000..5c538882b2b4 --- /dev/null +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -0,0 +1,26 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @mulrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @mulrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: MUL [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = mul i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @mulrri(i256 %rs1) nounwind { +; CHECK-LABEL: @mulrri +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[REG1:\$[0-9]+]], 500000 +; CHECK: CONST_I256 [[REG2:\$[0-9]+]], 0 +; CHECK: SUB [[REG3:\$[0-9]+]], [[REG2]], [[REG1]] +; CHECK: MUL [[TMP:\$[0-9]+]], [[IN1]], [[REG3]] + + %res = mul i256 %rs1, -500000 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll new file mode 100644 index 000000000000..c534b0e0af95 --- /dev/null +++ b/llvm/test/CodeGen/EVM/select.ll @@ -0,0 +1,22 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { +; CHECK-LABEL: @select +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: EQ [[TMP1:\$[0-9]+]], [[IN3]], [[IN4]] +; CHECK: ISZERO [[COND:\$[0-9]+]], [[TMP1]] +; CHECK: JUMPI @.BB0_2, [[COND]] +; CHECK: COPY_I256 [[IN1]], [[IN2]] +; CHECK-LABEL: .BB0_2: +; CHECK: RET + + %1 = icmp ne i256 %v3, %v4 + %2 = select i1 %1, i256 %v1, i256 %v2 + ret i256 %2 +} diff --git a/llvm/test/CodeGen/EVM/sext.ll b/llvm/test/CodeGen/EVM/sext.ll new file mode 100644 index 000000000000..18c5a89ef16b --- /dev/null +++ b/llvm/test/CodeGen/EVM/sext.ll @@ -0,0 +1,78 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @sexti1(i1 %rs1) nounwind { +; CHECK-LABEL: @sexti1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[MASK:\$[0-9]+]], 1 +; CHECK: AND [[TMP:\$[0-9]+]], [[IN1]], [[MASK]] +; CHECK: CONST_I256 [[ZERO:\$[0-9]+]], 0 +; CHECK: SUB [[RES:\$[0-9]+]], [[ZERO]], [[TMP]] + + %res = sext i1 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti8(i8 %rs1) nounwind { +; CHECK-LABEL: @sexti8 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 0 +; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] + + %res = sext i8 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti16(i16 %rs1) nounwind { +; CHECK-LABEL: @sexti16 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 1 +; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] + + %res = sext i16 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti32(i32 %rs1) nounwind { +; CHECK-LABEL: @sexti32 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 3 +; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] + + %res = sext i32 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti64(i64 %rs1) nounwind { +; CHECK-LABEL: @sexti64 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 7 +; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] + + %res = sext i64 %rs1 to i256 + ret i256 %res +} + +define i256 @sexti128(i128 %rs1) nounwind { +; CHECK-LABEL: @sexti128 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 15 +; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] + + %res = sext i128 %rs1 to i256 + ret i256 %res +} + +; Check that 'sext' also gets lowered for types not declared in MVT. +define i256 @sexti40(i40 %rs1) nounwind { +; CHECK-LABEL: @sexti40 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C1:\$[0-9]+]], 216 +; CHECK: SHL [[TMP1:\$[0-9]+]], [[IN1]], [[C1]] +; CHECK: SAR [[TMP2:\$[0-9]]], [[TMP1]], [[C1]] + + %res = sext i40 %rs1 to i256 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/shift.ll b/llvm/test/CodeGen/EVM/shift.ll new file mode 100644 index 000000000000..33c7c3d53ced --- /dev/null +++ b/llvm/test/CodeGen/EVM/shift.ll @@ -0,0 +1,34 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @shl(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @shl +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SHL [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = shl i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @shr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @shr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SHR [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = lshr i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @sar(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @sar +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SAR [[RES:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = ashr i256 %rs1, %rs2 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/signextload.ll b/llvm/test/CodeGen/EVM/signextload.ll new file mode 100644 index 000000000000..72ab9b6bbd7f --- /dev/null +++ b/llvm/test/CodeGen/EVM/signextload.ll @@ -0,0 +1,59 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @load_signexti8(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_signexti8 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 248 +; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i8, ptr addrspace(1) %ptr + %sext = sext i8 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti16(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_signexti16 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 240 +; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i16, ptr addrspace(1) %ptr + %sext = sext i16 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti32(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_signexti32 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 224 +; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i32, ptr addrspace(1) %ptr + %sext = sext i32 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti64(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_signexti64 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 192 +; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i64, ptr addrspace(1) %ptr + %sext = sext i64 %load to i256 + ret i256 %sext +} + +define i256 @load_signexti128(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_signexti128 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 128 +; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i128, ptr addrspace(1) %ptr + %sext = sext i128 %load to i256 + ret i256 %sext +} diff --git a/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll b/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll new file mode 100644 index 000000000000..0f8637acb448 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll @@ -0,0 +1,114 @@ +; RUN: opt -O2 -S < %s | FileCheck %s + +; UNSUPPORTED: evm + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test__addmod(i256 %arg1, i256 %arg2, i256 %modulo) { +; CHECK: @llvm.evm.addmod + + %res = call i256 @__addmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @test__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) { +; CHECK: @llvm.evm.mulmod + + %res = call i256 @__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) + ret i256 %res +} + +define i256 @test__signextend(i256 %bytesize, i256 %val) { +; CHECK: @llvm.evm.signextend + + %res = call i256 @__signextend(i256 %bytesize, i256 %val) + ret i256 %res +} + +define i256 @test__exp(i256 %base, i256 %exp) { +; CHECK: @llvm.evm.exp + + %res = call i256 @__exp(i256 %base, i256 %exp) + ret i256 %res +} + +define i256 @test__byte(i256 %index, i256 %val) { +; CHECK: @llvm.evm.byte + + %res = call i256 @__byte(i256 %index, i256 %val) + ret i256 %res +} + +define i256 @test__sdiv(i256 %dividend, i256 %divisor) { +; CHECK: @llvm.evm.sdiv + + %res = call i256 @__sdiv(i256 %dividend, i256 %divisor) + ret i256 %res +} + +define i256 @test__div(i256 %dividend, i256 %divisor) { +; CHECK: @llvm.evm.div + + %res = call i256 @__div(i256 %dividend, i256 %divisor) + ret i256 %res +} + +define i256 @test__smod(i256 %val, i256 %mod) { +; CHECK: @llvm.evm.smod + + %res = call i256 @__smod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @test__mod(i256 %val, i256 %mod) { +; CHECK: @llvm.evm.mod + + %res = call i256 @__mod(i256 %val, i256 %mod) + ret i256 %res +} + +define i256 @test__shl(i256 %shift, i256 %val) { +; CHECK: @llvm.evm.shl + + %res = call i256 @__shl(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @test__shr(i256 %shift, i256 %val) { +; CHECK: @llvm.evm.shr + + %res = call i256 @__shr(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @test__sar(i256 %shift, i256 %val) { +; CHECK: @llvm.evm.sar + + %res = call i256 @__sar(i256 %shift, i256 %val) + ret i256 %res +} + +define i256 @test__sha3(ptr addrspace(1) %offset, i256 %len) { +; CHECK: @llvm.evm.sha3 + + %res = call i256 @__sha3(ptr addrspace(1) %offset, i256 %len, i1 undef) + ret i256 %res +} + +declare i256 @__addmod(i256, i256, i256) #0 +declare i256 @__mulmod(i256, i256, i256) #0 +declare i256 @__signextend(i256, i256) #0 +declare i256 @__exp(i256, i256) #0 +declare i256 @__byte(i256, i256) #0 +declare i256 @__sdiv(i256, i256) #0 +declare i256 @__div(i256, i256) #0 +declare i256 @__smod(i256, i256) #0 +declare i256 @__mod(i256, i256) #0 +declare i256 @__shl(i256, i256) #0 +declare i256 @__shr(i256, i256) #0 +declare i256 @__sar(i256, i256) #0 +declare i256 @__sha3(ptr addrspace(1), i256, i1) #1 + +attributes #0 = { alwaysinline mustprogress nofree norecurse nosync nounwind readnone willreturn } +attributes #1 = { alwaysinline argmemonly readonly nofree null_pointer_is_valid } diff --git a/llvm/test/CodeGen/EVM/storage.ll b/llvm/test/CodeGen/EVM/storage.ll new file mode 100644 index 000000000000..be51d65b027f --- /dev/null +++ b/llvm/test/CodeGen/EVM/storage.ll @@ -0,0 +1,23 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @sstore(ptr addrspace(5) %key, i256 %val) nounwind { +; CHECK-LABEL: @sstore +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SSTORE [[IN1]], [[IN2]] + + store i256 %val, ptr addrspace(5) %key, align 32 + ret void +} + +define i256 @sload(ptr addrspace(5) %key) nounwind { +; CHECK-LABEL: @sload +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SLOAD [[RES1:\$[0-9]+]], [[IN1]] + + %val = load i256, ptr addrspace(5) %key, align 32 + ret i256 %val +} diff --git a/llvm/test/CodeGen/EVM/sub.ll b/llvm/test/CodeGen/EVM/sub.ll new file mode 100644 index 000000000000..7699781a1ff5 --- /dev/null +++ b/llvm/test/CodeGen/EVM/sub.ll @@ -0,0 +1,24 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @subrrr(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: @subrrr +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: SUB [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] + + %res = sub i256 %rs1, %rs2 + ret i256 %res +} + +define i256 @addrri(i256 %rs1) nounwind { +; CHECK-LABEL: @addrri +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[C1:\$[0-9]+]], 3 +; CHECK: SUB [[TMP:\$[0-9]+]], [[IN1]], [[C1]] + + %res = add i256 %rs1, -3 + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/truncstore.ll b/llvm/test/CodeGen/EVM/truncstore.ll new file mode 100644 index 000000000000..9c29961b5711 --- /dev/null +++ b/llvm/test/CodeGen/EVM/truncstore.ll @@ -0,0 +1,34 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@glob_i8 = addrspace(1) global i8 0 +@glob_i32 = addrspace(1) global i32 0 + +define void @storei8(i8 %val) nounwind { +; CHECK-LABEL: @storei8 +; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @glob_i8 +; CHECK: MSTORE8 [[ADDR]], [[VAL]] + + store i8 %val, ptr addrspace(1) @glob_i8 + ret void +} + +define void @storei32(i32 %val) nounwind { +; CHECK-LABEL: @storei32 +; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 +; CHECK: CONST_I256 [[TMP1:\$[0-9]+]], 224 +; CHECK: SHL [[SHL_VAL:\$[0-9]+]], [[VAL]], [[TMP1]] +; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @glob_i32 +; CHECK: MLOAD [[ORIG_MEM:\$[0-9]+]], [[ADDR]] +; CHECK: CONST_I256 [[TMP2:\$[0-9]+]], 32 +; CHECK: SHR [[SHR_MEM:\$[0-9]+]], [[ORIG_MEM]], [[TMP2]] +; CHECK: SHL [[SHL_MEM:\$[0-9]+]], [[SHR_MEM]], [[TMP2]] +; CHECK: OR [[RES_MEM:\$[0-9]+]], [[SHL_VAL]], [[SHL_MEM]] +; CHECK: MSTORE [[ADDR]], [[RES_MEM]] + + store i32 %val, ptr addrspace(1) @glob_i32 + ret void +} diff --git a/llvm/test/CodeGen/EVM/tstorage.ll b/llvm/test/CodeGen/EVM/tstorage.ll new file mode 100644 index 000000000000..2c719c539d04 --- /dev/null +++ b/llvm/test/CodeGen/EVM/tstorage.ll @@ -0,0 +1,23 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @tstore(ptr addrspace(6) %key, i256 %val) nounwind { +; CHECK-LABEL: @tstore +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: TSTORE [[IN1]], [[IN2]] + + store i256 %val, ptr addrspace(6) %key, align 32 + ret void +} + +define i256 @tload(ptr addrspace(6) %key) nounwind { +; CHECK-LABEL: @tload +; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: TLOAD [[RES1:\$[0-9]+]], [[IN1]] + + %val = load i256, ptr addrspace(6) %key, align 32 + ret i256 %val +} diff --git a/llvm/test/CodeGen/EVM/zero_any_extload.ll b/llvm/test/CodeGen/EVM/zero_any_extload.ll new file mode 100644 index 000000000000..9f9b34c7ddf4 --- /dev/null +++ b/llvm/test/CodeGen/EVM/zero_any_extload.ll @@ -0,0 +1,109 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i8 @load_anyext_i8(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_anyext_i8 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 248 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i8, ptr addrspace(1) %ptr + ret i8 %load +} + +define i16 @load_anyext_i16(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_anyext_i16 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 240 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i16, ptr addrspace(1) %ptr + ret i16 %load +} + +define i32 @load_anyext_i32(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_anyext_i32 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 224 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i32, ptr addrspace(1) %ptr + ret i32 %load +} + +define i64 @load_anyext_i64(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_anyext_i64 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 192 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i64, ptr addrspace(1) %ptr + ret i64 %load +} + +define i128 @load_anyext_i128(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_anyext_i128 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 128 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i128, ptr addrspace(1) %ptr + ret i128 %load +} + +define i256 @load_zeroext_i8(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_zeroext_i8 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 248 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i8, ptr addrspace(1) %ptr + %zext = zext i8 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i16(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_zeroext_i16 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 240 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i16, ptr addrspace(1) %ptr + %zext = zext i16 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i32(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_zeroext_i32 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 224 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i32, ptr addrspace(1) %ptr + %zext = zext i32 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i64(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_zeroext_i64 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 192 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i64, ptr addrspace(1) %ptr + %zext = zext i64 %load to i256 + ret i256 %zext +} + +define i256 @load_zeroext_i128(ptr addrspace(1) %ptr) nounwind { +; CHECK-LABEL: @load_zeroext_i128 +; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 128 +; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] + + %load = load i128, ptr addrspace(1) %ptr + %zext = zext i128 %load to i256 + ret i256 %zext +} diff --git a/llvm/test/CodeGen/EVM/zext.ll b/llvm/test/CodeGen/EVM/zext.ll new file mode 100644 index 000000000000..203803f782d0 --- /dev/null +++ b/llvm/test/CodeGen/EVM/zext.ll @@ -0,0 +1,49 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @zexti8(i8 %rs1) nounwind { +; CHECK-LABEL: @zexti8 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 255 +; CHECK: AND {{.*}}, {{.*}}, [[EXT]] + + %res = zext i8 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti16(i16 %rs1) nounwind { +; CHECK-LABEL: @zexti16 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 65535 +; CHECK: AND {{.*}}, {{.*}}, [[EXT]] + + %res = zext i16 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti32(i32 %rs1) nounwind { +; CHECK-LABEL: @zexti32 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 4294967295 +; CHECK: AND {{.*}}, {{.*}}, [[EXT]] + + %res = zext i32 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti64(i64 %rs1) nounwind { +; CHECK-LABEL: @zexti64 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 18446744073709551615 +; CHECK: AND {{.*}}, {{.*}}, [[EXT]] + + %res = zext i64 %rs1 to i256 + ret i256 %res +} + +define i256 @zexti128(i128 %rs1) nounwind { +; CHECK-LABEL: @zexti128 +; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 340282366920938463463374607431768211455 +; CHECK: AND {{.*}}, {{.*}}, [[EXT]] + + %res = zext i128 %rs1 to i256 + ret i256 %res +} diff --git a/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll b/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll index 3b4eef89a8d4..50a8b8eeab21 100644 --- a/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll +++ b/llvm/test/CodeGen/Generic/2002-04-16-StackFrameSizeAlignment.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; Compiling this file produces: ; Sparc.cpp:91: failed assertion `(offset - OFFSET) % getStackFrameSizeAlignment() == 0' diff --git a/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll b/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll index 6fb17991e739..9fd10b2ec46f 100644 --- a/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll +++ b/llvm/test/CodeGen/Generic/2003-05-27-phifcmpd.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} + define void @QRiterate(i32 %p.1, double %tmp.212) { entry: %tmp.184 = icmp sgt i32 %p.1, 0 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll b/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll index 14bb00048d20..21069c3391e3 100644 --- a/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll +++ b/llvm/test/CodeGen/Generic/2003-05-27-useboolinotherbb.ll @@ -1,3 +1,5 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support floats. ; RUN: llc < %s define void @QRiterate(double %tmp.212) { diff --git a/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll b/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll index cc0eb5cd1374..1c075f2f0105 100644 --- a/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll +++ b/llvm/test/CodeGen/Generic/2003-05-27-usefsubasbool.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define void @QRiterate(double %tmp.212) { diff --git a/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll b/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll index 3f64fca908bd..a82ece62ba6c 100644 --- a/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll +++ b/llvm/test/CodeGen/Generic/2003-05-28-ManyArgs.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/External/SPEC/CINT2000/175.vpr.llvm.bc diff --git a/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll b/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll index db578830a3ba..fe93d3c78087 100644 --- a/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll +++ b/llvm/test/CodeGen/Generic/2003-05-30-BadFoldGEP.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/External/SPEC/CINT2000/254.gap.llvm.bc diff --git a/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll b/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll index 9dcfde5063c0..4fb5223bc9c3 100644 --- a/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll +++ b/llvm/test/CodeGen/Generic/2003-05-30-BadPreselectPhi.ll @@ -1,4 +1,7 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-921 Needs proger GA wrapping to be implemented. ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/SingleSource/richards_benchmark.c diff --git a/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll b/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll index 010097a03650..5ee532d5de19 100644 --- a/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll +++ b/llvm/test/CodeGen/Generic/2003-07-06-BadIntCmp.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: May 28, 2003. ;; From: test/Programs/MultiSource/Olden-perimeter/maketree.c diff --git a/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll b/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll index fe0b43d26fe4..ba15eb48d97f 100644 --- a/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll +++ b/llvm/test/CodeGen/Generic/2003-07-07-BadLongConst.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [42 x i8] c" ui = %u (0x%x)\09\09UL-ui = %lld (0x%llx)\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll b/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll index b76384551b22..5ca6cd6805f5 100644 --- a/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll +++ b/llvm/test/CodeGen/Generic/2003-07-08-BadCastToBool.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; Date: Jul 8, 2003. ;; From: test/Programs/MultiSource/Olden-perimeter diff --git a/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll b/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll index 1f7f9891e2de..09cf50708878 100644 --- a/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll +++ b/llvm/test/CodeGen/Generic/2003-07-29-BadConstSbyte.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; Bug: PR31341 diff --git a/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll b/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll index fbffaa257ed0..df59a0ace848 100644 --- a/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll +++ b/llvm/test/CodeGen/Generic/2004-05-09-LiveVarPartialRegister.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @global_long_1 = linkonce global i64 7 ; [#uses=1] @global_long_2 = linkonce global i64 49 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll b/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll index 33599fa3954a..6ef75a8c3580 100644 --- a/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll +++ b/llvm/test/CodeGen/Generic/2005-04-09-GlobalInPHI.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + %struct.TypHeader = type { i32, ptr, [3 x i8], i8 } @.str_67 = external global [4 x i8] ; [#uses=1] @.str_87 = external global [17 x i8] ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll b/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll index 942592fc7214..33f8a37f3dac 100644 --- a/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll +++ b/llvm/test/CodeGen/Generic/2005-10-18-ZeroSizeStackObject.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @test() { %X = alloca { } ; [#uses=0] diff --git a/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll b/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll index cd9e6800ab5b..41a146c14a12 100644 --- a/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll +++ b/llvm/test/CodeGen/Generic/2005-12-01-Crash.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @str = external global [36 x i8] ; [#uses=0] @str.upgrd.1 = external global [29 x i8] ; [#uses=0] @str1 = external global [29 x i8] ; [#uses=0] diff --git a/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll b/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll index 6812d4d4b4eb..53135f7b40ca 100644 --- a/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll +++ b/llvm/test/CodeGen/Generic/2006-02-12-InsertLibcall.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @G = external global i32 ; [#uses=1] define void @encode_one_frame(i64 %tmp.2i) { diff --git a/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll b/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll index 8d406df1242d..7449b9639f10 100644 --- a/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll +++ b/llvm/test/CodeGen/Generic/2006-03-01-dagcombineinfloop.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + ; Infinite loop in the dag combiner, reduced from 176.gcc. %struct._obstack_chunk = type { ptr, ptr, [4 x i8] } %struct.anon = type { i32 } diff --git a/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll b/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll index 6daa5e0c9b7e..edf45d0235f4 100644 --- a/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll +++ b/llvm/test/CodeGen/Generic/2006-06-13-ComputeMaskedBitsCrash.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s -O0 - +; UNSUPPORTED: target=evm{{.*}} + %struct.cl_perfunc_opts = type { i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i32, i32, i32, i32, i32, i32, i32 } @cl_pf_opts = external global %struct.cl_perfunc_opts ; [#uses=2] diff --git a/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll b/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll index 2eb768517476..1bd0facb30e7 100644 --- a/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll +++ b/llvm/test/CodeGen/Generic/2006-06-28-SimplifySetCCCrash.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + %struct.rtunion = type { i64 } %struct.rtx_def = type { i16, i8, i8, [1 x %struct.rtunion] } @ix86_cpu = external global i32 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll b/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll index b3d2e95f644e..0675ab0dc19b 100644 --- a/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll +++ b/llvm/test/CodeGen/Generic/2006-07-03-schedulers.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s -pre-RA-sched=default ; RUN: llc < %s -pre-RA-sched=list-burr ; RUN: llc < %s -pre-RA-sched=fast diff --git a/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll b/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll index 7feeca65e312..c2ac533da4c3 100644 --- a/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll +++ b/llvm/test/CodeGen/Generic/2007-04-08-MultipleFrameIndices.ll @@ -1,4 +1,7 @@ ; RUN: llc -no-integrated-as < %s + +; EVM doesn't support vararg. +; XFAIL: target=evm{{.*}} ; PR1308 ; PR1557 diff --git a/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll b/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll index f85cda05b073..1096fa11a6ab 100644 --- a/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll +++ b/llvm/test/CodeGen/Generic/2007-04-27-InlineAsm-X-Dest.ll @@ -1,5 +1,8 @@ ; RUN: llc -no-integrated-as < %s +; EVM doesn't support inline assembly yet. +; UNSUPPORTED: target=evm{{.*}} + ; Test that we can have an "X" output constraint. define void @test(ptr %t) { diff --git a/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll b/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll index 5143fb21a215..4288dd3ccb33 100644 --- a/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll +++ b/llvm/test/CodeGen/Generic/2007-04-27-LargeMemObject.ll @@ -1,5 +1,8 @@ ; RUN: llc -no-integrated-as < %s +; EVM doesn't support inline asm. +; UNSUPPORTED: target=evm{{.*}} + %struct..0anon = type { [100 x i32] } define void @test() { diff --git a/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll b/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll index 58415bf1e827..2b830f859c4f 100644 --- a/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll +++ b/llvm/test/CodeGen/Generic/2007-12-17-InvokeAsm.ll @@ -1,3 +1,5 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support inline asm. ; RUN: llc -no-integrated-as < %s ; The test uses inline assembly with x86-specific constraints. diff --git a/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll b/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll index 755eebd93b61..89a9c8d6a178 100644 --- a/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll +++ b/llvm/test/CodeGen/Generic/2007-12-31-UnusedSelector.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; PR1833 +; UNSUPPORTED: target=evm{{.*}} %struct.__class_type_info_pseudo = type { %struct.__type_info_pseudo } %struct.__type_info_pseudo = type { ptr, ptr } diff --git a/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll b/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll index a0388c46ece2..b75f15938c49 100644 --- a/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll +++ b/llvm/test/CodeGen/Generic/2008-01-25-dag-combine-mul.ll @@ -3,6 +3,7 @@ ; XCore default subtarget does not support 8-byte alignment on stack. ; XFAIL: target=xcore{{.*}} +; UNSUPPORTED: target=evm{{.*}} define i32 @f(ptr %pc) { entry: diff --git a/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll b/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll index fc8297035a50..7c13062228d0 100644 --- a/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll +++ b/llvm/test/CodeGen/Generic/2008-01-30-LoadCrash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @letters.3100 = external constant [63 x i8] ; [#uses=2] diff --git a/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll b/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll index 43815237c67e..3ee420a0389e 100644 --- a/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll +++ b/llvm/test/CodeGen/Generic/2008-02-04-Ctlz.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s +; TODO: Check cttz, ctlz, ctpop, ConstantPool lowering. +; XFAIL: target=evm{{.*}} + @.str = internal constant [14 x i8] c"%lld %d %d %d\00" define i32 @main(i64 %arg) nounwind { diff --git a/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll b/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll index 0a21c39b2058..a479c02bd5a6 100644 --- a/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll +++ b/llvm/test/CodeGen/Generic/2008-02-20-MatchingMem.ll @@ -1,4 +1,6 @@ ; RUN: llc -no-integrated-as < %s +; EVM doesn't support inline asm. +; UNSUPPORTED: target=evm{{.*}} ; PR1133 define void @test(ptr %X) nounwind { entry: diff --git a/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll b/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll index 7284e0d606b8..ac3292032edf 100644 --- a/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll +++ b/llvm/test/CodeGen/Generic/2008-02-25-NegateZero.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; rdar://5763967 define void @test() { diff --git a/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll b/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll index b0282f32b81a..e6f75fe024e1 100644 --- a/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll +++ b/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; XFAIL: target=evm{{.*}} +; TODO: CPR-920 support operators ; PR2603 %struct.A = type { i8 } %struct.B = type { i8, [1 x i8] } diff --git a/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll b/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll index b09859def617..96283deb3410 100644 --- a/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll +++ b/llvm/test/CodeGen/Generic/2009-03-29-SoftFloatVectorExtract.ll @@ -1,4 +1,5 @@ ; XFAIL: target={{.*}}-aix{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR3899 diff --git a/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll b/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll index feec0966feb4..b7db8fdb4252 100644 --- a/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll +++ b/llvm/test/CodeGen/Generic/2009-11-16-BadKillsCrash.ll @@ -1,3 +1,5 @@ +; XFAIL: target=evm{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR5495 diff --git a/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll b/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll index 189b9319c3f5..9c3e72090121 100644 --- a/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll +++ b/llvm/test/CodeGen/Generic/2010-11-04-BigByval.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support byval yet. +; XFAIL: target=evm{{.*}} ; PR7170 ; The test is intentionally disabled only for the NVPTX target diff --git a/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll b/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll index 0a3c507cee7e..aad045b89751 100644 --- a/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll +++ b/llvm/test/CodeGen/Generic/2010-ZeroSizedArg.ll @@ -2,7 +2,7 @@ ; PR4975 ; NVPTX does not support zero sized type arg -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} %0 = type <{ [0 x i32] }> %union.T0 = type { } diff --git a/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll b/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll index a1aed0e3a4b6..32d3fe3f35b9 100644 --- a/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll +++ b/llvm/test/CodeGen/Generic/2013-03-20-APFloatCrash.ll @@ -1,3 +1,6 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support floats. + ; RUN: llc < %s define internal i1 @f(float %s) { diff --git a/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll b/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll index cec0b32f8c4c..da33efc5cbb5 100644 --- a/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll +++ b/llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll @@ -1,6 +1,7 @@ ; Test that opaque constants are not creating an infinite DAGCombine loop ; RUN: llc < %s ; XFAIL: target=r600{{.*}} +; UNSUPPORTED: target=evm{{.*}} @a = common global ptr null, align 8 @c = common global i32 0, align 4 diff --git a/llvm/test/CodeGen/Generic/APIntLoadStore.ll b/llvm/test/CodeGen/Generic/APIntLoadStore.ll index ed904a08e24b..6813d6984831 100644 --- a/llvm/test/CodeGen/Generic/APIntLoadStore.ll +++ b/llvm/test/CodeGen/Generic/APIntLoadStore.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntLoadStore.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/APIntParam.ll b/llvm/test/CodeGen/Generic/APIntParam.ll index 39f856d9dd14..8332ea398d24 100644 --- a/llvm/test/CodeGen/Generic/APIntParam.ll +++ b/llvm/test/CodeGen/Generic/APIntParam.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntParam.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/APIntSextParam.ll b/llvm/test/CodeGen/Generic/APIntSextParam.ll index d34c88e2cb87..0fbe8d99271a 100644 --- a/llvm/test/CodeGen/Generic/APIntSextParam.ll +++ b/llvm/test/CodeGen/Generic/APIntSextParam.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntSextParam.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/APIntZextParam.ll b/llvm/test/CodeGen/Generic/APIntZextParam.ll index 7fb0791c5dcc..b610ec8308a0 100644 --- a/llvm/test/CodeGen/Generic/APIntZextParam.ll +++ b/llvm/test/CodeGen/Generic/APIntZextParam.ll @@ -1,5 +1,8 @@ ; RUN: llc < %s > %t +; TODO: CPR-916 Fix the test. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX does not support arbitrary integer types and has acceptable subset tested in NVPTX/APIntZextParam.ll ; UNSUPPORTED: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/ConstantExprLowering.ll b/llvm/test/CodeGen/Generic/ConstantExprLowering.ll index ac44d9a90882..9b12aafdd1d3 100644 --- a/llvm/test/CodeGen/Generic/ConstantExprLowering.ll +++ b/llvm/test/CodeGen/Generic/ConstantExprLowering.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [16 x i8] c"%d %d %d %d %d\0A\00" ; [#uses=1] @XA = external global i32 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/ForceStackAlign.ll b/llvm/test/CodeGen/Generic/ForceStackAlign.ll index 7993b3eff65b..cd7e48246dba 100644 --- a/llvm/test/CodeGen/Generic/ForceStackAlign.ll +++ b/llvm/test/CodeGen/Generic/ForceStackAlign.ll @@ -11,6 +11,9 @@ ; NVPTX can only select dynamic_stackalloc on sm_52+ and with ptx73+ ; XFAIL: target=nvptx{{.*}} +; EVM realignment not supported. +; UNSUPPORTED: target=evm{{.*}} + define i32 @f(ptr %p) nounwind { entry: %0 = load i8, ptr %p diff --git a/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll b/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll index 56c7cf45705a..26842b6ff5f7 100644 --- a/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll +++ b/llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll @@ -1,5 +1,6 @@ ; RUN: llc -debugify-check-and-strip-all-safe -o - %s 2>&1 | FileCheck %s ; RUN: llc --experimental-debuginfo-iterators=false -debugify-check-and-strip-all-safe -o - %s 2>&1 | FileCheck %s +; UNSUPPORTED: target=evm{{.*}} ; ModuleID = 'main.c' source_filename = "main.c" diff --git a/llvm/test/CodeGen/Generic/PBQP.ll b/llvm/test/CodeGen/Generic/PBQP.ll index 31fc4e653d7b..dccafe51afff 100644 --- a/llvm/test/CodeGen/Generic/PBQP.ll +++ b/llvm/test/CodeGen/Generic/PBQP.ll @@ -1,4 +1,5 @@ ; RUN: llc -regalloc=pbqp < %s +; UNSUPPORTED: target=evm{{.*}} define i32 @foo() { entry: diff --git a/llvm/test/CodeGen/Generic/add-with-overflow-128.ll b/llvm/test/CodeGen/Generic/add-with-overflow-128.ll index 389e6511b6c7..f0add0e18e29 100644 --- a/llvm/test/CodeGen/Generic/add-with-overflow-128.ll +++ b/llvm/test/CodeGen/Generic/add-with-overflow-128.ll @@ -3,6 +3,9 @@ ; NVPTX fails to LowerFormalArguments for arg type i96 ; the arg byte size must be one of the {16, 8, 4, 2} ; XFAIL: target=nvptx{{.*}} +; UNSUPPORTED: target=evm{{.*}} + +; XFAIL: target=evm{{.*}} @ok = internal constant [4 x i8] c"%d\0A\00" @no = internal constant [4 x i8] c"no\0A\00" diff --git a/llvm/test/CodeGen/Generic/add-with-overflow-24.ll b/llvm/test/CodeGen/Generic/add-with-overflow-24.ll index cd05fefc91b9..842314a95796 100644 --- a/llvm/test/CodeGen/Generic/add-with-overflow-24.ll +++ b/llvm/test/CodeGen/Generic/add-with-overflow-24.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @ok = internal constant [4 x i8] c"%d\0A\00" @no = internal constant [4 x i8] c"no\0A\00" diff --git a/llvm/test/CodeGen/Generic/add-with-overflow.ll b/llvm/test/CodeGen/Generic/add-with-overflow.ll index fa459f64b039..9d95116531c3 100644 --- a/llvm/test/CodeGen/Generic/add-with-overflow.ll +++ b/llvm/test/CodeGen/Generic/add-with-overflow.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; RUN: llc < %s -fast-isel +; UNSUPPORTED: target=evm{{.*}} @ok = internal constant [4 x i8] c"%d\0A\00" @no = internal constant [4 x i8] c"no\0A\00" diff --git a/llvm/test/CodeGen/Generic/allow-check.ll b/llvm/test/CodeGen/Generic/allow-check.ll index a08488959862..ba7a1399aa8f 100644 --- a/llvm/test/CodeGen/Generic/allow-check.ll +++ b/llvm/test/CodeGen/Generic/allow-check.ll @@ -6,6 +6,7 @@ ; XFAIL: target=nvptx{{.*}} ; XFAIL: target=sparc{{.*}} ; XFAIL: target=hexagon-{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s -O3 -global-isel=0 -fast-isel=0 ; RUN: llc < %s -O3 -global-isel=1 -fast-isel=0 diff --git a/llvm/test/CodeGen/Generic/asm-large-immediate.ll b/llvm/test/CodeGen/Generic/asm-large-immediate.ll index 67a7a1e75a83..cbccc678e132 100644 --- a/llvm/test/CodeGen/Generic/asm-large-immediate.ll +++ b/llvm/test/CodeGen/Generic/asm-large-immediate.ll @@ -1,5 +1,8 @@ ; RUN: llc -no-integrated-as < %s | FileCheck %s +; EVM doesn't support inline asm yet. +; UNSUPPORTED: target=evm{{.*}} + define void @test() { entry: ; CHECK: /* result: 68719476738 */ diff --git a/llvm/test/CodeGen/Generic/badFoldGEP.ll b/llvm/test/CodeGen/Generic/badFoldGEP.ll index d9bf877fe881..f45af74ccbe7 100644 --- a/llvm/test/CodeGen/Generic/badFoldGEP.ll +++ b/llvm/test/CodeGen/Generic/badFoldGEP.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; GetMemInstArgs() folded the two getElementPtr instructions together, ;; producing an illegal getElementPtr. That's because the type generated diff --git a/llvm/test/CodeGen/Generic/badarg6.ll b/llvm/test/CodeGen/Generic/badarg6.ll index 3ddad36e0941..cd20c1fcb6c3 100644 --- a/llvm/test/CodeGen/Generic/badarg6.ll +++ b/llvm/test/CodeGen/Generic/badarg6.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; On this code, llc did not pass the sixth argument (%reg321) to printf. ; It passed the first five in %o0 - %o4, but never initialized %o5. diff --git a/llvm/test/CodeGen/Generic/bool-to-double.ll b/llvm/test/CodeGen/Generic/bool-to-double.ll index 81350a40b4db..5a2ffe3ea62e 100644 --- a/llvm/test/CodeGen/Generic/bool-to-double.ll +++ b/llvm/test/CodeGen/Generic/bool-to-double.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats +; UNSUPPORTED: target=evm{{.*}} define double @test(i1 %X) { %Y = uitofp i1 %X to double ; [#uses=1] ret double %Y diff --git a/llvm/test/CodeGen/Generic/builtin-expect.ll b/llvm/test/CodeGen/Generic/builtin-expect.ll index adeeea6c32cf..528b091b169e 100644 --- a/llvm/test/CodeGen/Generic/builtin-expect.ll +++ b/llvm/test/CodeGen/Generic/builtin-expect.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define i32 @test1(i32 %x) nounwind uwtable ssp { entry: diff --git a/llvm/test/CodeGen/Generic/cast-fp.ll b/llvm/test/CodeGen/Generic/cast-fp.ll index a205cce6c9aa..21248a430fe8 100644 --- a/llvm/test/CodeGen/Generic/cast-fp.ll +++ b/llvm/test/CodeGen/Generic/cast-fp.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats +; UNSUPPORTED: target=evm{{.*}} @a_fstr = internal constant [8 x i8] c"a = %f\0A\00" ; [#uses=1] @a_lstr = internal constant [10 x i8] c"a = %lld\0A\00" ; [#uses=1] @a_dstr = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/constindices.ll b/llvm/test/CodeGen/Generic/constindices.ll index 0e8b53fb36b2..7c4d3951bcb3 100644 --- a/llvm/test/CodeGen/Generic/constindices.ll +++ b/llvm/test/CodeGen/Generic/constindices.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; Test that a sequence of constant indices are folded correctly ; into the equivalent offset at compile-time. diff --git a/llvm/test/CodeGen/Generic/crash.ll b/llvm/test/CodeGen/Generic/crash.ll index a1de27503e7e..cb798898a581 100644 --- a/llvm/test/CodeGen/Generic/crash.ll +++ b/llvm/test/CodeGen/Generic/crash.ll @@ -1,4 +1,6 @@ ; RUN: llc %s -o - +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; PR6332 %struct.AVCodecTag = type {} diff --git a/llvm/test/CodeGen/Generic/dag-combine-crash.ll b/llvm/test/CodeGen/Generic/dag-combine-crash.ll index bfc5be913ef4..d4ce66c5651a 100644 --- a/llvm/test/CodeGen/Generic/dag-combine-crash.ll +++ b/llvm/test/CodeGen/Generic/dag-combine-crash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @main() { if.end: diff --git a/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll b/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll index d2402aadba18..59eb93cb722a 100644 --- a/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll +++ b/llvm/test/CodeGen/Generic/dag-combine-ossfuzz-crash.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; XFAIL: target=avr{{.*}} +; UNSUPPORTED: target=evm{{.*}} ; llc built with address sanitizer crashes because of a dangling node pointer ; oss-fuzz - DAGCombiner::useDivRem (5011) diff --git a/llvm/test/CodeGen/Generic/dwarf-md5.ll b/llvm/test/CodeGen/Generic/dwarf-md5.ll index 6cee2420a989..13fe5b16ebf4 100644 --- a/llvm/test/CodeGen/Generic/dwarf-md5.ll +++ b/llvm/test/CodeGen/Generic/dwarf-md5.ll @@ -1,4 +1,6 @@ ; XFAIL: target={{.*}}-aix{{.*}} +; EVM doesn't support dwarf yet +; UNSUPPORTED: target=evm{{.*}} ; MD5 checksums provided by IR should be passed through to asm. ; They'll be emitted to an object file only for DWARF 5 or later. diff --git a/llvm/test/CodeGen/Generic/dwarf-source.ll b/llvm/test/CodeGen/Generic/dwarf-source.ll index 55eba5e9d71a..3d0c2ec813bc 100644 --- a/llvm/test/CodeGen/Generic/dwarf-source.ll +++ b/llvm/test/CodeGen/Generic/dwarf-source.ll @@ -1,4 +1,6 @@ ; XFAIL: target={{.*}}-aix{{.*}} +; EVM doesn't support dwarf yet +; UNSUPPORTED: target=evm{{.*}} ; Source text provided by IR should be passed through to asm. ; It is emitted to an object file only for DWARF 5 or later. diff --git a/llvm/test/CodeGen/Generic/empty-load-store.ll b/llvm/test/CodeGen/Generic/empty-load-store.ll index dbd199956296..988214055565 100644 --- a/llvm/test/CodeGen/Generic/empty-load-store.ll +++ b/llvm/test/CodeGen/Generic/empty-load-store.ll @@ -2,7 +2,7 @@ ; PR2612 ; Triggers a crash on assertion as NVPTX does not support 0-sized arrays. -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} @current_foo = internal global { } zeroinitializer diff --git a/llvm/test/CodeGen/Generic/exception-handling.ll b/llvm/test/CodeGen/Generic/exception-handling.ll index 81fe962ae60c..37fcce74f62f 100644 --- a/llvm/test/CodeGen/Generic/exception-handling.ll +++ b/llvm/test/CodeGen/Generic/exception-handling.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support EH. +; UNSUPPORTED: target=evm{{.*}} ; PR10733 declare void @_Znam() diff --git a/llvm/test/CodeGen/Generic/fastcall.ll b/llvm/test/CodeGen/Generic/fastcall.ll index c42099eccdf3..eb9b90b06947 100644 --- a/llvm/test/CodeGen/Generic/fastcall.ll +++ b/llvm/test/CodeGen/Generic/fastcall.ll @@ -1,6 +1,7 @@ ; Test fastcc works. Test from bug 2770. ; RUN: llc < %s -relocation-model=pic +; UNSUPPORTED: target=evm{{.*}} %struct.__gcov_var = type { i32 } @__gcov_var = external global %struct.__gcov_var diff --git a/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll b/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll index 632e6b3fc044..0c5adf11c16f 100644 --- a/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll +++ b/llvm/test/CodeGen/Generic/fp-to-int-invalid.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR4057 define void @test_cast_float_to_char(ptr %result) nounwind { diff --git a/llvm/test/CodeGen/Generic/fp_to_int.ll b/llvm/test/CodeGen/Generic/fp_to_int.ll index ad944132d338..3a31ce55bb10 100644 --- a/llvm/test/CodeGen/Generic/fp_to_int.ll +++ b/llvm/test/CodeGen/Generic/fp_to_int.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define i8 @test1(double %X) { diff --git a/llvm/test/CodeGen/Generic/fpoperations.ll b/llvm/test/CodeGen/Generic/fpoperations.ll index 53dd307db249..b58174ec35e0 100644 --- a/llvm/test/CodeGen/Generic/fpoperations.ll +++ b/llvm/test/CodeGen/Generic/fpoperations.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s | FileCheck %s ; This test checks default lowering of the intrinsics operating floating point diff --git a/llvm/test/CodeGen/Generic/fpowi-promote.ll b/llvm/test/CodeGen/Generic/fpowi-promote.ll index cb7dfc7036ba..31f85aaab71c 100644 --- a/llvm/test/CodeGen/Generic/fpowi-promote.ll +++ b/llvm/test/CodeGen/Generic/fpowi-promote.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR1239 diff --git a/llvm/test/CodeGen/Generic/fwdtwice.ll b/llvm/test/CodeGen/Generic/fwdtwice.ll index 9d6e943b05d4..966b38b0311f 100644 --- a/llvm/test/CodeGen/Generic/fwdtwice.ll +++ b/llvm/test/CodeGen/Generic/fwdtwice.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ;; ;; Test the sequence: ;; cast -> setle 0, %cast -> br %cond diff --git a/llvm/test/CodeGen/Generic/global-ret0.ll b/llvm/test/CodeGen/Generic/global-ret0.ll index 78586fa6a461..f2d0343c01ab 100644 --- a/llvm/test/CodeGen/Generic/global-ret0.ll +++ b/llvm/test/CodeGen/Generic/global-ret0.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @g = global i32 0 ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/hello.ll b/llvm/test/CodeGen/Generic/hello.ll index 65848c7e5d6c..dd4c19c27c8e 100644 --- a/llvm/test/CodeGen/Generic/hello.ll +++ b/llvm/test/CodeGen/Generic/hello.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [7 x i8] c"hello\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll b/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll index 8156dfdd74eb..705bd89b491a 100644 --- a/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll +++ b/llvm/test/CodeGen/Generic/inline-asm-mem-clobber.ll @@ -3,6 +3,9 @@ ; XCore default subtarget does not support 8-byte alignment on stack. ; XFAIL: target=xcore{{.*}} +; EVM doesn't support inline asm yet. +; UNSUPPORTED: target=evm{{.*}} + @G = common global i32 0, align 4 define i32 @foo(ptr %p) nounwind uwtable { diff --git a/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll b/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll index 5f9f34d1e78c..44813f16c9ed 100644 --- a/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll +++ b/llvm/test/CodeGen/Generic/inline-asm-special-strings.ll @@ -1,4 +1,6 @@ ; RUN: llc -no-integrated-as < %s | FileCheck %s +; EVM doesn't support inline asm yet. +; UNSUPPORTED: target=evm{{.*}} define void @bar() nounwind { ; CHECK: foo 0 0{{$}} diff --git a/llvm/test/CodeGen/Generic/intrinsics.ll b/llvm/test/CodeGen/Generic/intrinsics.ll index 82e2b3ec7dee..70805a19b1ff 100644 --- a/llvm/test/CodeGen/Generic/intrinsics.ll +++ b/llvm/test/CodeGen/Generic/intrinsics.ll @@ -1,6 +1,13 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support floats. +; invariant and sideeffect have nothing to do with the codegen. ; RUN: llc < %s ; RUN: llc -O0 < %s +; EVM doesn't support floats. +; invariant and sideeffect have nothing to do with the codegen. +; UNSUPPORTED: target=evm{{.*}} + ; NVPTX can't select sinf(float)/sin(double) ; XFAIL: target=nvptx{{.*}} diff --git a/llvm/test/CodeGen/Generic/invalid-memcpy.ll b/llvm/test/CodeGen/Generic/invalid-memcpy.ll index 1ea95af7ab0c..06258de43f04 100644 --- a/llvm/test/CodeGen/Generic/invalid-memcpy.ll +++ b/llvm/test/CodeGen/Generic/invalid-memcpy.ll @@ -1,5 +1,7 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + ; This testcase is invalid (the alignment specified for memcpy is ; greater than the alignment guaranteed for Qux or C.0.1173), but it ; should compile, not crash the code generator. diff --git a/llvm/test/CodeGen/Generic/isunord.ll b/llvm/test/CodeGen/Generic/isunord.ll index ebbba010793b..60154f7ee110 100644 --- a/llvm/test/CodeGen/Generic/isunord.ll +++ b/llvm/test/CodeGen/Generic/isunord.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}} declare i1 @llvm.isunordered.f64(double, double) diff --git a/llvm/test/CodeGen/Generic/live-debug-label.ll b/llvm/test/CodeGen/Generic/live-debug-label.ll index 3121b8700ed1..28e5a03a6c59 100644 --- a/llvm/test/CodeGen/Generic/live-debug-label.ll +++ b/llvm/test/CodeGen/Generic/live-debug-label.ll @@ -10,6 +10,9 @@ ; XFAIL: target=riscv{{.*}} ; XFAIL: target=amdgcn-{{.*}} +; The LiveDebugValuesID pass that handles DBG_LABEL instructions is disabled for EVM. +; XFAIL: target=evm{{.*}} + ; Generated with "clang++ -g -O1 -S -emit-llvm" ; ; inline bool bar(char c) { diff --git a/llvm/test/CodeGen/Generic/llc-start-stop.ll b/llvm/test/CodeGen/Generic/llc-start-stop.ll index b02472473a00..3bf01dc0aa09 100644 --- a/llvm/test/CodeGen/Generic/llc-start-stop.ll +++ b/llvm/test/CodeGen/Generic/llc-start-stop.ll @@ -1,5 +1,7 @@ ; NVPTX customizes the list of passes so the test cannot find what it expects ; XFAIL: target=nvptx{{.*}} +; EVM customizes the list of passes so the test cannot find what it expects +; XFAIL: target=evm{{.*}} ; Note: -verify-machineinstrs is used in order to make this test compatible with EXPENSIVE_CHECKS. ; RUN: llc < %s -debug-pass=Structure -stop-after=loop-reduce -verify-machineinstrs -o /dev/null 2>&1 \ diff --git a/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll b/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll index aee06435cdfb..345a019098e7 100644 --- a/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll +++ b/llvm/test/CodeGen/Generic/llvm-ct-intrinsics.ll @@ -1,6 +1,9 @@ ; Make sure this testcase is supported by all code generators ; RUN: llc < %s +; TODO: Check cttz, ctlz, ctpop, ConstantPool lowering. +; XFAIL: target=evm{{.*}} + declare i64 @llvm.ctpop.i64(i64) declare i32 @llvm.ctpop.i32(i32) diff --git a/llvm/test/CodeGen/Generic/negintconst.ll b/llvm/test/CodeGen/Generic/negintconst.ll index c357765636fe..7e0b3c2e81f1 100644 --- a/llvm/test/CodeGen/Generic/negintconst.ll +++ b/llvm/test/CodeGen/Generic/negintconst.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; Test that a negative constant smaller than 64 bits (e.g., int) ; is correctly implemented with sign-extension. diff --git a/llvm/test/CodeGen/Generic/pr12507.ll b/llvm/test/CodeGen/Generic/pr12507.ll index 80bfecf98417..1a2848ef6f13 100644 --- a/llvm/test/CodeGen/Generic/pr12507.ll +++ b/llvm/test/CodeGen/Generic/pr12507.ll @@ -1,7 +1,7 @@ ; RUN: llc < %s ; NVPTX failed to lower arg i160, as size > 64 -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} @c = external global i32, align 4 diff --git a/llvm/test/CodeGen/Generic/pr2625.ll b/llvm/test/CodeGen/Generic/pr2625.ll index e54862438896..def48e43d598 100644 --- a/llvm/test/CodeGen/Generic/pr2625.ll +++ b/llvm/test/CodeGen/Generic/pr2625.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + ; PR2625 define i32 @main(ptr) { diff --git a/llvm/test/CodeGen/Generic/pr33094.ll b/llvm/test/CodeGen/Generic/pr33094.ll index 65089a39c756..072d764d0d19 100644 --- a/llvm/test/CodeGen/Generic/pr33094.ll +++ b/llvm/test/CodeGen/Generic/pr33094.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; PR33094 ; Make sure that a constant extractvalue doesn't cause a crash in diff --git a/llvm/test/CodeGen/Generic/print-add.ll b/llvm/test/CodeGen/Generic/print-add.ll index c1d776857bb8..30333083f1f2 100644 --- a/llvm/test/CodeGen/Generic/print-add.ll +++ b/llvm/test/CodeGen/Generic/print-add.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [4 x i8] c"%d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-arith-fp.ll b/llvm/test/CodeGen/Generic/print-arith-fp.ll index 87a2e2ee3d35..42e8e758c278 100644 --- a/llvm/test/CodeGen/Generic/print-arith-fp.ll +++ b/llvm/test/CodeGen/Generic/print-arith-fp.ll @@ -1,3 +1,5 @@ +; EVM doesn't support floats. +; UNSUPPORTED: target=evm{{.*}}, target=evm{{.*}} ; RUN: llc < %s @a_str = internal constant [8 x i8] c"a = %f\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %f\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-arith-int.ll b/llvm/test/CodeGen/Generic/print-arith-int.ll index 401e789ea190..3dd624c150bf 100644 --- a/llvm/test/CodeGen/Generic/print-arith-int.ll +++ b/llvm/test/CodeGen/Generic/print-arith-int.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %d\0A\00" ; [#uses=1] @add_str = internal constant [12 x i8] c"a + b = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-int.ll b/llvm/test/CodeGen/Generic/print-int.ll index c6291ab47972..1468b8f6a4a0 100644 --- a/llvm/test/CodeGen/Generic/print-int.ll +++ b/llvm/test/CodeGen/Generic/print-int.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @.str_1 = internal constant [4 x i8] c"%d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-mul-exp.ll b/llvm/test/CodeGen/Generic/print-mul-exp.ll index 6e17a23047bc..f66d142ab82a 100644 --- a/llvm/test/CodeGen/Generic/print-mul-exp.ll +++ b/llvm/test/CodeGen/Generic/print-mul-exp.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @a_mul_str = internal constant [13 x i8] c"a * %d = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-mul.ll b/llvm/test/CodeGen/Generic/print-mul.ll index 9b3b48966915..99c1482f614c 100644 --- a/llvm/test/CodeGen/Generic/print-mul.ll +++ b/llvm/test/CodeGen/Generic/print-mul.ll @@ -1,4 +1,7 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-921 Needs proger GA wrapping to be implemented. ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/print-shift.ll b/llvm/test/CodeGen/Generic/print-shift.ll index b15dd64681da..348f9919a1f7 100644 --- a/llvm/test/CodeGen/Generic/print-shift.ll +++ b/llvm/test/CodeGen/Generic/print-shift.ll @@ -1,5 +1,9 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-921 Needs proger GA wrapping to be implemented. ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + @a_str = internal constant [8 x i8] c"a = %d\0A\00" ; [#uses=1] @b_str = internal constant [8 x i8] c"b = %d\0A\00" ; [#uses=1] @a_shl_str = internal constant [14 x i8] c"a << %d = %d\0A\00" ; [#uses=1] diff --git a/llvm/test/CodeGen/Generic/ptr-annotate.ll b/llvm/test/CodeGen/Generic/ptr-annotate.ll index 5047593af462..a2d71aded116 100644 --- a/llvm/test/CodeGen/Generic/ptr-annotate.ll +++ b/llvm/test/CodeGen/Generic/ptr-annotate.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; PR15253 diff --git a/llvm/test/CodeGen/Generic/select-cc.ll b/llvm/test/CodeGen/Generic/select-cc.ll index 2b39fc17a55d..7a2eec4bf0fe 100644 --- a/llvm/test/CodeGen/Generic/select-cc.ll +++ b/llvm/test/CodeGen/Generic/select-cc.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vectors. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define <2 x double> @vector_select(<2 x double> %x, <2 x double> %y) nounwind { diff --git a/llvm/test/CodeGen/Generic/select.ll b/llvm/test/CodeGen/Generic/select.ll index 7658f2809de1..8a97eabf261d 100644 --- a/llvm/test/CodeGen/Generic/select.ll +++ b/llvm/test/CodeGen/Generic/select.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} %Domain = type { ptr, i32, ptr, i32, i32, ptr, ptr } @AConst = constant i32 123 ; [#uses=1] @@ -119,21 +120,24 @@ define i1 @boolexpr(i1 %b, i32 %N) { ret i1 %b3 } +; EVM local begin +; EVM doesn't support floats ; Test branch on floating point comparison ; -define void @testfloatbool(float %x, float %y) { - br label %Top - -Top: ; preds = %Top, %0 - %p = fadd float %x, %y ; [#uses=1] - %z = fsub float %x, %y ; [#uses=1] - %b = fcmp ole float %p, %z ; [#uses=2] - %c = xor i1 %b, true ; [#uses=0] - br i1 %b, label %Top, label %goon - -goon: ; preds = %Top - ret void -} +;define void @testfloatbool(float %x, float %y) { +; br label %Top +; +;Top: ; preds = %Top, %0 +; %p = fadd float %x, %y ; [#uses=1] +; %z = fsub float %x, %y ; [#uses=1] +; %b = fcmp ole float %p, %z ; [#uses=2] +; %c = xor i1 %b, true ; [#uses=0] +; br i1 %b, label %Top, label %goon +; +;goon: ; preds = %Top +; ret void +;} +; EVM local end ; Test cases where an LLVM instruction requires no machine @@ -184,10 +188,13 @@ define i32 @checkFoldGEP(ptr %D, i64 %idx) { ret i32 %reg820 } +; EVM local begin +; EVM doesn't support vectors ; Test case for scalarising a 1 element vselect ; -define <1 x i32> @checkScalariseVSELECT(<1 x i32> %a, <1 x i32> %b) { - %cond = icmp uge <1 x i32> %a, %b - %s = select <1 x i1> %cond, <1 x i32> %a, <1 x i32> %b - ret <1 x i32> %s -} +;define <1 x i32> @checkScalariseVSELECT(<1 x i32> %a, <1 x i32> %b) { +; %cond = icmp uge <1 x i32> %a, %b +; %s = select <1 x i1> %cond, <1 x i32> %a, <1 x i32> %b +; ret <1 x i32> %s +;} +; EVM local end diff --git a/llvm/test/CodeGen/Generic/selection-dag-determinism.ll b/llvm/test/CodeGen/Generic/selection-dag-determinism.ll index 1adff3d61ba2..2853957dec1e 100644 --- a/llvm/test/CodeGen/Generic/selection-dag-determinism.ll +++ b/llvm/test/CodeGen/Generic/selection-dag-determinism.ll @@ -7,6 +7,7 @@ ; RUN: cmp %t1.o %t3.o ; RUN: cmp %t1.o %t4.o ; RUN: cmp %t1.o %t5.o +; UNSUPPORTED: target=evm{{.*}} ; Regression test for nondeterminism introduced in https://reviews.llvm.org/D57694 diff --git a/llvm/test/CodeGen/Generic/stacksave-restore.ll b/llvm/test/CodeGen/Generic/stacksave-restore.ll index a2c1ba4d62b0..6be545a2effb 100644 --- a/llvm/test/CodeGen/Generic/stacksave-restore.ll +++ b/llvm/test/CodeGen/Generic/stacksave-restore.ll @@ -1,3 +1,5 @@ +; EVM doesn't support dynamic_stackalloc yet. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; NVPTX can not select llvm.stacksave (dynamic_stackalloc) and llvm.stackrestore diff --git a/llvm/test/CodeGen/Generic/storetrunc-fp.ll b/llvm/test/CodeGen/Generic/storetrunc-fp.ll index b952eced4aab..21ff2dd9cd13 100644 --- a/llvm/test/CodeGen/Generic/storetrunc-fp.ll +++ b/llvm/test/CodeGen/Generic/storetrunc-fp.ll @@ -1,3 +1,5 @@ +; Unsupported because of floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define void @foo(double %a, double %b, ptr %fp) { diff --git a/llvm/test/CodeGen/Generic/trap.ll b/llvm/test/CodeGen/Generic/trap.ll index 67d1a7a347f3..ffd0726caf04 100644 --- a/llvm/test/CodeGen/Generic/trap.ll +++ b/llvm/test/CodeGen/Generic/trap.ll @@ -1,4 +1,8 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-918 Lower trap to panic ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} + define i32 @test() noreturn nounwind { entry: tail call void @llvm.trap( ) diff --git a/llvm/test/CodeGen/Generic/undef-phi.ll b/llvm/test/CodeGen/Generic/undef-phi.ll index 0e221fe612ab..dcdb2abebe81 100644 --- a/llvm/test/CodeGen/Generic/undef-phi.ll +++ b/llvm/test/CodeGen/Generic/undef-phi.ll @@ -1,4 +1,7 @@ +; XFAIL: target=evm{{.*}} +; TODO: CPR-922 Fix ; 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/CodeGen/Generic/v-split.ll b/llvm/test/CodeGen/Generic/v-split.ll index 26db28ef855a..5e404d35fc10 100644 --- a/llvm/test/CodeGen/Generic/v-split.ll +++ b/llvm/test/CodeGen/Generic/v-split.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vectors and floats. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s %f8 = type <8 x float> diff --git a/llvm/test/CodeGen/Generic/vector-casts.ll b/llvm/test/CodeGen/Generic/vector-casts.ll index 76accaa5e550..46908ca3585b 100644 --- a/llvm/test/CodeGen/Generic/vector-casts.ll +++ b/llvm/test/CodeGen/Generic/vector-casts.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vector instructions. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s ; PR2671 diff --git a/llvm/test/CodeGen/Generic/vector-constantexpr.ll b/llvm/test/CodeGen/Generic/vector-constantexpr.ll index 416367fa3b52..4088e173b64e 100644 --- a/llvm/test/CodeGen/Generic/vector-constantexpr.ll +++ b/llvm/test/CodeGen/Generic/vector-constantexpr.ll @@ -1,3 +1,5 @@ +; EVM doesn't support vector instructions. +; UNSUPPORTED: target=evm{{.*}} ; RUN: llc < %s define void @""(ptr %inregs, ptr %outregs) { diff --git a/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll b/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll index 9e3ca2c15744..2732df9f2753 100644 --- a/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll +++ b/llvm/test/CodeGen/Generic/vector-identity-shuffle.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s +; EVM doesn't support vector instructions. +; UNSUPPORTED: target=evm{{.*}} define void @test(ptr %tmp2.i) { diff --git a/llvm/test/CodeGen/Generic/vector.ll b/llvm/test/CodeGen/Generic/vector.ll index 13bc12b8c7ae..88c0ddc98cb2 100644 --- a/llvm/test/CodeGen/Generic/vector.ll +++ b/llvm/test/CodeGen/Generic/vector.ll @@ -1,3 +1,5 @@ +; UNSUPPORTED: target=evm{{.*}} +; EVM doesn't support vector instructions and floats. ; Test that vectors are scalarized/lowered correctly. ; RUN: llc < %s diff --git a/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll b/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll index 337fbb767f36..e972702f6865 100644 --- a/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll +++ b/llvm/test/CodeGen/MLRegAlloc/default-eviction-advisor.ll @@ -9,6 +9,8 @@ ; regalloc-enable-advisor is not enabled for NVPTX ; UNSUPPORTED: target=nvptx{{.*}} +; regalloc-enable-advisor is not enabled for EVM +; UNSUPPORTED: target=evm{{.*}} define void @f2(i64 %lhs, i64 %rhs, i64* %addr) { %sum = add i64 %lhs, %rhs diff --git a/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll b/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll index 75bae3166343..6d193adf6b68 100644 --- a/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll +++ b/llvm/test/CodeGen/MLRegAlloc/default-priority-advisor.ll @@ -8,7 +8,7 @@ ; RUN: llc -O2 -regalloc-enable-priority-advisor=default < %s 2>&1 | FileCheck %s --check-prefix=DEFAULT ; regalloc-enable-priority-advisor is not enabled for NVPTX -; UNSUPPORTED: target=nvptx{{.*}} +; UNSUPPORTED: target=nvptx{{.*}}, target=evm{{.*}} define void @f2(i64 %lhs, i64 %rhs, i64* %addr) { %sum = add i64 %lhs, %rhs diff --git a/llvm/test/DebugInfo/Generic/lit.local.cfg b/llvm/test/DebugInfo/Generic/lit.local.cfg index 7ce6a3d39231..c4d57ec41426 100644 --- a/llvm/test/DebugInfo/Generic/lit.local.cfg +++ b/llvm/test/DebugInfo/Generic/lit.local.cfg @@ -1,4 +1,4 @@ if not config.target_triple: config.unsupported = True -elif config.target_triple.startswith(("nvptx", "xcore")): +elif config.target_triple.startswith(("nvptx", "evm", "xcore")): config.unsupported = True diff --git a/llvm/test/Feature/optnone-llc.ll b/llvm/test/Feature/optnone-llc.ll index 7e6678f9cdc4..cf9801756b4f 100644 --- a/llvm/test/Feature/optnone-llc.ll +++ b/llvm/test/Feature/optnone-llc.ll @@ -7,6 +7,7 @@ ; REQUIRES: asserts, default_triple ; UNSUPPORTED: target=nvptx{{.*}} +; XFAIL: target=evm{{.*}} ; This test verifies that we don't run Machine Function optimizations ; on optnone functions, and that we can turn off FastISel. diff --git a/llvm/test/Linker/subprogram-linkonce-weak.ll b/llvm/test/Linker/subprogram-linkonce-weak.ll index 0d4eae90cfa1..e8e2b242c4f5 100644 --- a/llvm/test/Linker/subprogram-linkonce-weak.ll +++ b/llvm/test/Linker/subprogram-linkonce-weak.ll @@ -6,6 +6,7 @@ ; ; Bug 47131 ; XFAIL: target=sparc{{.*}} +; XFAIL: target=evm{{.*}} ; ; This testcase tests the following flow: ; - File A defines a linkonce version of @foo which has inlined into @bar. diff --git a/llvm/test/MC/AsmParser/include.ll b/llvm/test/MC/AsmParser/include.ll index 3321f0a6a287..733985d59b78 100644 --- a/llvm/test/MC/AsmParser/include.ll +++ b/llvm/test/MC/AsmParser/include.ll @@ -2,6 +2,9 @@ ; UNSUPPORTED: target={{.*}}-zos{{.*}},target=nvptx{{.*}} ; REQUIRES: default_triple +; EVM/EVM doesn't support inline asm (yet). +; XFAIL: target=evm{{.*}}, target=evm{{.*}} + module asm ".include \22module.x\22" define void @f() { diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll index 23d6c5ae1fdb..5ed9fad1cd6c 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll +++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} define i32 @a() { ; CHECK: Function Attrs: nofree nosync nounwind memory(none) diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll index ee8437e8c0f1..c5b46e334293 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll +++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} @x = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll index 7a97498b8f32..041d6c0eddaa 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll +++ b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} @s = external constant i8 diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll index ea6392714bf6..739ea5d9de69 100644 --- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2 ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}} @g = global i32 20 diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 8635f2bbdc49..85a60d81cf36 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Atomic load/store to local doesn't affect whether a function is ; readnone/readonly. diff --git a/llvm/test/Transforms/FunctionAttrs/convergent.ll b/llvm/test/Transforms/FunctionAttrs/convergent.ll index a0f4c07e4337..eed57bdc2793 100644 --- a/llvm/test/Transforms/FunctionAttrs/convergent.ll +++ b/llvm/test/Transforms/FunctionAttrs/convergent.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} define i32 @nonleaf() convergent { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) diff --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll index 7e246c482431..f9c98e4b007c 100644 --- a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -S -o - -passes=function-attrs < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Verify we remove argmemonly/inaccessiblememonly/inaccessiblemem_or_argmemonly ; function attributes when we derive readnone. diff --git a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll index 0f087e1a05f7..ef7a1b73e677 100644 --- a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll +++ b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -S < %s -passes=function-attrs | FileCheck %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} declare void @llvm.sideeffect() diff --git a/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll b/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll index bb9ef9156794..e3d6e4b0bcdb 100644 --- a/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll +++ b/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}} ;; target triple = "amdgcn-amd-amdhsa" target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-ni:7:8" diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll index 7df6132ac6a3..6110f325f00d 100644 --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2 ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} @g = global ptr null ; [#uses=1] diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll index 4432c4f3c541..c6405b8dd182 100644 --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=COMMON,FNATTRS ; RUN: opt -S -passes=attributor-light %s | FileCheck %s --check-prefixes=COMMON,ATTRIBUTOR +; XFAIL: target=evm{{.*}} target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll index a902974fed28..443fcd406e1b 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}} define i32 @leaf() { diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll index de5398f17ce5..7fa7bbcb7021 100644 --- a/llvm/test/Transforms/FunctionAttrs/nosync.ll +++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll index afa9ae31b5fb..def097a0ba2f 100644 --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}} ; TEST 1 define i32 @foo1() { diff --git a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll index c1aa14b88f0a..e92efc2b7ce5 100644 --- a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -S -passes=function-attrs < %s | FileCheck %s +; XFAIL: target=evm{{.*}} define void @f() { ; CHECK: Function Attrs: nofree nosync nounwind diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll index aab1ec1d2954..366acb180936 100644 --- a/llvm/test/Transforms/FunctionAttrs/optnone.ll +++ b/llvm/test/Transforms/FunctionAttrs/optnone.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; XFAIL: target=evm{{.*}} @x = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll index be61990fd627..c3bc3cd956e7 100644 --- a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -S -passes=function-attrs < %s | FileCheck %s +; XFAIL: target=evm{{.*}} @i = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll index 39513976f90d..9cd4ba4fd7ad 100644 --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -2,6 +2,7 @@ ; RUN: opt < %s -passes=function-attrs -S | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt < %s -passes=attributor-light -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s ; RUN: opt < %s -passes=attributor-light-cgscc -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR-CGSCC %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} @x = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/stats.ll b/llvm/test/Transforms/FunctionAttrs/stats.ll index 5f007b4078ff..73bb933b725d 100644 --- a/llvm/test/Transforms/FunctionAttrs/stats.ll +++ b/llvm/test/Transforms/FunctionAttrs/stats.ll @@ -1,4 +1,5 @@ ; RUN: opt -passes=function-attrs -stats -disable-output %s 2>&1 | FileCheck %s +; XFAIL: target=evm{{.*}} ; REQUIRES: asserts diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll index 70926345ce27..30789579b37e 100644 --- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} define void @mustprogress_readnone() mustprogress { ; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll index de2d5e223894..54f8057fb248 100644 --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} define void @nouses-argworn-funrn(ptr writeonly %.aaa) { ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) diff --git a/llvm/test/Transforms/IndVarSimplify/pr38855.ll b/llvm/test/Transforms/IndVarSimplify/pr38855.ll index 0ebad14fe519..f87d3fa7bf59 100644 --- a/llvm/test/Transforms/IndVarSimplify/pr38855.ll +++ b/llvm/test/Transforms/IndVarSimplify/pr38855.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -disable-nounwind-inference=false -passes=inline,function-attrs,indvars < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Check that the invalidation happens correctly and the test does not crash. define void @f2() { diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll index f1121fa88a4b..1122d34b1a58 100644 --- a/llvm/test/Transforms/Inline/cgscc-update.ll +++ b/llvm/test/Transforms/Inline/cgscc-update.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs,inline)' -S | FileCheck %s +; XFAIL: target=evm{{.*}} ; This test runs the inliner and the function attribute deduction. It ensures ; that when the inliner mutates the call graph it correctly updates the CGSCC ; iteration so that we can compute refined function attributes. In this way it diff --git a/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll b/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll index ea2cfe74be26..6f052acda375 100644 --- a/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll +++ b/llvm/test/Transforms/LoopIdiom/X86/memset-size-compute.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom -S %s | FileCheck %s +; XFAIL: * target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx11.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/basic-address-space.ll b/llvm/test/Transforms/LoopIdiom/basic-address-space.ll index de55a46a164e..38e96c721891 100644 --- a/llvm/test/Transforms/LoopIdiom/basic-address-space.ll +++ b/llvm/test/Transforms/LoopIdiom/basic-address-space.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:32:32:32-p1:64:64:64-p2:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:32-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/basic.ll b/llvm/test/Transforms/LoopIdiom/basic.ll index 87247aa76cfc..c84b59413443 100644 --- a/llvm/test/Transforms/LoopIdiom/basic.ll +++ b/llvm/test/Transforms/LoopIdiom/basic.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" ; For @test11_pattern diff --git a/llvm/test/Transforms/LoopIdiom/debug-line.ll b/llvm/test/Transforms/LoopIdiom/debug-line.ll index eccfc38b9f86..ac6ef9b9e901 100644 --- a/llvm/test/Transforms/LoopIdiom/debug-line.ll +++ b/llvm/test/Transforms/LoopIdiom/debug-line.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/disable-options.ll b/llvm/test/Transforms/LoopIdiom/disable-options.ll index 823006113f9b..c84a64fb930a 100644 --- a/llvm/test/Transforms/LoopIdiom/disable-options.ll +++ b/llvm/test/Transforms/LoopIdiom/disable-options.ll @@ -9,6 +9,7 @@ ; RUN: opt -passes="loop-idiom" -aa-pipeline=basic-aa -disable-loop-idiom-memcpy < %s -S | FileCheck %s --check-prefix=DIS-MEMCPY ; RUN: opt -passes="loop-idiom" -aa-pipeline=basic-aa -disable-loop-idiom-memset < %s -S | FileCheck %s --check-prefix=DIS-MEMSET ; RUN: opt -passes="loop-idiom" -aa-pipeline=basic-aa -disable-loop-idiom-memset -disable-loop-idiom-memcpy < %s -S | FileCheck %s --check-prefix=DIS-ALL +; XFAIL: target=evm{{.*}} define void @test-memcpy(i64 %Size) nounwind ssp { ; DIS-NONE-LABEL: @test-memcpy( diff --git a/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll b/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll index 0304e08662d2..ec500e1dc393 100644 --- a/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll +++ b/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll @@ -1,4 +1,5 @@ ; RUN: opt -S < %s -passes=loop-idiom | FileCheck %s +; XFAIL: target=evm{{.*}} declare void @llvm.sideeffect() diff --git a/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll b/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll index 90aa09287fd6..a89c18dbdc6b 100644 --- a/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll +++ b/llvm/test/Transforms/LoopIdiom/lir-heurs-multi-block-loop.ll @@ -1,4 +1,5 @@ ; RUN: opt -passes=loop-idiom -use-lir-code-size-heurs=true < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} ; When compiling for codesize we avoid idiom recognition for a ; multi-block loop unless it is one of diff --git a/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll b/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll index 95859c0b49f4..d08fbf7c072d 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-debugify-remarks.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=debugify,loop-idiom,verify -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -pass-remarks-output=%t.yaml -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 | FileCheck %s ; RUN: FileCheck --input-file=%t.yaml %s --check-prefixes=YAML +; XFAIL: target=evm{{.*}} ; RUN: opt -passes=debugify,loop-idiom,verify -pass-remarks=loop-idiom -pass-remarks-analysis=loop-idiom -pass-remarks-output=%t.yaml -verify-each -verify-dom-info -verify-loop-info < %s -S 2>&1 --try-experimental-debuginfo-iterators | FileCheck %s ; RUN: FileCheck --input-file=%t.yaml %s --check-prefixes=YAML diff --git a/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll b/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll index 0c7d1ec59b2c..7ce951638597 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-pr52104.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} define void @f(ptr nocapture nonnull align 4 dereferenceable(20) %0, i32 %1) local_unnamed_addr #0 align 32 { ; CHECK-LABEL: @f( diff --git a/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll b/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll index c87397aabec7..abbf4ca4af1e 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-runtime-32bit.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="function(loop(indvars,loop-idiom,loop-deletion),simplifycfg)" -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Compile command: ; $ clang -m32 -fno-discard-value-names -O0 -S -emit-llvm -Xclang -disable-O0-optnone Code.c ; $ bin/opt -S -passes=mem2reg,loop-simplify,lcssa,loop-rotate \ diff --git a/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll b/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll index 6ce935044e95..dc0d807104bf 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-runtime-64bit.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="function(loop(indvars,loop-idiom,loop-deletion),simplifycfg)" -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Compile command: ; $ clang -m64 -fno-discard-value-names -O0 -S -emit-llvm -Xclang -disable-O0-optnone Code.c ; $ bin/opt -S -passes=mem2reg,loop-simplify,lcssa,loop-rotate \ diff --git a/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll b/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll index 6b952efb1e6d..64346d958d36 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-runtime-debug.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; REQUIRES: asserts ; RUN: opt < %s -S -debug -passes=loop-idiom 2>&1 | FileCheck %s +; XFAIL: target=evm{{.*}} ; The C code to generate this testcase: ; void test(int *ar, int n, int m) ; { diff --git a/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll b/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll index 61005c2ba20e..bb4be76b65b2 100644 --- a/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll +++ b/llvm/test/Transforms/LoopIdiom/memset-tbaa.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="loop-idiom" < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} define dso_local void @double_memset(ptr nocapture %p) { diff --git a/llvm/test/Transforms/LoopIdiom/memset.ll b/llvm/test/Transforms/LoopIdiom/memset.ll index 8b4f68b62664..ed69f011fce8 100644 --- a/llvm/test/Transforms/LoopIdiom/memset.ll +++ b/llvm/test/Transforms/LoopIdiom/memset.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} define dso_local void @double_memset(ptr nocapture %p, ptr noalias nocapture %q, i32 %n) { diff --git a/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll b/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll index 13ec5150f055..f5edd5bdb766 100644 --- a/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll +++ b/llvm/test/Transforms/LoopIdiom/non-integral-pointers.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -passes=loop-idiom < %s | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:4" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopIdiom/pr70008.ll b/llvm/test/Transforms/LoopIdiom/pr70008.ll index 53ba0c4aaf20..8d65f3ff02e8 100644 --- a/llvm/test/Transforms/LoopIdiom/pr70008.ll +++ b/llvm/test/Transforms/LoopIdiom/pr70008.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 ; RUN: opt -S -passes=loop-idiom < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Make sure we don't assert if the BECount is larger than 64 bits. diff --git a/llvm/test/Transforms/LoopIdiom/pr80954.ll b/llvm/test/Transforms/LoopIdiom/pr80954.ll index 55bdcb44c2c2..73218dedad6d 100644 --- a/llvm/test/Transforms/LoopIdiom/pr80954.ll +++ b/llvm/test/Transforms/LoopIdiom/pr80954.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 ; RUN: opt -S -passes=loop-idiom < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; Make sure this does not assert in SCEVExpander. diff --git a/llvm/test/Transforms/LoopIdiom/reuse-cast.ll b/llvm/test/Transforms/LoopIdiom/reuse-cast.ll index 9d79165c44d2..42c72023072d 100644 --- a/llvm/test/Transforms/LoopIdiom/reuse-cast.ll +++ b/llvm/test/Transforms/LoopIdiom/reuse-cast.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom -S %s | FileCheck %s +; XFAIL: target=evm{{.*}} define void @reuse_cast_1(ptr %ptr, i1 %c) { ; CHECK-LABEL: @reuse_cast_1( diff --git a/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll b/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll index 9794adcaeed6..4708c934ef6e 100644 --- a/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll +++ b/llvm/test/Transforms/LoopIdiom/struct-custom-dl.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:40:64:64:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" %struct.foo = type { i32, i32 } diff --git a/llvm/test/Transforms/LoopIdiom/struct.ll b/llvm/test/Transforms/LoopIdiom/struct.ll index 73d569113f89..a4f98c0f7655 100644 --- a/llvm/test/Transforms/LoopIdiom/struct.ll +++ b/llvm/test/Transforms/LoopIdiom/struct.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll b/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll index ac50c8716c73..c7bf6be014d6 100644 --- a/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll +++ b/llvm/test/Transforms/LoopIdiom/unroll-custom-dl.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" ; CHECK: @.memset_pattern = private unnamed_addr constant [4 x i32] [i32 2, i32 2, i32 2, i32 2], align 16 diff --git a/llvm/test/Transforms/LoopIdiom/unroll.ll b/llvm/test/Transforms/LoopIdiom/unroll.ll index 7c41310abdfd..0be99e21f25e 100644 --- a/llvm/test/Transforms/LoopIdiom/unroll.ll +++ b/llvm/test/Transforms/LoopIdiom/unroll.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes=loop-idiom < %s -S | FileCheck %s +; XFAIL: target=evm{{.*}} target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" ; CHECK: @.memset_pattern = private unnamed_addr constant [4 x i32] [i32 2, i32 2, i32 2, i32 2], align 16 diff --git a/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll b/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll index fbb9e2a7b6b8..82b3309a26fb 100644 --- a/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll +++ b/llvm/test/Transforms/LoopStrengthReduce/X86/2011-11-29-postincphi.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s | FileCheck %s +; XFAIL: * ; ; PR11431: handle a phi operand that is replaced by a postinc user. ; LSR first expands %t3 to %t2 in %phi diff --git a/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll b/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll index 39e2d6f1acca..b114460eda82 100644 --- a/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll +++ b/llvm/test/Transforms/LoopStrengthReduce/X86/ivchain-X86.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -O3 -mtriple=x86_64-unknown-unknown -mcpu=core2 | FileCheck %s -check-prefix=X64 ; RUN: llc < %s -O3 -mtriple=i686-unknown-unknown -mcpu=core2 | FileCheck %s -check-prefix=X32 +; XFAIL: target=evm{{.*}} ; @simple is the most basic chain of address induction variables. Chaining ; saves at least one register and avoids complex addressing and setup diff --git a/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll b/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll index 604aba93ee16..46b9940e0e9f 100644 --- a/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll +++ b/llvm/test/Transforms/MergedLoadStoreMotion/st_sink_bugfix_22613.ll @@ -3,6 +3,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" ; RUN: opt -O2 -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} ; CHECK-LABEL: main ; CHECK: memset diff --git a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll index 708cdc9ca45e..38657b21da87 100644 --- a/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll +++ b/llvm/test/Transforms/PhaseOrdering/X86/merge-functions.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes="default" -enable-merge-functions -S < %s | FileCheck %s +; XFAIL: * ; TODO: These two functions should get merged, but currently aren't, because ; the function merging pass is scheduled too early. diff --git a/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll b/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll index 40c42ffdfd10..01ac26d4d5e1 100644 --- a/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll +++ b/llvm/test/Transforms/PhaseOrdering/X86/pr48844-br-to-switch-vectorization.ll @@ -1,6 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -O2 -mattr=avx < %s | FileCheck %s ; RUN: opt -S -passes="default" -mattr=avx < %s | FileCheck %s +; XFAIL: * target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll b/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll index 98b11578b49f..f402e2b2ba21 100644 --- a/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll +++ b/llvm/test/Transforms/PhaseOrdering/enable-loop-header-duplication-oz.ll @@ -8,6 +8,7 @@ ; RUN: opt -passes='default' -S < %s | FileCheck %s --check-prefix=NOROTATION ; RUN: opt -passes='default' -S -enable-loop-header-duplication < %s | FileCheck %s --check-prefix=ROTATION ; RUN: opt -passes='default' -S < %s | FileCheck %s --check-prefix=ROTATION +; XFAIL: target=evm{{.*}} define void @test(i8* noalias nonnull align 1 %start, i8* %end) unnamed_addr { ; NOROTATION-LABEL: define void @test( diff --git a/llvm/test/Transforms/PhaseOrdering/func-attrs.ll b/llvm/test/Transforms/PhaseOrdering/func-attrs.ll index 187e32afe00a..c6baf27522c9 100644 --- a/llvm/test/Transforms/PhaseOrdering/func-attrs.ll +++ b/llvm/test/Transforms/PhaseOrdering/func-attrs.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -O2 -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}}, target=evm{{.*}} declare void @g() diff --git a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll index 489da5a1ed32..3136fe6fce41 100644 --- a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll +++ b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes='default' -S < %s | FileCheck %s +; XFAIL: target=evm{{.*}} define void @PR47852(ptr noundef %d, i32 noundef %c) { ; CHECK-LABEL: @PR47852( diff --git a/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll b/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll index 413406f921be..ba353c525736 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/MagicPointer.ll @@ -1,6 +1,7 @@ ; Test that simplifycfg can create switch instructions from constant pointers. ; ; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s +; XFAIL: * target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-apple-darwin10.0.0" diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll index 7c97827d108d..539a6bbfe428 100644 --- a/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch-to-lookup-globals.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: -p ; RUN: opt -S -passes="simplifycfg" < %s | FileCheck %s +; XFAIL: * target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py index fe1262893212..1c39017089e9 100644 --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -479,7 +479,7 @@ def have_cxx_shared_library(): if config.target_triple: config.available_features.add("default_triple") # Direct object generation - if not config.target_triple.startswith(("nvptx", "xcore")): + if not config.target_triple.startswith(("nvptx", "evm", "xcore")): config.available_features.add("object-emission") if config.have_llvm_driver: diff --git a/llvm/test/tools/llvm-reduce/file-output-type.test b/llvm/test/tools/llvm-reduce/file-output-type.test index 93b9ca4acb08..74d280d07803 100644 --- a/llvm/test/tools/llvm-reduce/file-output-type.test +++ b/llvm/test/tools/llvm-reduce/file-output-type.test @@ -1,3 +1,5 @@ +# TODO: CPR-1629 EVM does not implement MachineFunctionInfo cloning +# XFAIL: target=evm{{.*}} # REQUIRES: default_triple # RUN: rm -rf %t.dir && mkdir %t.dir && cd %t.dir diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index 373a58d259af..42d0dd896d91 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -435,7 +435,13 @@ void TestAllForms() { EXPECT_EQ(AddrValue, toAddress(DieDG.find(Attr_Last), 0)); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; @@ -444,7 +450,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version2Addr4AllForms) { TestAllForms<2, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint64_t AddrType; @@ -453,7 +465,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version2Addr8AllForms) { TestAllForms<2, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; @@ -462,7 +480,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version3Addr4AllForms) { TestAllForms<3, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; @@ -471,7 +495,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version3Addr8AllForms) { TestAllForms<3, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; @@ -480,7 +510,13 @@ TEST(DWARFDebugInfo, TestDWARF32Version4Addr4AllForms) { TestAllForms<4, AddrType, RefAddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8AllForms) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8AllForms) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; @@ -489,7 +525,9 @@ TEST(DWARFDebugInfo, TestDWARF32Version4Addr8AllForms) { TestAllForms<4, AddrType, RefAddrType>(); } -#ifdef NO_SUPPORT_DEBUG_STR_OFFSETS +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_STR_OFFSETS) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version5Addr4AllForms) { #else TEST(DWARFDebugInfo, TestDWARF32Version5Addr4AllForms) { @@ -502,7 +540,9 @@ TEST(DWARFDebugInfo, TestDWARF32Version5Addr4AllForms) { TestAllForms<5, AddrType, RefAddrType>(); } -#ifdef NO_SUPPORT_DEBUG_STR_OFFSETS +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_STR_OFFSETS) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version5Addr8AllForms) { #else TEST(DWARFDebugInfo, TestDWARF32Version5Addr8AllForms) { @@ -603,42 +643,78 @@ template void TestChildren() { EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; TestChildren<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 8 byte // addresses. typedef uint64_t AddrType; TestChildren<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; TestChildren<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; TestChildren<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; TestChildren<4, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8Children) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8Children) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; @@ -853,42 +929,78 @@ template void TestReferences() { toDebugInfoReference(CU2ToCU1RefAddrDieDG.find(DW_AT_type), -1ULL)); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; TestReferences<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 2, with 8 byte // addresses. typedef uint64_t AddrType; TestReferences<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; TestReferences<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; TestReferences<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; TestReferences<4, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8References) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8References) { +#endif +// EVM local end // Test that we can decode all forms for DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; @@ -1025,49 +1137,87 @@ template void TestAddresses() { EXPECT_EQ(HighPC, ActualHighPC); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr4Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 2, with 4 byte // addresses. typedef uint32_t AddrType; TestAddresses<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version2Addr8Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version2Addr8Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 2, with 8 byte // addresses. typedef uint64_t AddrType; TestAddresses<2, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr4Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr4Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 3, with 4 byte // addresses. typedef uint32_t AddrType; TestAddresses<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version3Addr8Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version3Addr8Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 3, with 8 byte // addresses. typedef uint64_t AddrType; TestAddresses<3, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr4Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr4Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 4, with 4 byte // addresses. typedef uint32_t AddrType; TestAddresses<4, AddrType>(); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARF32Version4Addr8Addresses) { +#else TEST(DWARFDebugInfo, TestDWARF32Version4Addr8Addresses) { +#endif +// EVM local end // Test that we can decode address values in DWARF32, version 4, with 8 byte // addresses. typedef uint64_t AddrType; TestAddresses<4, AddrType>(); } -#ifdef NO_SUPPORT_DEBUG_STR_OFFSETS +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_STR_OFFSETS) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestStringOffsets) { #else TEST(DWARFDebugInfo, TestStringOffsets) { @@ -1078,6 +1228,7 @@ TEST(DWARFDebugInfo, TestStringOffsets) { const char *String1 = "Hello"; const char *String2 = "World"; +// EVM local end auto ExpectedDG = dwarfgen::Generator::create(Triple, 5); ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded()); @@ -1136,7 +1287,9 @@ TEST(DWARFDebugInfo, TestStringOffsets) { EXPECT_STREQ(String1, *Extracted3); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestEmptyStringOffsets) { #else TEST(DWARFDebugInfo, TestEmptyStringOffsets) { @@ -1170,7 +1323,13 @@ TEST(DWARFDebugInfo, TestEmptyStringOffsets) { DwarfContext->getDWARFObj().getStrOffsetsSection().Data.empty()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestRelations) { +#else TEST(DWARFDebugInfo, TestRelations) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1357,7 +1516,13 @@ TEST(DWARFDebugInfo, TestDWARFDie) { EXPECT_FALSE(DefaultDie.getSibling().isValid()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestChildIterators) { +#else TEST(DWARFDebugInfo, TestChildIterators) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1422,7 +1587,13 @@ TEST(DWARFDebugInfo, TestChildIterators) { EXPECT_EQ(A.begin(), A.end()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestChildIteratorsOnInvalidDie) { +#else TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) { +#endif +// EVM local end // Verify that an invalid DIE has no children. DWARFDie Invalid; auto begin = Invalid.begin(); @@ -1432,7 +1603,13 @@ TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) { EXPECT_EQ(begin, end); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestEmptyChildren) { +#else TEST(DWARFDebugInfo, TestEmptyChildren) { +#endif +// EVM local end const char *yamldata = "debug_abbrev:\n" " - Table:\n" " - Code: 0x00000001\n" @@ -1466,7 +1643,13 @@ TEST(DWARFDebugInfo, TestEmptyChildren) { EXPECT_EQ(CUDie.begin(), CUDie.end()); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestAttributeIterators) { +#else TEST(DWARFDebugInfo, TestAttributeIterators) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1528,7 +1711,13 @@ TEST(DWARFDebugInfo, TestAttributeIterators) { EXPECT_EQ(E, ++I); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestFindRecurse) { +#else TEST(DWARFDebugInfo, TestFindRecurse) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1618,7 +1807,13 @@ TEST(DWARFDebugInfo, TestFindRecurse) { EXPECT_EQ(AbsDieName, StringOpt.value_or(nullptr)); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestSelfRecursiveType) { +#else TEST(DWARFDebugInfo, TestSelfRecursiveType) { +#endif +// EVM local end typedef uint32_t AddrType; Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType)); if (!isConfigurationSupported(Triple)) @@ -1654,7 +1849,13 @@ TEST(DWARFDebugInfo, TestSelfRecursiveType) { } } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDwarfToFunctions) { +#else TEST(DWARFDebugInfo, TestDwarfToFunctions) { +#endif +// EVM local end // Test all of the dwarf::toXXX functions that take a // std::optional and extract the values from it. uint64_t InvalidU64 = 0xBADBADBADBADBADB; @@ -1864,7 +2065,13 @@ TEST(DWARFDebugInfo, TestDwarfToFunctions) { // Test } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestFindAttrs) { +#else TEST(DWARFDebugInfo, TestFindAttrs) { +#endif +// EVM local end Triple Triple = getNormalizedDefaultTargetTriple(); if (!isConfigurationSupported(Triple)) GTEST_SKIP(); @@ -1927,7 +2134,9 @@ TEST(DWARFDebugInfo, TestFindAttrs) { EXPECT_EQ(DieMangled, toString(NameOpt, "")); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST(DWARFDebugInfo, DISABLED_TestImplicitConstAbbrevs) { #else TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { @@ -2055,7 +2264,13 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2); } +// EVM local begin +#if defined(_EVM) +TEST(DWARFDebugInfo, DISABLED_TestDWARFDieRangeInfoContains) { +#else TEST(DWARFDebugInfo, TestDWARFDieRangeInfoContains) { +#endif +// EVM local end DWARFVerifier::DieRangeInfo Empty; ASSERT_TRUE(Empty.contains(Empty)); diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp index 980b627625ee..30b804bbc883 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -186,7 +186,13 @@ void checkDefaultPrologue(uint16_t Version, DwarfFormat Format, EXPECT_STREQ(*toString(Prologue.FileNames[0].Name), "a file"); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_GetOrParseLineTableAtInvalidOffset) { +#else TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); generate(); @@ -208,7 +214,14 @@ TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) { "offset 0x00000001 is not a valid debug line section offset")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_GetOrParseLineTableAtInvalidOffsetAfterData) { +#else TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -229,7 +242,9 @@ TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) { "offset 0x00000001 is not a valid debug line section offset")); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_PrologueGetLength) { #else TEST_P(DebugLineParameterisedFixture, PrologueGetLength) { @@ -256,7 +271,9 @@ TEST_P(DebugLineParameterisedFixture, PrologueGetLength) { EXPECT_EQ((*ExpectedLineTable)->Prologue.getLength(), ExpectedLength); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_GetOrParseLineTableValidTable) { #else TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) { @@ -326,7 +343,9 @@ TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) { // correctly. } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_ClearLineValidTable) { #else TEST_P(DebugLineParameterisedFixture, ClearLineValidTable) { @@ -402,7 +421,13 @@ TEST_P(DebugLineParameterisedFixture, ClearLineValidTable) { EXPECT_EQ(Expected4->Sequences.size(), 2u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForReservedLength) { +#else TEST_F(DebugLineBasicFixture, ErrorForReservedLength) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -425,7 +450,14 @@ struct DebugLineUnsupportedVersionFixture : public TestWithParam, uint16_t Version; }; +// EVM local begin +#if defined(_EVM) +TEST_P(DebugLineUnsupportedVersionFixture, + DISABLED_ErrorForUnsupportedVersion) { +#else TEST_P(DebugLineUnsupportedVersionFixture, ErrorForUnsupportedVersion) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -447,7 +479,9 @@ INSTANTIATE_TEST_SUITE_P(UnsupportedVersionTestParams, Values(/*1 below min */ 1, /* 1 above max */ 6, /* Maximum possible */ 0xffff)); -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_ErrorForInvalidV5IncludeDirTable) { #else TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { @@ -492,7 +526,9 @@ TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { "found")); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooLargePrologueLength) { #else TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { @@ -532,7 +568,9 @@ TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { .str())); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooShortPrologueLength) { #else TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) { @@ -590,7 +628,14 @@ INSTANTIATE_TEST_SUITE_P( std::make_pair(4, DWARF64), // Test v4 fields and DWARF64. std::make_pair(5, DWARF32), std::make_pair(5, DWARF64))); +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForExtendedOpcodeLengthSmallerThanExpected) { +#else TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -619,7 +664,14 @@ TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForExtendedOpcodeLengthLargerThanExpected) { +#else TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -648,7 +700,13 @@ TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForUnitLengthTooLarge) { +#else TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -677,7 +735,13 @@ TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) { EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForMismatchedAddressSize) { +#else TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { +#endif +// EVM local end if (!setupGenerator(4, 8)) GTEST_SKIP(); @@ -706,8 +770,15 @@ TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForMismatchedAddressSizeUnsetInitialAddress) { +#else TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSizeUnsetInitialAddress) { +#endif +// EVM local end if (!setupGenerator(4, 0)) GTEST_SKIP(); @@ -733,8 +804,15 @@ TEST_F(DebugLineBasicFixture, EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ErrorForUnsupportedAddressSizeInSetAddressLength) { +#else TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeInSetAddressLength) { +#endif +// EVM local end // Use DWARF v4, and 0 for data extractor address size so that the address // size is derived from the opcode length. if (!setupGenerator(4, 0)) @@ -766,7 +844,13 @@ TEST_F(DebugLineBasicFixture, EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ErrorForAddressSizeGreaterThanByteSize) { +#else TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) { +#endif +// EVM local end // Use DWARF v4, and 0 for data extractor address size so that the address // size is derived from the opcode length. if (!setupGenerator(4, 0)) @@ -790,7 +874,9 @@ TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) { ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded()); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_ErrorForUnsupportedAddressSizeDefinedInHeader) { #else @@ -834,7 +920,13 @@ TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeDefinedInHeader) { EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_CallbackUsedForUnterminatedSequence) { +#else TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1048,7 +1140,13 @@ struct OpIndexFixture : Test, CommonFixture { } }; +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_OpIndexAdvance) { +#else TEST_F(OpIndexFixture, OpIndexAdvance) { +#endif +// EVM local end if (!setupGenerator(4, 4)) GTEST_SKIP(); @@ -1115,7 +1213,13 @@ TEST_F(OpIndexFixture, OpIndexAdvance) { VerifyRow((*Table)->Rows[5], 0x50, 4, 110); } +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_OpIndexReset) { +#else TEST_F(OpIndexFixture, OpIndexReset) { +#endif +// EVM local end if (!setupGenerator(4, 4)) GTEST_SKIP(); @@ -1178,7 +1282,13 @@ TEST_F(OpIndexFixture, OpIndexReset) { EXPECT_EQ((*Table)->Rows[7].OpIndex, 0u); } +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_MaxOpsZeroDwarf3) { +#else TEST_F(OpIndexFixture, MaxOpsZeroDwarf3) { +#endif +// EVM local end if (!setupGenerator(3, 4)) GTEST_SKIP(); @@ -1195,7 +1305,13 @@ TEST_F(OpIndexFixture, MaxOpsZeroDwarf3) { ASSERT_THAT_EXPECTED(Table, Succeeded()); } +// EVM local begin +#if defined(_EVM) +TEST_F(OpIndexFixture, DISABLED_MaxOpsZeroDwarf4) { +#else TEST_F(OpIndexFixture, MaxOpsZeroDwarf4) { +#endif +// EVM local end if (!setupGenerator(4, 4)) GTEST_SKIP(); @@ -1247,7 +1363,13 @@ struct LineRangeFixture : TestWithParam>, uint8_t LineRange; }; +// EVM local begin +#if defined(_EVM) +TEST_P(LineRangeFixture, DISABLED_LineRangeProblemsReportedCorrectly) { +#else TEST_P(LineRangeFixture, LineRangeProblemsReportedCorrectly) { +#endif +// EVM local end runTest(/*CheckAdvancePC=*/false, "but the prologue line_range value is 0. The address and line will " "not be adjusted"); @@ -1282,7 +1404,13 @@ struct BadMinInstLenFixture : TestWithParam>, uint8_t MinInstLength; }; +// EVM local begin +#if defined(_EVM) +TEST_P(BadMinInstLenFixture, DISABLED_MinInstLengthProblemsReportedCorrectly) { +#else TEST_P(BadMinInstLenFixture, MinInstLengthProblemsReportedCorrectly) { +#endif +// EVM local end runTest(/*CheckAdvancePC=*/true, "but the prologue minimum_instruction_length value is 0, which " "prevents any address advancing"); @@ -1293,7 +1421,13 @@ INSTANTIATE_TEST_SUITE_P( Values(std::make_tuple(0, true), // Test zero value (error). std::make_tuple(1, false))); // Test non-zero value (no error). +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ParserParsesCorrectly) { +#else TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1320,7 +1454,13 @@ TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { EXPECT_FALSE(Unrecoverable); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ParserSkipsCorrectly) { +#else TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1341,7 +1481,13 @@ TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) { EXPECT_FALSE(Unrecoverable); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, DISABLED_ParserAlwaysDoneForEmptySection) { +#else TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1351,7 +1497,14 @@ TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) { EXPECT_TRUE(Parser.done()); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserMarkedAsDoneForBadLengthWhenParsing) { +#else TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1374,7 +1527,14 @@ TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) { "reserved unit length of value 0xfffffff0")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserMarkedAsDoneForBadLengthWhenSkipping) { +#else TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1397,7 +1557,14 @@ TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) { "reserved unit length of value 0xfffffff0")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserReportsFirstErrorInEachTableWhenParsing) { +#else TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1423,7 +1590,14 @@ TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) { "unsupported version 1")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserReportsNonPrologueProblemsWhenParsing) { +#else TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1455,8 +1629,15 @@ TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) { EXPECT_FALSE(Unrecoverable); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserReportsPrologueErrorsInEachTableWhenSkipping) { +#else TEST_F(DebugLineBasicFixture, ParserReportsPrologueErrorsInEachTableWhenSkipping) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1482,7 +1663,14 @@ TEST_F(DebugLineBasicFixture, "unsupported version 1")); } +// EVM local begin +#if defined(_EVM) +TEST_F(DebugLineBasicFixture, + DISABLED_ParserIgnoresNonPrologueErrorsWhenSkipping) { +#else TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); @@ -1498,7 +1686,9 @@ TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) { EXPECT_FALSE(Unrecoverable); } -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_VerboseOutput) { #else TEST_F(DebugLineBasicFixture, VerboseOutput) { @@ -1664,7 +1854,9 @@ struct TruncatedPrologueFixture StringRef ExpectedErr; }; -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_P(TruncatedPrologueFixture, DISABLED_ErrorForTruncatedPrologue) { #else TEST_P(TruncatedPrologueFixture, ErrorForTruncatedPrologue) { @@ -1845,7 +2037,14 @@ struct TruncatedExtendedOpcodeFixture uint64_t OpcodeLength; }; +// EVM local begin +#if defined(_EVM) +TEST_P(TruncatedExtendedOpcodeFixture, + DISABLED_ErrorForTruncatedExtendedOpcode) { +#else TEST_P(TruncatedExtendedOpcodeFixture, ErrorForTruncatedExtendedOpcode) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); LineTable < = setupTable(); @@ -1924,7 +2123,14 @@ INSTANTIATE_TEST_SUITE_P( "unexpected end of data at offset 0x35 while reading [0x32, " "0x36)"))); +// EVM local begin +#if defined(_EVM) +TEST_P(TruncatedStandardOpcodeFixture, + DISABLED_ErrorForTruncatedStandardOpcode) { +#else TEST_P(TruncatedStandardOpcodeFixture, ErrorForTruncatedStandardOpcode) { +#endif +// EVM local end if (!setupGenerator()) GTEST_SKIP(); LineTable < = setupTable(); @@ -1980,7 +2186,9 @@ INSTANTIATE_TEST_SUITE_P( "unable to decode LEB128 at offset 0x00000032: " "malformed uleb128, extends past end"))); -#ifdef NO_SUPPORT_DEBUG_ADDR +// EVM local begin +#if defined(NO_SUPPORT_DEBUG_ADDR) || defined(_EVM) +// EVM local end TEST_F(DebugLineBasicFixture, DISABLED_PrintPathsProperly) { #else TEST_F(DebugLineBasicFixture, PrintPathsProperly) { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp index ea54f321e441..2ba1672c9106 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDieManualExtractTest.cpp @@ -21,7 +21,11 @@ using namespace utils; namespace { +#if defined(_EVM) +TEST(DWARFDie, DISABLED_manualExtractDump) { +#else TEST(DWARFDie, manualExtractDump) { +#endif typedef uint32_t AddrType; uint16_t Version = 4; Triple Triple = getDefaultTargetTripleForAddrSize(sizeof(AddrType)); diff --git a/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp b/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp index ffa3fdaf6e7e..ef8d9c037f67 100644 --- a/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp +++ b/llvm/unittests/Passes/PassBuilderBindings/PassBuilderBindingsTest.cpp @@ -55,7 +55,11 @@ class PassBuilderCTest : public testing::Test { LLVMContextRef Context; }; +#if defined(_EVM) +TEST_F(PassBuilderCTest, DISABLED_Basic) { +#else TEST_F(PassBuilderCTest, Basic) { +#endif LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions(); LLVMPassBuilderOptionsSetLoopUnrolling(Options, 1); LLVMPassBuilderOptionsSetVerifyEach(Options, 1); @@ -69,7 +73,11 @@ TEST_F(PassBuilderCTest, Basic) { LLVMDisposePassBuilderOptions(Options); } +#if defined(_EVM) +TEST_F(PassBuilderCTest, DISABLED_InvalidPassIsError) { +#else TEST_F(PassBuilderCTest, InvalidPassIsError) { +#endif LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions(); LLVMErrorRef E1 = LLVMRunPasses(Module, "", TM, Options); LLVMErrorRef E2 = LLVMRunPasses(Module, "does-not-exist-pass", TM, Options); diff --git a/llvm/unittests/TargetParser/TripleTest.cpp b/llvm/unittests/TargetParser/TripleTest.cpp index ae5df4621df9..6811dcdc4402 100644 --- a/llvm/unittests/TargetParser/TripleTest.cpp +++ b/llvm/unittests/TargetParser/TripleTest.cpp @@ -72,7 +72,11 @@ TEST(TripleTest, BasicParsing) { EXPECT_EQ("d", T.getEnvironmentName().str()); } +#if defined(_EVM) +TEST(TripleTest, DISABLED_ParsedIDs) { +#else TEST(TripleTest, ParsedIDs) { +#endif Triple T; T = Triple("i386-apple-darwin"); @@ -1233,6 +1237,20 @@ TEST(TripleTest, ParsedIDs) { EXPECT_TRUE(T.isTime64ABI()); EXPECT_TRUE(T.isHardFloatABI()); + // EVM local begin + T = Triple("evm-unknown-unknown"); + EXPECT_EQ(Triple::evm, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("evm"); + EXPECT_EQ(Triple::evm, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + // EVM local end + T = Triple("huh"); EXPECT_EQ(Triple::UnknownArch, T.getArch()); } @@ -1257,7 +1275,11 @@ static std::string Join(StringRef A, StringRef B, StringRef C, StringRef D) { return Str; } +#if defined(_EVM) +TEST(TripleTest, DISABLED_Normalization) { +#else TEST(TripleTest, Normalization) { +#endif EXPECT_EQ("unknown", Triple::normalize("")); EXPECT_EQ("unknown-unknown", Triple::normalize("-")); @@ -1431,7 +1453,13 @@ TEST(TripleTest, MutateName) { EXPECT_EQ("i386-pc-darwin", T.getTriple()); } +// EVM local begin +#if defined(_EVM) +TEST(TripleTest, DISABLED_BitWidthChecks) { +#else TEST(TripleTest, BitWidthChecks) { +#endif +// EVM local end Triple T; EXPECT_FALSE(T.isArch16Bit()); EXPECT_FALSE(T.isArch32Bit()); @@ -1623,9 +1651,21 @@ TEST(TripleTest, BitWidthChecks) { EXPECT_FALSE(T.isArch16Bit()); EXPECT_TRUE(T.isArch32Bit()); EXPECT_FALSE(T.isArch64Bit()); + + // EVM local begin + T.setArch(Triple::evm); + EXPECT_FALSE(T.isArch16Bit()); + EXPECT_FALSE(T.isArch32Bit()); + EXPECT_FALSE(T.isArch64Bit()); + EXPECT_TRUE(T.isEVM()); + // EVM local end } +#if defined(_EVM) +TEST(TripleTest, DISABLED_BitWidthArchVariants) { +#else TEST(TripleTest, BitWidthArchVariants) { +#endif Triple T; EXPECT_EQ(Triple::UnknownArch, T.get32BitArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); @@ -1827,7 +1867,11 @@ TEST(TripleTest, BitWidthArchVariants) { EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); } +#if defined(_EVM) +TEST(TripleTest, DISABLED_EndianArchVariants) { +#else TEST(TripleTest, EndianArchVariants) { +#endif Triple T; EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.getLittleEndianArchVariant().getArch()); @@ -1978,6 +2022,11 @@ TEST(TripleTest, EndianArchVariants) { EXPECT_TRUE(T.isLittleEndian()); EXPECT_EQ(Triple::UnknownArch, T.getBigEndianArchVariant().getArch()); EXPECT_EQ(Triple::dxil, T.getLittleEndianArchVariant().getArch()); + + // EVM local begin + T.setArch(Triple::evm); + EXPECT_FALSE(T.isLittleEndian()); + // EVM local end } TEST(TripleTest, XROS) { From 311208802587fea2227bd9b0bd38c24785df973f Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 28 Mar 2024 13:13:56 +0200 Subject: [PATCH 057/203] Add build scripts and CI workflows --- .github/PULL_REQUEST_TEMPLATE.md | 138 +++++++ .github/lnt-benchmarks.yml | 25 ++ .../benchmarks-integration-tests.yml | 142 +++++++ .github/workflows/cache-regen.yml | 68 ++++ .github/workflows/coverage.yml | 237 +++++++++++ .github/workflows/forge-benchmarks.yaml | 43 ++ .github/workflows/regression-tests.yml | 197 ++++++++++ .github/workflows/scheduled.yml | 185 +++++++++ README.md | 119 ++++-- .../clang-tidy/tool/clang-tidy-diff_Zegar.py | 347 +++++++++++++++++ codecov.yml | 9 + infra/docker-compose.yml | 10 + llvm/.clang-tidy | 3 + llvm/test/CMakeLists.txt | 53 +++ llvm/unittests/Support/CommandLineTest.cpp | 5 + llvm/utils/git/code-format-helper-era-llvm.py | 367 ++++++++++++++++++ .../git/requirements_formatting_era_llvm.txt | 52 +++ 17 files changed, 1962 insertions(+), 38 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/lnt-benchmarks.yml create mode 100644 .github/workflows/benchmarks-integration-tests.yml create mode 100644 .github/workflows/cache-regen.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/forge-benchmarks.yaml create mode 100644 .github/workflows/regression-tests.yml create mode 100644 .github/workflows/scheduled.yml create mode 100755 clang-tools-extra/clang-tidy/tool/clang-tidy-diff_Zegar.py create mode 100644 codecov.yml create mode 100644 infra/docker-compose.yml create mode 100644 llvm/utils/git/code-format-helper-era-llvm.py create mode 100644 llvm/utils/git/requirements_formatting_era_llvm.txt diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..f7a5a06250ec --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,138 @@ +# Code Review Checklist + + + +## Purpose + + +## Ticket Number + + +## Requirements +- [ ] Have the requirements been met? +- [ ] Have stakeholder(s) approved the change? + +## Implementation +- [ ] Does this code change accomplish what it is supposed to do? +- [ ] Can this solution be simplified? +- [ ] Does this change add unwanted compile-time or run-time dependencies? +- [ ] Could an additional framework, API, library, or service improve the solution? +- [ ] Could we reuse part of LLVM instead of implementing the patch or a part of it? +- [ ] Is the code at the right abstraction level? +- [ ] Is the code modular enough? +- [ ] Can a better solution be found in terms of maintainability, readability, performance, or security? +- [ ] Does similar functionality already exist in the codebase? If yes, why isn’t it reused? +- [ ] Are there any best practices, design patterns or language-specific patterns that could substantially improve this code? + +## Logic Errors and Bugs +- [ ] Can you think of any use case in which the +code does not behave as intended? +- [ ] Can you think of any inputs or external events +that could break the code? + +## Error Handling and Logging +- [ ] Is error handling done the correct way? +- [ ] Should any logging or debugging information +be added or removed? +- [ ] Are error messages user-friendly? +- [ ] Are there enough log events and are they +written in a way that allows for easy +debugging? + +## Maintainability +- [ ] Is the code easy to read? +- [ ] Is the code not repeated (DRY Principle)? +- [ ] Is the code method/class not too long? + +## Dependencies +- [ ] Were updates to documentation, configuration, or readme files made as required by this change? +- [ ] Are there any potential impacts on other parts of the system or backward compatibility? + +## Security +- [ ] Does the code introduce any security vulnerabilities? + +## Performance +- [ ] Do you think this code change decreases +system performance? +- [ ] Do you see any potential to improve the +performance of the code significantly? + +## Testing and Testability +- [ ] Is the code testable? +- [ ] Have automated tests been added, or have related ones been updated to cover the change? + - [ ] For changes to mutable state +- [ ] Do tests reasonably cover the code change (unit/integration/system tests)? + - [ ] Line Coverage + - [ ] Region Coverage + - [ ] Branch Coverage +- [ ] Are there some test cases, input or edge cases +that should be tested in addition? + +## Readability +- [ ] Is the code easy to understand? +- [ ] Which parts were confusing to you and why? +- [ ] Can the readability of the code be improved by +smaller methods? +- [ ] Can the readability of the code be improved by +different function, method or variable names? +- [ ] Is the code located in the right +file/folder/package? +- [ ] Do you think certain methods should be +restructured to have a more intuitive control +flow? +- [ ] Is the data flow understandable? +- [ ] Are there redundant or outdated comments? +- [ ] Could some comments convey the message +better? +- [ ] Would more comments make the code more +understandable? +- [ ] Could some comments be removed by making the code itself more readable? +- [ ] Is there any commented-out code? +- [ ] Have you run a spelling and grammar checker? + +## Documentation +- [ ] Is there sufficient documentation? +- [ ] Is the ReadMe.md file up to date? + +## Best Practices +- [ ] Follow Single Responsibility principle? +- [ ] Are different errors handled correctly? +- [ ] Are errors and warnings logged? +- [ ] Magic values avoided? +- [ ] No unnecessary comments? +- [ ] Minimal nesting used? + +## Experts' Opinion +- [ ] Do you think a specific expert, like a security +expert or a usability expert, should look over +the code before it can be accepted? +- [ ] Will this code change impact different teams, and should they review the change as well? + + diff --git a/.github/lnt-benchmarks.yml b/.github/lnt-benchmarks.yml new file mode 100644 index 000000000000..8d28caa4767e --- /dev/null +++ b/.github/lnt-benchmarks.yml @@ -0,0 +1,25 @@ +name: LNT benchmarks + +on: + push: + branches: + - main + workflow_dispatch: + pull_request: + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + lnt-benchmarks: + # Run only for matter-labs pull requests, ignore for forks + if: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} + uses: matter-labs/era-compiler-ci/.github/workflows/lnt.yml@v1 + secrets: inherit + with: + compiler_llvm_branch: ${{ github.head_ref }} + ccache-key: 'llvm-Linux-X64-gnu' + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name }} + use-dev-machine: ${{ github.event_name != 'push' }} diff --git a/.github/workflows/benchmarks-integration-tests.yml b/.github/workflows/benchmarks-integration-tests.yml new file mode 100644 index 000000000000..c620354bfe77 --- /dev/null +++ b/.github/workflows/benchmarks-integration-tests.yml @@ -0,0 +1,142 @@ +name: Tests + +on: + pull_request: + workflow_dispatch: + inputs: + compiler_tester_reference_rev: + description: "compiler-tester revision to use as a benchmark reference" + required: true + default: "main" + compiler_tester_candidate_rev: + description: "compiler-tester revision to use as a benchmark candidate. Defaults to `main` branch if the `era-compiler-llvm-test` tag doesn't exist" + required: true + default: "era-compiler-llvm-test" + compiler_llvm_reference_branch: + description: "compiler-llvm branch to use as a benchmark reference" + required: true + default: "main" + compiler_llvm_candidate_branch: + description: "compiler-llvm branch to use as a benchmark candidate" + required: true + default: "main" + compiler_llvm_benchmark_mode: + description: "Mode filter for compiler-llvm benchmarks" + required: false + default: "^M^B3" + compiler_llvm_benchmark_path: + description: "Path filter for compiler-llvm benchmarks" + required: false + default: "" + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + # Check for secrets leak in the repository + secrets-scanner: + uses: matter-labs/era-compiler-ci/.github/workflows/secrets-scanner.yaml@v1 + secrets: inherit + + compiler-tester-ref: + runs-on: ubuntu-latest + outputs: + reference-ref: ${{ steps.compiler_tester_ref.outputs.reference-ref }} + candidate-ref: ${{ steps.compiler_tester_ref.outputs.candidate-ref }} + env: + ERA_COMPILER_LLVM_TEST_TAG: era-compiler-llvm-test + ERA_COMPILER_LLVM_REF_DEFAULT: main + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + repository: matter-labs/era-compiler-tester + + - name: Define compiler tester ref + id: compiler_tester_ref + shell: bash + run: | + REFERENCE_REF=${{ inputs.compiler_tester_reference_rev || env.ERA_COMPILER_LLVM_REF_DEFAULT }} + if [ -n "$(git ls-remote --tags --heads --refs origin ${REFERENCE_REF})" ]; then + echo "reference-ref=${REFERENCE_REF}" | tee -a "${GITHUB_OUTPUT}" + else + echo "reference-ref=${{ env.ERA_COMPILER_LLVM_REF_DEFAULT }}" | tee -a "${GITHUB_OUTPUT}" + fi + CANDIDATE_REF=${{ inputs.compiler_tester_candidate_rev || env.ERA_COMPILER_LLVM_TEST_TAG }} + if [ -n "$(git ls-remote --tags --heads --refs origin ${CANDIDATE_REF})" ]; then + echo "candidate-ref=${CANDIDATE_REF}" | tee -a "${GITHUB_OUTPUT}" + else + echo "candidate-ref=${{ env.ERA_COMPILER_LLVM_REF_DEFAULT }}" | tee -a "${GITHUB_OUTPUT}" + fi + + target-machine: + runs-on: ubuntu-latest + outputs: + evm: ${{ steps.evm.outputs.machine || steps.default.outputs.evm }} + eravm: ${{ steps.eravm.outputs.machine || steps.default.outputs.eravm }} + steps: + + - name: Check for EraVM target + id: eravm + if: contains(github.event.pull_request.title, '[eravm]') || contains(github.event.pull_request.title, '[EraVM]') + run: echo "machine=eravm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for EVM target + id: evm + if: contains(github.event.pull_request.title, '[evm]') || contains(github.event.pull_request.title, '[EVM]') + run: echo "machine=evm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for default target + id: default + shell: bash -ex {0} + run: | + if [[ "${{ join(steps.*.outputs.*) }}" == "" ]]; then + echo "eravm=eravm" | tee -a "${GITHUB_OUTPUT}" + echo "evm=evm" | tee -a "${GITHUB_OUTPUT}" + fi + + # Benchmarks workflow call from the era-compiler-ci repository + # This is a common part of the benchmarks workflow for all repositories + # If you would like to make a change to the benchmarks workflow, please do it in the era-compiler-ci repository + benchmarks: + needs: [compiler-tester-ref, target-machine] + uses: matter-labs/era-compiler-ci/.github/workflows/benchmarks.yml@v1 + secrets: inherit + strategy: + fail-fast: false + matrix: + target: '${{ needs.target-machine.outputs.* }}' + toolchain: [ 'ir-llvm' ] + with: + compiler_tester_reference_branch: ${{ needs.compiler-tester-ref.outputs.reference-ref }} + compiler_tester_candidate_branch: ${{ needs.compiler-tester-ref.outputs.candidate-ref }} + compiler_llvm_reference_branch: ${{ github.event.inputs.compiler_llvm_reference_branch || github.event.repository.default_branch }} + compiler_llvm_candidate_branch: ${{ github.event.inputs.compiler_llvm_candidate_branch || github.head_ref }} + compiler_llvm_benchmark_mode: ${{ github.event.inputs.compiler_llvm_benchmark_mode || '^M^B3' }} + compiler_llvm_benchmark_path: ${{ github.event.inputs.compiler_llvm_benchmark_path || '' }} + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name }} # required to properly test forks + target-machine: ${{ matrix.target }} + toolchain: ${{ matrix.toolchain }} + environment: ${{ matrix.target == 'eravm' && 'zk_evm' || 'EVMInterpreter' }} + ccache-key: 'llvm-Linux-X64-gnu' + + # Integration tests workflow call from the era-compiler-ci repository + # This is a common part of the integration tests workflow for all repositories + # If you would like to make a change to the integration tests workflow, please do it in the era-compiler-ci repository + integration-tests: + needs: [compiler-tester-ref, target-machine] + uses: matter-labs/era-compiler-ci/.github/workflows/integration-tests.yaml@v1 + secrets: inherit + strategy: + fail-fast: false + matrix: + target: ${{ needs.target-machine.outputs.* }} + with: + compiler-tester-ref: ${{ needs.compiler-tester-ref.outputs.candidate-ref }} + llvm-ref: ${{ github.event.inputs.compiler_llvm_candidate_branch || github.head_ref || github.event.repository.default_branch }} + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name }} # required to properly test forks + target-machine: ${{ matrix.target }} + ccache-key: 'llvm-Linux-X64-gnu' diff --git a/.github/workflows/cache-regen.yml b/.github/workflows/cache-regen.yml new file mode 100644 index 000000000000..360ba41b8c49 --- /dev/null +++ b/.github/workflows/cache-regen.yml @@ -0,0 +1,68 @@ +name: Cache regen + +on: + workflow_dispatch: + schedule: + - cron: '0 20 * * *' # each day at 8 PM GMT + +# Cancel the workflow if any new changes pushed to a feature branch or the trunk +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + cleanup-ccache: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Clean up cache + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh cache list + gh cache delete --all || true + + regen-ccache: + needs: cleanup-ccache + strategy: + fail-fast: false # finalize testing of all targets even if one failed + matrix: + include: + - name: "Linux x86 gnu" + runner: matterlabs-ci-runner-high-performance + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + target: "x86_64-unknown-linux-gnu" + runs-on: ${{ matrix.runner }} + container: + image: ${{ matrix.image || '' }} # Special workaround to allow matrix builds with optional container + name: ${{ matrix.name }} + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + path: "llvm" + + - name: Build LLVM for tests + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + target-env: ${{ contains(matrix.target, 'musl') && 'musl' || 'gnu' }} + build-type: RelWithDebInfo + save-ccache: 'false' + enable-tests: 'true' + enable-assertions: 'true' + clone-llvm: 'false' + ccache-key: ${{ format('llvm-{0}-{1}-{2}', runner.os, runner.arch, 'gnu') }} + + - name: Build LLVM for benchmarks + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + target-env: ${{ contains(matrix.target, 'musl') && 'musl' || 'gnu' }} + enable-assertions: 'false' + build-type: Release + save-ccache: 'true' + clone-llvm: 'false' + ccache-key: ${{ format('llvm-{0}-{1}-{2}', runner.os, runner.arch, 'gnu') }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000000..6f0f8ef7d775 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,237 @@ +name: Code coverage + +on: + push: + branches: + - main + pull_request: + schedule: + - cron: '0 0 * * 0' # Weekly on Sunday at 00:00 + workflow_dispatch: + +defaults: + run: + shell: bash -ex {0} + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + PROFDATA_FILE: llvm.profdata + LCOV_FILE: codecov.lcov + OUTPUT_HTML_DIR: COVERAGE + +jobs: + + run-with-coverage: + if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} + runs-on: matterlabs-ci-runner-high-performance + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + steps: + + - name: Checkout solx + uses: actions/checkout@v4 + with: + ref: 'main' + repository: matter-labs/solx + submodules: recursive + + # This step is required to checkout submodules + # that are disabled in .gitmodules config + - name: Checkout submodules + run: | + git config --global --add safe.directory '*' + git submodule update --force --depth=1 --recursive --checkout + + - name: Remove llvm submodule + run: rm -rf llvm + + - name: Checkout llvm + uses: actions/checkout@v4 + with: + path: llvm + + - name: Building solc + uses: matter-labs/era-compiler-ci/.github/actions/build-solc@v1 + with: + cmake-build-type: 'Release' + working-dir: 'era-solidity' + upload-testing-binary: false + + - name: Build LLVM with coverage + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + clone-llvm: 'false' + enable-coverage: true + enable-tests: true + enable-assertions: 'false' + ccache-key: ${{ format('llvm-{0}-{1}', runner.os, runner.arch) }} + + - name: Build solx with coverage + uses: matter-labs/era-compiler-ci/.github/actions/build-rust@v1 + env: + BOOST_PREFIX: ${{ github.workspace }}/era-solidity/boost/lib + SOLC_PREFIX: ${{ github.workspace }}/era-solidity/build + with: + exec_name: 'solx' + target: 'x86_64-unknown-linux-gnu' + release-suffix: test + enable-coverage: true + + - name: Running Lit tests + run: | + ninja -C './target-llvm/build-final' verify-llvm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-generic -v || true + ninja -C './target-llvm/build-final' check-llvm-mc-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-unit -v || true + ninja -C './target-llvm/build-final' check-lld-unit -v || true + + - name: Generate coverage reports + run: | + llvm-profdata merge -sparse -o ${PROFDATA_FILE} ./target-llvm/build-final/profiles/*.profraw + llvm-cov show --show-directory-coverage \ + --format=html --output-dir=${OUTPUT_HTML_DIR} \ + -instr-profile=${PROFDATA_FILE} ./releases/test/solx-test + llvm-cov export --format=lcov -instr-profile=${PROFDATA_FILE} \ + ./releases/test/solx-test > ./${LCOV_FILE} + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: 'Coverage HTML' + path: ${{ env.OUTPUT_HTML_DIR }} + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + working-directory: ${{ github.workspace }}/llvm # Mandatory if main repo is in different directory + directory: ${{ github.workspace }} # Mandatory if main repo is in different directory + token: ${{ secrets.CODECOV_TOKEN }} + file: ${{ env.LCOV_FILE }} + slug: ${{ github.repository }} + + # Calculates full code coverage for LLVM + # based on regression AND integration tests together + # Runs for `main` branch by schedule or by request as it takes >3 hours to complete + integration-tests-coverage: + if: ${{ github.event_name != 'pull_request' && github.event_name != 'push' }} + runs-on: matterlabs-ci-runner-highdisk + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:main + env: + BOOST_PREFIX: ${{ github.workspace }}/era-solidity/boost/lib + SOLC_PREFIX: ${{ github.workspace }}/era-solidity/build + TARGET: 'x86_64-unknown-linux-gnu' + steps: + + - name: Checkout compiler-tester + uses: actions/checkout@v4 + with: + ref: 'main' + repository: matter-labs/era-compiler-tester + submodules: recursive + + # This step is required to checkout submodules + # that are disabled in .gitmodules config + - name: Checkout submodules + run: | + git config --global --add safe.directory '*' + git submodule update --init --force --depth=1 --recursive --checkout + + - name: Remove llvm submodule + run: rm -rf llvm + + - name: Checkout llvm + uses: actions/checkout@v4 + with: + path: llvm + + - name: Build LLVM for tester + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + clone-llvm: false + + - name: Build compiler-tester + run: | + cargo build --release --target ${TARGET} --bin 'compiler-tester' + # Clean-up LLVM for the coverage build + rm -rf target-llvm + + - name: Building solc + uses: matter-labs/era-compiler-ci/.github/actions/build-solc@v1 + with: + cmake-build-type: 'Release' + working-dir: 'era-solidity' + upload-testing-binary: false + + - name: Build LLVM with coverage + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + clone-llvm: 'false' + enable-coverage: true + enable-tests: true + enable-assertions: 'false' + ccache-key: ${{ format('llvm-{0}-{1}', runner.os, runner.arch) }} + + - name: Build solx with coverage + env: + CARGO_CHECKOUT_DIR: /usr/local/cargo/git/checkouts + RUSTFLAGS: "-C instrument-coverage" + run: | + cargo build --target ${TARGET} \ + --manifest-path ${CARGO_CHECKOUT_DIR}/solx-*/*/Cargo.toml \ + --target-dir './target-solx/' + + - name: Running Lit tests + run: | + ninja -C './target-llvm/build-final' verify-llvm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-codegen-generic -v || true + ninja -C './target-llvm/build-final' check-llvm-mc-evm -v || true + ninja -C './target-llvm/build-final' check-llvm-unit -v || true + ninja -C './target-llvm/build-final' check-lld-unit -v || true + llvm-profdata merge -sparse -o ${PROFDATA_FILE} ./target-llvm/build-final/profiles/*.profraw + + - name: Run integration tests with coverage + run: | + TMP="${PROFDATA_FILE}.tmp" + for TEST_PATH in tests/solidity/complex/* tests/solidity/simple/* tests/solidity/ethereum/*; do + WORKDIR=$(basename ${TEST_PATH}) + mkdir -p "${GITHUB_WORKSPACE}/${WORKDIR}" + ./target/${TARGET}/release/compiler-tester \ + --target evm \ + --toolchain ir-llvm \ + --solx "./target-solx/${TARGET}/debug/solx" \ + --load-system-contracts ./system-contracts-stable-build \ + --path ${TEST_PATH} \ + --workflow build \ + --verbose + + mv *.profraw "${GITHUB_WORKSPACE}/${WORKDIR}/" || true + du -hs "${GITHUB_WORKSPACE}/${WORKDIR}" + find ${GITHUB_WORKSPACE}/${WORKDIR} -type f -name '*.profraw' -print > profiles.lst + if [[ -f "${PROFDATA_FILE}" ]]; then + llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @profiles.lst + else + llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" @profiles.lst + fi + mv -f "${TMP}" "${PROFDATA_FILE}" + rm -rf "${GITHUB_WORKSPACE}/${WORKDIR}" + done + + - name: Generate coverage reports + run: | + llvm-cov show --show-directory-coverage \ + --format=html --output-dir=${OUTPUT_HTML_DIR} \ + -instr-profile=${PROFDATA_FILE} ./target-solx/${TARGET}/debug/solx + llvm-cov export --format=lcov -instr-profile=${PROFDATA_FILE} \ + ./target-solx/${TARGET}/debug/solx > ./llvm/${LCOV_FILE} + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v4 + with: + name: 'Coverage integration tests HTML' + path: ${{ env.OUTPUT_HTML_DIR }} diff --git a/.github/workflows/forge-benchmarks.yaml b/.github/workflows/forge-benchmarks.yaml new file mode 100644 index 000000000000..6c39025fef2a --- /dev/null +++ b/.github/workflows/forge-benchmarks.yaml @@ -0,0 +1,43 @@ +name: Forge benchmarks + +on: + workflow_dispatch: + inputs: + solx_candidate_branch: + description: 'Solidity candidate branch to use. If empty, the current branch will be used.' + required: false + default: '' + solx_reference_branch: + description: 'Solidity reference branch to use. If empty, the current branch will be used.' + required: false + default: '' + compiler_llvm_candidate_branch: + description: 'LLVM candidate branch to use. If empty, the current branch will be used.' + required: false + default: '' + compiler_llvm_reference_branch: + description: 'LLVM reference branch to use. If empty, the default repository branch will be used.' + required: false + default: 'main' + pull_request: + +permissions: + contents: read + pull-requests: write + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + forge-benchmarks: + uses: matter-labs/era-compiler-ci/.github/workflows/forge-benchmarks.yaml@v1 + secrets: inherit + with: + print-markdown-tables: 'false' # do not print markdown tables to PRs + upload-reports: ${{ github.event_name == 'pull_request' }} + solx_candidate_branch: ${{ inputs.solx_candidate_branch || 'main' }} + solx_reference_branch: ${{ inputs.solx_reference_branch || 'main' }} + compiler_llvm_candidate_branch: ${{ inputs.compiler_llvm_candidate_branch || github.head_ref }} + compiler_llvm_reference_branch: ${{ inputs.compiler_llvm_reference_branch || github.base_ref || github.event.repository.default_branch }} + compiler-llvm-repo: ${{ github.event.pull_request.head.repo.full_name || 'matter-labs/era-compiler-llvm' }} # required to properly test forks diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml new file mode 100644 index 000000000000..f3c2fd6ce63b --- /dev/null +++ b/.github/workflows/regression-tests.yml @@ -0,0 +1,197 @@ +name: Regression tests + +on: + pull_request: + branches: + - main + workflow_dispatch: + inputs: + commits-to-test: + description: "Number of commits to test" + required: true + default: '1' + # For more information about the supported sanitizers in LLVM, see `LLVM_USE_SANITIZER` option in: + # https://www.llvm.org/docs/CMake.html + llvm-sanitizer: + required: false + default: 'Address' + type: string + description: 'A sanitizer to build LLVM with. Possible values are Address, Memory, MemoryWithOrigins, Undefined, Thread, DataFlow, and Address;Undefined' + enable-valgrind: + required: false + default: 'false' + type: string + description: 'Run LLVM regression tests under valgrind.' + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + + target-machine: + runs-on: ubuntu-latest + outputs: + evm: ${{ steps.evm.outputs.machine }} + eravm: ${{ steps.eravm.outputs.machine }} + default: ${{ steps.default.outputs.machine }} + steps: + + - name: Check for EraVM target + id: eravm + if: contains(github.event.pull_request.title, '[eravm]') || contains(github.event.pull_request.title, '[EraVM]') + run: echo "machine=eravm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for EVM target + id: evm + if: contains(github.event.pull_request.title, '[evm]') || contains(github.event.pull_request.title, '[EVM]') + run: echo "machine=evm" | tee -a "${GITHUB_OUTPUT}" + + - name: Check for default target + id: default + shell: bash -ex {0} + run: | + if [[ "${{ join(steps.*.outputs.*) }}" == "" ]]; then + echo "machine=default" | tee -a "${GITHUB_OUTPUT}" + fi + + prepare-test-matrix: + needs: target-machine + runs-on: ubuntu-latest + outputs: + matrix-formatting: ${{ steps.prepare.outputs.matrix-formatting }} + matrix-tests: ${{ steps.prepare.outputs.matrix-tests }} + fetch_depth: ${{ steps.extract_branch.outputs.fetch_depth }} + + steps: + + - name: Extract branch name + id: extract_branch + run: | + echo "head_ref=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" | tee -a "${GITHUB_OUTPUT}" + echo "base_ref=${{ github.base_ref || github.event.repository.default_branch }}" | tee -a "${GITHUB_OUTPUT}" + echo "fetch_depth=$(( ${{ github.event.pull_request.commits || github.event.inputs.commits-to-test }} + 1 ))" | tee -a "${GITHUB_OUTPUT}" + + - uses: actions/checkout@v4 + with: + ref: ${{ steps.extract_branch.outputs.head_ref }} + fetch-depth: ${{ github.event.pull_request.commits || github.event.inputs.commits-to-test }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - name: Prepare commits + id: prepare + shell: bash -ex {0} + env: + TEST_COMMITS_LIMIT: 25 # Limit the number of commits to test + run: | + COMMITS_TO_TEST=$(git log -n ${{ github.event.pull_request.commits || github.event.inputs.commits-to-test }} --pretty='"%H"' | head -n ${TEST_COMMITS_LIMIT}) + echo "matrix-formatting={ \"commit\": [${COMMITS_TO_TEST//$'\n'/, }] }" | tee -a "${GITHUB_OUTPUT}" + TARGETS=$(echo '${{ toJSON(needs.target-machine.outputs) }}' | jq '.[]') + echo "matrix-tests={ \"commit\": [${COMMITS_TO_TEST//$'\n'/, }], \"target\": [${TARGETS//$'\n'/, }] }" | tee -a "${GITHUB_OUTPUT}" + + check-formatting: + if: github.event_name == 'pull_request' + runs-on: [ci-runner-compiler, Linux] + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + options: -m 110g + needs: prepare-test-matrix + strategy: + fail-fast: false + max-parallel: 3 + matrix: ${{ fromJson(needs.prepare-test-matrix.outputs.matrix-formatting) }} + steps: + + - uses: actions/checkout@v4 + with: + ref: ${{ matrix.commit }} + fetch-depth: ${{ needs.prepare-test-matrix.outputs.fetch_depth }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - name: Define previous commit + id: previous-commit + run: echo "sha=$(git show --quiet --pretty='%H' ${{ matrix.commit }}^1)" | tee -a "${GITHUB_OUTPUT}" + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 + with: + separator: "," + sha: ${{ matrix.commit }} + base_sha: ${{ steps.previous-commit.outputs.sha }} + + - name: Install formatting requirements + run: python3 -m pip install -r ./llvm/utils/git/requirements_formatting_era_llvm.txt + + - name: Run code formatter + run: | + set -x + if [ '${{ github.event.pull_request.head.repo.fork }}' = 'true' ]; then + TOKEN_PARAM="" + else + TOKEN_PARAM="--token ${{ secrets.GITHUB_TOKEN }}" + fi + python3 ./llvm/utils/git/code-format-helper-era-llvm.py ${TOKEN_PARAM} \ + --issue-number ${{ github.event.pull_request.number }} \ + --start-rev ${{ steps.previous-commit.outputs.sha }} \ + --end-rev ${{ matrix.commit }} \ + --changed-files ${{ steps.changed-files.outputs.all_changed_files }} + + regression-tests: + runs-on: [ci-runner-compiler, Linux] + container: + image: ghcr.io/matter-labs/zksync-llvm-runner:latest + options: -m 110g + needs: prepare-test-matrix + strategy: + fail-fast: false + max-parallel: 3 + matrix: ${{ fromJson(needs.prepare-test-matrix.outputs.matrix-tests) }} + steps: + + - uses: actions/checkout@v4 + with: + ref: ${{ matrix.commit }} + fetch-depth: ${{ needs.prepare-test-matrix.outputs.fetch_depth }} + path: "llvm" + repository: ${{ github.event.pull_request.head.repo.full_name }} + + - name: Define previous commit + if: github.event_name == 'pull_request' + working-directory: llvm + id: previous-commit + run: echo "sha=$(git show --quiet --pretty='%H' ${{ matrix.commit }}^1)" | tee -a "${GITHUB_OUTPUT}" + + - name: Build LLVM + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + default-target-triple: ${{ matrix.target }} + enable-tests: true + enable-assertions: true + clone-llvm: false + ccache-key-type: 'static' # rotate ccache key every month + sanitizer: ${{ inputs.llvm-sanitizer }} + enable-valgrind: ${{ inputs.enable-valgrind }} + + # `verify-llvm` is a special target that runs all the regression tests + # it includes `check-llvm` and special codegen tests + # called via `verify-llvm-codegen-opt` and `verify-llvm-codegen-llc` targets + - name: Running Lit tests + run: | + ninja -C './target-llvm/build-final' verify-llvm -v + ninja -C './target-llvm/build-final' check-lld-unit -v + + - name: clang-tidy + if: github.event_name == 'pull_request' + working-directory: llvm + run: | + CHANGES=$(git diff -U0 ${{ steps.previous-commit.outputs.sha }}) + echo "Running clang-tidy on the following changes: ${CHANGES}" + echo "${CHANGES}" | \ + ./clang-tools-extra/clang-tidy/tool/clang-tidy-diff_Zegar.py \ + -p1 -clang-tidy-binary $(which clang-tidy) \ + -path ../target-llvm/build-final/compile_commands.json diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml new file mode 100644 index 000000000000..9d9e59a67531 --- /dev/null +++ b/.github/workflows/scheduled.yml @@ -0,0 +1,185 @@ +name: Scheduled build + +# This workflow is triggered by a schedule or manually +# It allows to build LLVM, run regression tests and integration tests +# for all supported platforms by user's choice +# It also allows to enable assertions and/or valgrind for regression tests +# and regenerates ccache + +on: + schedule: + - cron: '0 0 * * 0' # every week + workflow_dispatch: + inputs: + ref: + description: "Git REF to use for the build" + required: false + type: string + build_macos: + description: "Build for MacOS?" + required: false + type: boolean + default: true + build_linux_amd64: + description: "Build for Linux amd64?" + required: false + type: boolean + default: true + build_linux_arm64: + description: "Build for Linux arm64?" + required: false + type: boolean + default: true + build_windows: + description: "Build for Windows?" + required: false + type: boolean + default: true + run_regression_tests: + description: "Run regression tests?" + required: false + type: boolean + default: false + run_integration_tests: + description: "Run integration tests?" + required: false + type: boolean + default: false + enable-valgrind: + required: false + default: false + type: boolean + description: 'Enable valgrind for regression tests?' + valgrind-options: + required: false + default: '' + type: string + description: 'Space-separated list of additional valgrind options.' + enable_assertions: + description: "Enable assertions?" + required: false + type: boolean + default: true + + +jobs: + + prepare-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.prepare-matrix.outputs.matrix }} + steps: + - name: Prepare matrix + id: prepare-matrix + run: | + # Define general matrix parameters + # Windows is not supported yet on era-compiler-tester side + # WINDOWS='{"name":"Windows-x86","runner":"windows-2019-github-hosted-64core"}' + MACOS_AMD64='{"name":"MacOS-x86","runner":"macos-13-large"}' + MACOS_ARM64='{"name":"MacOS-arm64","runner":["self-hosted","macOS","ARM64"]}' + LINUX_AMD64='{"name":"Linux-AMD64","runner":"matterlabs-ci-runner-high-performance","image":"ghcr.io/matter-labs/zksync-llvm-runner:latest"}' + LINUX_ARM64='{"name":"Linux-ARM64","runner":"matterlabs-ci-runner-arm","image":"ghcr.io/matter-labs/zksync-llvm-runner:latest"}' + # Disable platforms for non-tag builds if user requested + if [ ${GITHUB_EVENT_NAME} = workflow_dispatch ]; then + [ ${{ github.event.inputs.build_windows }} != true ] && WINDOWS= + [ ${{ github.event.inputs.build_macos }} != true ] && MACOS_AMD64= + [ ${{ github.event.inputs.build_macos }} != true ] && MACOS_ARM64= + [ ${{ github.event.inputs.build_linux_amd64 }} != true ] && LINUX_AMD64= + [ ${{ github.event.inputs.build_linux_arm64 }} != true ] && LINUX_ARM64= + fi + PLATFORMS=(${WINDOWS} ${MACOS_AMD64} ${MACOS_ARM64} ${LINUX_AMD64} ${LINUX_ARM64}) + echo "matrix={ \"include\": [ $(IFS=, ; echo "${PLATFORMS[*]}") ] }" | tee -a "${GITHUB_OUTPUT}" + + + integration-tests: + needs: prepare-matrix + uses: matter-labs/era-compiler-ci/.github/workflows/integration-tests.yaml@main + secrets: inherit + if: ${{ inputs.run_integration_tests || github.event_name == 'schedule' }} + with: + ccache-key-type: 'static' # rotate ccache key every month + llvm-ref: ${{ inputs.ref }} + target-machine: 'eravm' # TODO: add `evm` target when LLVM EVM BE is ready + platforms-matrix: ${{ needs.prepare-matrix.outputs.matrix }} + + + build: + needs: prepare-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }} + runs-on: ${{ matrix.runner }} + container: + image: ${{ matrix.image || '' }} # Special workaround to allow matrix builds with optional container + name: ${{ matrix.name }} + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + path: "llvm" + + - name: Prepare Windows env + if: runner.os == 'Windows' + uses: matter-labs/era-compiler-ci/.github/actions/prepare-msys@v1 + + - name: Build LLVM + uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 + with: + enable-tests: true + enable-assertions: ${{ inputs.enable_assertions }} + clone-llvm: false + ccache-key-type: 'static' + save-ccache: ${{ matrix.name == 'Linux x86' }} + enable-valgrind: ${{ inputs.enable-valgrind }} + valgrind-options: ${{ inputs.valgrind-options }} + + # Required to keep executable permissions for binaries + - name: Prepare tarball + run: tar -czf ${{ runner.os }}-${{ runner.arch }}-target-final.tar.gz ./target-llvm/target-final + + - name: Upload LLVM binaries + uses: actions/upload-artifact@v4 + with: + name: llvm-bins-${{ runner.os }}-${{ runner.arch }} + path: ./${{ runner.os }}-${{ runner.arch }}-target-final.tar.gz + + # On Windows, run `llvm-lit` directly with xfail parameter + # due to different incompatibilities in tests and MSYS2 environment + - name: Lit tests (Windows) + if: ${{ (inputs.run_regression_tests || github.event_name == 'schedule') && runner.os == 'Windows' }} + shell: 'msys2 {0}' + env: + LLVM_XFAILS: + "Object/archive-big-read.test;\ + Object/archive-extract.test;\ + Object/archive-toc.test;\ + TableGen/x86-fold-tables.td;\ + tools/llvm-ar/empty-uid-gid.test;\ + tools/llvm-cov/native_separators.c;\ + tools/llvm-libtool-darwin/deterministic-library.test;\ + tools/llvm-nm/invalid-tapi-files.test;\ + tools/llvm-nm/tapi-files.test;\ + tools/llvm-objcopy/ELF/deterministic-archive.test;\ + tools/llvm-ranlib/D-flag.test;\ + tools/llvm-tapi-diff/v5.test" + LLD_XFAILS: + "MachO/double-unwind-info.s;\ + MachO/error-limit.test;\ + MachO/local-private-extern.yaml" + run: | + ./target-llvm/build-final/bin/llvm-lit.py -sv ./target-llvm/build-final/test --xfail ${LLVM_XFAILS} + ./target-llvm/build-final/bin/llvm-lit.py -sv ./target-llvm/build-final/tools/lld/test --xfail ${LLD_XFAILS} + + - name: Lit tests (MacOS/Linux) + if: ${{ (inputs.run_regression_tests || github.event_name == 'schedule') && runner.os != 'Windows' }} + run: ninja -C './target-llvm/build-final' verify-llvm -v + + - name: Send Slack notification + uses: 8398a7/action-slack@v3 + if: failure() && github.event_name == 'schedule' + with: + status: ${{ job.status }} + fields: repo,commit,author,action,eventName,ref,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/README.md b/README.md index f95409291f05..02b40eb1cb5a 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,120 @@ -# zkSync Era: The zkEVM LLVM Framework +# ZKsync Era: The ZKsync LLVM Framework [![Logo](eraLogo.svg)](https://zksync.io/) -zkSync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security +ZKsync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security or decentralization. As it's EVM-compatible (with Solidity/Vyper), 99% of Ethereum projects can redeploy without -needing to refactor or re-audit any code. zkSync Era also uses an LLVM-based compiler that will eventually enable +needing to refactor or re-audit any code. ZKsync Era also uses an LLVM-based compiler that will eventually enable developers to write smart contracts in popular languages such as C++ and Rust. -This directory and its sub-directories contain the source code for the zkEVM fork of the [LLVM](https://llvm.org) framework, +This directory and its sub-directories contain the source code for the ZKsync fork of the [LLVM](https://llvm.org) framework, a toolkit for the construction of highly optimized compilers, optimizers, and run-time environments used by the Solidity and Vyper compilers developed by Matter Labs. ## Overview -Welcome to the zkEVM LLVM project! +Welcome to the ZKsync LLVM project! The project has multiple components. The core of the project is the `llvm` directory. This contains all of the tools, libraries, and header files needed to process intermediate representations and convert them into object files. Tools include an assembler, disassembler, bitcode analyzer, and bitcode optimizer. These tools are not yet officially supported for third-party front-ends. -It also contains zkEVM modifications of the standard [LLVM regression tests](https://llvm.org/docs/TestingGuide.html#regression-tests). - -The zkEVM back-end is called `EraVM`, and the architecture is called `eravm`. +It also contains ZKsync modifications of the standard [LLVM regression tests](https://llvm.org/docs/TestingGuide.html#regression-tests). ## Building -The zkEVM LLVM framework must be built with our tool called `zkevm-llvm`: +The ZKsync LLVM framework must be built with our tool called `zksync-llvm`: + +
+1. Install the system prerequisites. + + * Linux (Debian): + + Install the following packages: + ```shell + apt install cmake ninja-build curl git libssl-dev pkg-config clang lld + ``` + * Linux (Arch): + + Install the following packages: + ```shell + pacman -Syu which cmake ninja curl git pkg-config clang lld + ``` + + * MacOS: + + * Install the [HomeBrew](https://brew.sh) package manager. + * Install the following packages: + + ```shell + brew install cmake ninja coreutils + ``` + + * Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apple’s Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. +
+ +
+2. Install Rust. + + * Follow the latest [official instructions](https://www.rust-lang.org/tools/install: + ```shell + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + . ${HOME}/.cargo/env + ``` + + > Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. +
+ +
+3. Install the ZKsync LLVM framework builder. + + * Install the builder using `cargo`: + ```shell + cargo install compiler-llvm-builder + ``` -1. Install some tools system-wide: - 1.a. `apt install cmake ninja-build clang-13 lld-13` on a Debian-based Linux, with optional `musl-tools` if you need a `musl` build - 1.b. `pacman -S cmake ninja clang lld` on an Arch-based Linux - 1.c. On MacOS, install the [HomeBrew](https://brew.sh) package manager (being careful to install it as the appropriate user), then `brew install cmake ninja coreutils`. Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apple’s Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. - 1.d. Their equivalents with other package managers + > The builder is not the ZKsync LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. -2. [Install Rust](https://www.rust-lang.org/tools/install) +
- Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. - Also install the `musl` target if you are compiling on Linux in order to distribute the binaries: - `rustup target add x86_64-unknown-linux-musl` +
+4. Create the `LLVM.lock` file. -3. Install the zkEVM LLVM framework builder: + * In a directory in which you want the `llvm` directory, create an `LLVM.lock` file with the URL and branch or tag you want to build, for example: - 3.a. `cargo install compiler-llvm-builder` on MacOS, or Linux for personal use - 3.b. `cargo install compiler-llvm-builder --target x86_64-unknown-linux-musl` on Linux for distribution + ```properties + url = "https://github.com/matter-labs/era-compiler-llvm" + branch = "main" + ``` - The builder is not the zkEVM LLVM framework itself, but a tool that clones its repository and runs the sequence of build commands. - By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. +
-4. In a directory in which you want the `llvm` directory, create an `LLVM.lock` file with the URL and branch or tag you want to build. For example: +
+5. Build LLVM. - ``` - url = "" - branch = "" - ``` + * Clone and build the ZKsync LLVM framework using the `zksync-llvm` tool: + ```shell + zksync-llvm clone + zksync-llvm build + ``` -5. Run the builder to clone and build the zkevm LLVM framework: - 5.1. `zkevm-llvm clone` - 5.2. `zkevm-llvm build` + The build artifacts will end up in the `./target-llvm/target-final/` directory. + You may point your `LLVM_SYS_170_PREFIX` to that directory to use this build as a compiler dependency. + If built with the `--enable-tests` option, test tools will be in the `./target-llvm/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `zksync-llvm build --help`. - The build artifacts will end up in the `./target-llvm/target-final/` directory. - You may point your `LLVM_SYS_150_PREFIX` to that directory to use this build as a compiler dependency. - If built with the `--enable-tests` option, test tools will be in the `./target-llvm/build-final/` directory, along with copies of the build artifacts. +
## Troubleshooting -- If you get a “failed to authenticate when downloading repository… if the git CLI succeeds then net.git-fetch-with-cli may help here” error, -then prepending the `cargo` command with `CARGO_NET_GIT_FETCH_WITH_CLI=true` may help. +- Make sure your system is up-to-date. +- Make sure you have the required system prerequisites installed. - Unset any LLVM-related environment variables you may have set. +- If you encounter any problems with the building process, please open an issue in the [era-compiler-llvm](https://github.com/matter-labs/era-compiler-llvm/issues) repository. ## License -The zkEVM fork of the LLVM framework is distributed under the terms of +The ZKsync fork of the LLVM framework is distributed under the terms of Apache License, Version 2.0 with LLVM Exceptions, ([LICENSE](LICENSE) or ) ## Resources @@ -88,7 +131,7 @@ Apache License, Version 2.0 with LLVM Exceptions, ([LICENSE](LICENSE) or = 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey = "Diagnostics" + merged = [] + for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")): + content = yaml.safe_load(open(replacefile, "r")) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = {"MainSourceFile": "", mergekey: merged} + with open(mergefile, "w") as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, "w").close() + + +def main(): + parser = argparse.ArgumentParser( + description="Run clang-tidy against changed files, and " + "output diagnostics only for modified " + "lines." + ) + parser.add_argument( + "-clang-tidy-binary", + metavar="PATH", + default="clang-tidy", + help="path to clang-tidy binary", + ) + parser.add_argument( + "-p", + metavar="NUM", + default=0, + help="strip the smallest prefix containing P slashes", + ) + parser.add_argument( + "-regex", + metavar="PATTERN", + default=None, + help="custom pattern selecting file paths to check " + "(case sensitive, overrides -iregex)", + ) + parser.add_argument( + "-iregex", + metavar="PATTERN", + default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)", + help="custom pattern selecting file paths to check " + "(case insensitive, overridden by -regex)", + ) + parser.add_argument( + "-j", + type=int, + default=1, + help="number of tidy instances to be run in parallel.", + ) + parser.add_argument( + "-timeout", type=int, default=None, help="timeout per each file in seconds." + ) + parser.add_argument( + "-fix", action="store_true", default=False, help="apply suggested fixes" + ) + parser.add_argument( + "-checks", + help="checks filter, when not specified, use clang-tidy " "default", + default="", + ) + parser.add_argument("-use-color", action="store_true", help="Use colors in output") + parser.add_argument( + "-path", dest="build_path", help="Path used to read a compile command database." + ) + if yaml: + parser.add_argument( + "-export-fixes", + metavar="FILE", + dest="export_fixes", + help="Create a yaml file to store suggested fixes in, " + "which can be applied with clang-apply-replacements.", + ) + parser.add_argument( + "-extra-arg", + dest="extra_arg", + action="append", + default=[], + help="Additional argument to append to the compiler " "command line.", + ) + parser.add_argument( + "-extra-arg-before", + dest="extra_arg_before", + action="append", + default=[], + help="Additional argument to prepend to the compiler " "command line.", + ) + parser.add_argument( + "-quiet", + action="store_true", + default=False, + help="Run clang-tidy in quiet mode", + ) + parser.add_argument( + "-load", + dest="plugins", + action="append", + default=[], + help="Load the specified plugin in clang-tidy.", + ) + + clang_tidy_args = [] + argv = sys.argv[1:] + if "--" in argv: + clang_tidy_args.extend(argv[argv.index("--") :]) + argv = argv[: argv.index("--")] + + args = parser.parse_args(argv) + + # Extract changed lines for each file. + filename = None + lines_by_file = {} + for line in sys.stdin: + match = re.search('^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line) + if match: + filename = match.group(2) + if filename is None: + continue + + if args.regex is not None: + if not re.match("^%s$" % args.regex, filename): + continue + else: + if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE): + continue + + match = re.search("^@@.*\+(\d+)(,(\d+))?", line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).append([start_line, end_line]) + + if not any(lines_by_file): + print("No relevant changes found.") + sys.exit(0) + + max_task_count = args.j + if max_task_count == 0: + max_task_count = multiprocessing.cpu_count() + max_task_count = min(len(lines_by_file), max_task_count) + + tmpdir = None + if yaml and args.export_fixes: + tmpdir = tempfile.mkdtemp() + + # Tasks for clang-tidy. + task_queue = queue.Queue(max_task_count) + # A lock for console output. + lock = threading.Lock() + + # List of files with a non-zero return code. + failed_files = [] + + # Run a pool of clang-tidy workers. + start_workers( + max_task_count, run_tidy, (task_queue, lock, args.timeout, failed_files) + ) + + # Form the common args list. + common_clang_tidy_args = [] + if args.fix: + common_clang_tidy_args.append("-fix") + if args.checks != "": + common_clang_tidy_args.append("-checks=" + args.checks) + if args.quiet: + common_clang_tidy_args.append("-quiet") + if args.build_path is not None: + common_clang_tidy_args.append("-p=%s" % args.build_path) + if args.use_color: + common_clang_tidy_args.append("--use-color") + for arg in args.extra_arg: + common_clang_tidy_args.append("-extra-arg=%s" % arg) + for arg in args.extra_arg_before: + common_clang_tidy_args.append("-extra-arg-before=%s" % arg) + for plugin in args.plugins: + common_clang_tidy_args.append("-load=%s" % plugin) + + for name in lines_by_file: + line_filter_json = json.dumps( + [{"name": name, "lines": lines_by_file[name]}], separators=(",", ":") + ) + + # Run clang-tidy on files containing changes. + command = [args.clang_tidy_binary] + command.append("-line-filter=" + line_filter_json) + if yaml and args.export_fixes: + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, tmp_name) = tempfile.mkstemp(suffix=".yaml", dir=tmpdir) + os.close(handle) + command.append("-export-fixes=" + tmp_name) + command.extend(common_clang_tidy_args) + command.append(name) + command.extend(clang_tidy_args) + + task_queue.put(command) + + # Application return code + return_code = 0 + + # Wait for all threads to be done. + task_queue.join() + # Application return code + return_code = 0 + if failed_files: + return_code = 1 + + if yaml and args.export_fixes: + print("Writing fixes to " + args.export_fixes + " ...") + try: + merge_replacement_files(tmpdir, args.export_fixes) + except: + sys.stderr.write("Error exporting fixes.\n") + traceback.print_exc() + return_code = 1 + + if tmpdir: + shutil.rmtree(tmpdir) + sys.exit(return_code) + + +if __name__ == "__main__": + main() diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000000..c139341dc999 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +codecov: + require_ci_to_pass: no + +coverage: + status: + project: + default: false # disable the default status that measures entire project + patch: + default: false # disable the patch status that measures patch changes diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml new file mode 100644 index 000000000000..4bff98ba6c75 --- /dev/null +++ b/infra/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.2' +services: + zk: + image: "matterlabs/llvm_runner:latest" + working_dir: /usr/src/zksync + command: "tail -f /dev/null" + volumes: + - ${GITHUB_WORKSPACE}:/usr/src/zksync + environment: + - SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY} diff --git a/llvm/.clang-tidy b/llvm/.clang-tidy index f29cbbd6b357..617d2d1b1b1b 100644 --- a/llvm/.clang-tidy +++ b/llvm/.clang-tidy @@ -18,6 +18,7 @@ Checks: > -cppcoreguidelines-macro-usage, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-type-const-cast, @@ -26,6 +27,7 @@ Checks: > hicpp-*, -hicpp-braces-around-statements, -hicpp-named-parameter, + -hicpp-no-array-decay, -hicpp-signed-bitwise, -hicpp-special-member-functions, llvm-*, @@ -33,6 +35,7 @@ Checks: > misc-static-assert, misc-unused-using-decls, -misc-const-correctness, + -misc-include-cleaner, modernize-loop-convert, modernize-make-unique, modernize-raw-string-literal, diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt index 6b7f2b58e603..9b64092e547d 100644 --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -245,6 +245,54 @@ add_lit_testsuite(check-llvm "Running the LLVM regression tests" ) set_target_properties(check-llvm PROPERTIES FOLDER "LLVM/Tests") +add_lit_testsuite(verify-llvm-codegen-opt "Running CodeGen LLVM regression tests with verification options set" + ${CMAKE_CURRENT_BINARY_DIR} + EXCLUDE_FROM_CHECK_ALL + PARAMS "opt=${LLVM_BINARY_DIR}/bin/opt \ +--cgp-verify-bfi-updates \ +--earlycse-debug-hash \ +--loop-distribute-verify \ +--machine-combiner-verify-pattern-order \ +--phicse-debug-hash \ +--scev-verify-ir \ +--tail-dup-verify \ +--unroll-verify-domtree \ +--verify-assumption-cache \ +--verify-analysis-invalidation \ +--verify-cfiinstrs \ +--verify-coalescing \ +--verify-dom-info \ +--verify-each \ +--verify-loop-info \ +--verify-loop-lcssa \ +--verify-machine-dom-info \ +--verify-machineinstrs \ +--verify-memoryssa \ +--verify-misched \ +--verify-regalloc \ +--verify-scev-strict" + ARGS "--filter CodeGen" + DEPENDS ${LLVM_TEST_DEPENDS} + ) + +add_lit_testsuite(verify-llvm-codegen-llc "Running EVM LLVM regression tests with verification options set" + ${CMAKE_CURRENT_BINARY_DIR} + EXCLUDE_FROM_CHECK_ALL + PARAMS "llc=${LLVM_BINARY_DIR}/bin/llc \ +--cgp-verify-bfi-updates \ +--compile-twice \ +--earlycse-debug-hash \ +--machine-combiner-verify-pattern-order \ +--phicse-debug-hash \ +--reassociate-geps-verify-no-dead-code \ +--safepoint-ir-verifier-print-only \ +--scev-verify-ir \ +--tail-dup-verify \ +--unroll-verify-domtree \ +--verify-regalloc" + DEPENDS ${LLVM_TEST_DEPENDS} + ) + add_lit_testsuites(LLVM ${CMAKE_CURRENT_SOURCE_DIR} ${exclude_from_check_all} DEPENDS ${LLVM_TEST_DEPENDS} @@ -255,3 +303,8 @@ add_lit_testsuites(LLVM ${CMAKE_CURRENT_SOURCE_DIR} add_custom_target(check) add_dependencies(check check-all) set_target_properties(check PROPERTIES FOLDER "LLVM/Tests") + +# Setup an alias for 'verify-llvm'. +add_custom_target(verify-llvm DEPENDS check verify-llvm-codegen-opt verify-llvm-codegen-llc) +add_dependencies(check verify-llvm-codegen-opt verify-llvm-codegen-llc) +set_target_properties(verify-llvm PROPERTIES FOLDER "LLVM/Tests") diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp index 23f6081cd32a..02e2f99f4ff9 100644 --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -938,6 +938,11 @@ TEST(CommandLineTest, ResponseFiles) { } TEST(CommandLineTest, RecursiveResponseFiles) { + // EVM local begin + // Temporary disable on Windows due to issues with paths on MSYS2. + if (Triple(sys::getProcessTriple()).isOSWindows()) + GTEST_SKIP(); + // EVM local end vfs::InMemoryFileSystem FS; #ifdef _WIN32 const char *TestRoot = "C:\\"; diff --git a/llvm/utils/git/code-format-helper-era-llvm.py b/llvm/utils/git/code-format-helper-era-llvm.py new file mode 100644 index 000000000000..fe61be99ea2e --- /dev/null +++ b/llvm/utils/git/code-format-helper-era-llvm.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python3 +# +# ====- code-format-helper, runs code formatters from the ci or in a hook --*- python -*--==# +# +# 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 +# +# ==--------------------------------------------------------------------------------------==# + +import argparse +import os +import subprocess +import sys +from typing import List, Optional + +""" +This script is run by GitHub actions to ensure that the code in PR's conform to +the coding style of LLVM. It can also be installed as a pre-commit git hook to +check the coding style before submitting it. The canonical source of this script +is in the LLVM source tree under llvm/utils/git. + +For C/C++ code it uses clang-format and for Python code it uses darker (which +in turn invokes black). + +You can learn more about the LLVM coding style on llvm.org: +https://llvm.org/docs/CodingStandards.html + +You can install this script as a git hook by symlinking it to the .git/hooks +directory: + +ln -s $(pwd)/llvm/utils/git/code-format-helper.py .git/hooks/pre-commit + +You can control the exact path to clang-format or darker with the following +environment variables: $CLANG_FORMAT_PATH and $DARKER_FORMAT_PATH. +""" + + +class FormatArgs: + start_rev: str = None + end_rev: str = None + repo: str = None + changed_files: List[str] = [] + token: str = None + verbose: bool = True + issue_number: int = 0 + + def __init__(self, args: argparse.Namespace = None) -> None: + if not args is None: + self.start_rev = args.start_rev + self.end_rev = args.end_rev + self.repo = args.repo + self.token = args.token + self.changed_files = args.changed_files + self.issue_number = args.issue_number + + +class FormatHelper: + COMMENT_TAG = "" + name: str + friendly_name: str + + @property + def comment_tag(self) -> str: + return self.COMMENT_TAG.replace("fmt", self.name) + + @property + def instructions(self) -> str: + raise NotImplementedError() + + def has_tool(self) -> bool: + raise NotImplementedError() + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + raise NotImplementedError() + + def pr_comment_text_for_diff(self, diff: str) -> str: + return f""" +:warning: {self.friendly_name}, {self.name} found issues in your code. :warning: + +
+ +You can test this locally with the following command: + + +``````````bash +{self.instructions} +`````````` + +
+ +
+ +View the diff from {self.name} here. + + +``````````diff +{diff} +`````````` + +
+""" + + # TODO: any type should be replaced with the correct github type, but it requires refactoring to + # not require the github module to be installed everywhere. + def find_comment(self, pr: any) -> any: + for comment in pr.as_issue().get_comments(): + if self.comment_tag in comment.body: + return comment + return None + + def update_pr(self, comment_text: str, args: FormatArgs, create_new: bool) -> None: + import github + from github import IssueComment, PullRequest + + repo = github.Github(args.token).get_repo(args.repo) + pr = repo.get_issue(args.issue_number).as_pull_request() + + comment_text = self.comment_tag + "\n\n" + comment_text + + existing_comment = self.find_comment(pr) + if existing_comment: + existing_comment.edit(comment_text) + elif create_new: + pr.as_issue().create_comment(comment_text) + + def run(self, changed_files: List[str], args: FormatArgs) -> bool: + changed_files = [arg for arg in changed_files if "third-party" not in arg] + diff = self.format_run(changed_files, args) + should_update_gh = args.token is not None and args.repo is not None + + if diff is None: + if should_update_gh: + comment_text = ( + ":white_check_mark: With the latest revision " + f"this PR passed the {self.friendly_name}." + ) + self.update_pr(comment_text, args, create_new=False) + return True + elif len(diff) > 0: + if should_update_gh: + comment_text = self.pr_comment_text_for_diff(diff) + self.update_pr(comment_text, args, create_new=True) + else: + print( + f"Warning: {self.friendly_name}, {self.name} detected " + "some issues with your code formatting..." + ) + return False + else: + # The formatter failed but didn't output a diff (e.g. some sort of + # infrastructure failure). + comment_text = ( + f":warning: The {self.friendly_name} failed without printing " + "a diff. Check the logs for stderr output. :warning:" + ) + self.update_pr(comment_text, args, create_new=False) + return False + + +class ClangFormatHelper(FormatHelper): + name = "clang-format" + friendly_name = "C/C++ code formatter" + + @property + def instructions(self) -> str: + return " ".join(self.cf_cmd) + + def should_include_extensionless_file(self, path: str) -> bool: + return path.startswith("libcxx/include") + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + _, ext = os.path.splitext(path) + if ext in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx", ".inc", ".cppm"): + filtered_files.append(path) + elif ext == "" and self.should_include_extensionless_file(path): + filtered_files.append(path) + return filtered_files + + @property + def clang_fmt_path(self) -> str: + if "CLANG_FORMAT_PATH" in os.environ: + return os.environ["CLANG_FORMAT_PATH"] + return "git-clang-format" + + def has_tool(self) -> bool: + cmd = [self.clang_fmt_path, "-h"] + proc = None + try: + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + return proc.returncode == 0 + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + cpp_files = self.filter_changed_files(changed_files) + if not cpp_files: + return None + + cf_cmd = [self.clang_fmt_path, "--diff"] + + if args.start_rev and args.end_rev: + cf_cmd.append(args.start_rev) + cf_cmd.append(args.end_rev) + + cf_cmd.append("--") + cf_cmd += cpp_files + + if args.verbose: + print(f"Running: {' '.join(cf_cmd)}") + self.cf_cmd = cf_cmd + proc = subprocess.run(cf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + # Print the diff in the log so that it is viewable there + print(proc.stdout.decode("utf-8")) + return proc.stdout.decode("utf-8") + else: + return None + + +class DarkerFormatHelper(FormatHelper): + name = "darker" + friendly_name = "Python code formatter" + + @property + def instructions(self) -> str: + return " ".join(self.darker_cmd) + + def filter_changed_files(self, changed_files: List[str]) -> List[str]: + filtered_files = [] + for path in changed_files: + name, ext = os.path.splitext(path) + if ext == ".py": + filtered_files.append(path) + + return filtered_files + + @property + def darker_fmt_path(self) -> str: + if "DARKER_FORMAT_PATH" in os.environ: + return os.environ["DARKER_FORMAT_PATH"] + return "darker" + + def has_tool(self) -> bool: + cmd = [self.darker_fmt_path, "--version"] + proc = None + try: + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + return proc.returncode == 0 + + def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]: + py_files = self.filter_changed_files(changed_files) + if not py_files: + return None + darker_cmd = [ + self.darker_fmt_path, + "--check", + "--diff", + ] + if args.start_rev and args.end_rev: + darker_cmd += ["-r", f"{args.start_rev}...{args.end_rev}"] + darker_cmd += py_files + if args.verbose: + print(f"Running: {' '.join(darker_cmd)}") + self.darker_cmd = darker_cmd + proc = subprocess.run( + darker_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + if args.verbose: + sys.stdout.write(proc.stderr.decode("utf-8")) + + if proc.returncode != 0: + # formatting needed, or the command otherwise failed + if args.verbose: + print(f"error: {self.name} exited with code {proc.returncode}") + # Print the diff in the log so that it is viewable there + print(proc.stdout.decode("utf-8")) + return proc.stdout.decode("utf-8") + else: + sys.stdout.write(proc.stdout.decode("utf-8")) + return None + + +ALL_FORMATTERS = (DarkerFormatHelper(), ClangFormatHelper()) + + +def hook_main(): + # fill out args + args = FormatArgs() + args.verbose = False + + # find the changed files + cmd = ["git", "diff", "--cached", "--name-only", "--diff-filter=d"] + proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = proc.stdout.decode("utf-8") + for line in output.splitlines(): + args.changed_files.append(line) + + failed_fmts = [] + for fmt in ALL_FORMATTERS: + if fmt.has_tool(): + if not fmt.run(args.changed_files, args): + failed_fmts.append(fmt.name) + else: + print(f"Couldn't find {fmt.name}, can't check " + fmt.friendly_name.lower()) + + if len(failed_fmts) > 0: + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + script_path = os.path.abspath(__file__) + if ".git/hooks" in script_path: + hook_main() + sys.exit(0) + + parser = argparse.ArgumentParser() + parser.add_argument( + "--token", type=str, required=False, help="GitHub authentiation token" + ) + parser.add_argument( + "--repo", + type=str, + default=os.getenv("GITHUB_REPOSITORY", "llvm/llvm-project"), + help="The GitHub repository that we are working with in the form of / (e.g. llvm/llvm-project)", + ) + parser.add_argument("--issue-number", type=int, required=True) + parser.add_argument( + "--start-rev", + type=str, + required=True, + help="Compute changes from this revision.", + ) + parser.add_argument( + "--end-rev", type=str, required=True, help="Compute changes to this revision" + ) + parser.add_argument( + "--changed-files", + type=str, + help="Comma separated list of files that has been changed", + ) + + args = FormatArgs(parser.parse_args()) + + changed_files = [] + if args.changed_files: + changed_files = args.changed_files.split(",") + + failed_formatters = [] + for fmt in ALL_FORMATTERS: + if not fmt.run(changed_files, args): + failed_formatters.append(fmt.name) + + if len(failed_formatters) > 0: + print(f"error: some formatters failed: {' '.join(failed_formatters)}") + sys.exit(1) diff --git a/llvm/utils/git/requirements_formatting_era_llvm.txt b/llvm/utils/git/requirements_formatting_era_llvm.txt new file mode 100644 index 000000000000..ff744f0d4225 --- /dev/null +++ b/llvm/utils/git/requirements_formatting_era_llvm.txt @@ -0,0 +1,52 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --output-file=llvm/utils/git/requirements_formatting.txt llvm/utils/git/requirements_formatting.txt.in +# +black==23.9.1 + # via + # -r llvm/utils/git/requirements_formatting.txt.in + # darker +certifi==2023.7.22 + # via requests +cffi==1.15.1 + # via + # cryptography + # pynacl +charset-normalizer==3.2.0 + # via requests +click==8.1.7 + # via black +cryptography==41.0.3 + # via pyjwt +darker==1.7.2 + # via -r llvm/utils/git/requirements_formatting.txt.in +deprecated==1.2.14 + # via pygithub +idna==3.4 + # via requests +mypy-extensions==1.0.0 + # via black +packaging==23.1 + # via black +pathspec==0.11.2 + # via black +platformdirs==3.10.0 + # via black +pycparser==2.21 + # via cffi +pygithub==1.59.1 + # via -r llvm/utils/git/requirements_formatting.txt.in +pyjwt[crypto]==2.8.0 + # via pygithub +pynacl==1.5.0 + # via pygithub +requests==2.31.0 + # via pygithub +toml==0.10.2 + # via darker +urllib3==2.0.4 + # via requests +wrapt==1.15.0 + # via deprecated From 510916d951aaac1776c00563aa91cdb80257abff Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Mon, 19 Jun 2023 20:25:06 +0200 Subject: [PATCH 058/203] [MemCpyOpt] LLVM Handle i256 length properly in memcpy optimizer LLVM doesn't assume memcpy lenght can exceed i64 max value, if it does don't perform optimization. --- .../lib/Transforms/Scalar/MemCpyOptimizer.cpp | 25 +++++++++++++++++++ llvm/test/Transforms/MemCpyOpt/i256length.ll | 23 +++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 llvm/test/Transforms/MemCpyOpt/i256length.ll diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp index cee34f0a6da1..f4e495c9eed9 100644 --- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -836,6 +836,11 @@ bool MemCpyOptPass::processStore(StoreInst *SI, BasicBlock::iterator &BBI) { } bool MemCpyOptPass::processMemSet(MemSetInst *MSI, BasicBlock::iterator &BBI) { + // EVM local begin + if (isa(MSI->getLength()) && + cast(MSI->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end // See if there is another memset or store neighboring this memset which // allows us to widen out the memset to do a single larger store. if (isa(MSI->getLength()) && !MSI->isVolatile()) @@ -1125,6 +1130,12 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad, bool MemCpyOptPass::processMemCpyMemCpyDependence(MemCpyInst *M, MemCpyInst *MDep, BatchAAResults &BAA) { + // EVM local begin + if (isa(M->getLength()) && + cast(M->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end + // If dep instruction is reading from our current input, then it is a noop // transfer and substituting the input won't change this instruction. Just // ignore the input and let someone else zap MDep. This handles cases like: @@ -1723,6 +1734,11 @@ static bool isZeroSize(Value *Size) { /// circumstances). This allows later passes to remove the first memcpy /// altogether. bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) { + // EVM local begin + if (isa(M->getLength()) && + cast(M->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end // We can only optimize non-volatile memcpy's. if (M->isVolatile()) return false; @@ -1857,6 +1873,11 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) { /// Transforms memmove calls to memcpy calls when the src/dst are guaranteed /// not to alias. bool MemCpyOptPass::processMemMove(MemMoveInst *M) { + // EVM local begin + if (isa(M->getLength()) && + cast(M->getLength())->getValue().getSignificantBits() > 64) + return false; + // EVM local end // See if the source could be modified by this memmove potentially. if (isModSet(AA->getModRefInfo(M, MemoryLocation::getForSource(M)))) return false; @@ -1904,6 +1925,10 @@ bool MemCpyOptPass::processByValArgument(CallBase &CB, unsigned ArgNo) { // The length of the memcpy must be larger or equal to the size of the byval. auto *C1 = dyn_cast(MDep->getLength()); + // EVM local begin + if (C1 && C1->getValue().getSignificantBits() > 64) + return false; + // EVM local end if (!C1 || !TypeSize::isKnownGE( TypeSize::getFixed(C1->getValue().getZExtValue()), ByValSize)) return false; diff --git a/llvm/test/Transforms/MemCpyOpt/i256length.ll b/llvm/test/Transforms/MemCpyOpt/i256length.ll new file mode 100644 index 000000000000..5cb04d17800a --- /dev/null +++ b/llvm/test/Transforms/MemCpyOpt/i256length.ll @@ -0,0 +1,23 @@ +; RUN: opt -S -passes=memcpyopt < %s + +target triple = "evm-unknown-unknown" + +@ptr_calldata = private global ptr addrspace(3) null + +declare void @foo() + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(1) noalias nocapture readonly, i256, i1 immarg) + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) + +; Function Attrs: nofree null_pointer_is_valid +define private void @__deploy() { +entry: + call void @foo() + %calldata_pointer = load ptr addrspace(3), ptr @ptr_calldata, align 32 + %calldata_source_pointer = getelementptr i8, ptr addrspace(3) %calldata_pointer, i256 0 + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) align 1 null, ptr addrspace(3) align 1 %calldata_source_pointer, i256 32000000000000000000000000000000000000000000, i1 false) + unreachable +} From b8b180bcedab0d46748a15b07ee3b671c2dff1ae Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 5 Jul 2023 15:07:35 +0300 Subject: [PATCH 059/203] [EVM] Adding intial implementation of a code stackification. This includes: - stack manipulation instructions - initial support of instructions encoding - register coloring pass - alloca hoisting pass It hoist alloca instrustions to the first BB of a function. This makes possible to transform all allocas into vregs. - pass that forms single use expressions with rematerialization of: - CALLDATALOAD - ADD ptr, imm FIXME: this is a ad-hoc workaround to decrease runtime stack height. This can easily blow out a code size. - pass that optimizes live intervals. - storing the number of a function parameters in FunctionInfo. Signed-off-by: Pavel Kopyl --- llvm/include/llvm/MC/MCDecoderOps.h | 8 +- llvm/lib/MC/ELFObjectWriter.cpp | 22 +- llvm/lib/MC/MCExpr.cpp | 4 +- llvm/lib/Target/EVM/CMakeLists.txt | 12 +- .../Target/EVM/Disassembler/CMakeLists.txt | 13 + .../EVM/Disassembler/EVMDisassembler.cpp | 172 +++ llvm/lib/Target/EVM/EVM.h | 19 +- llvm/lib/Target/EVM/EVMAllocaHoisting.cpp | 85 ++ llvm/lib/Target/EVM/EVMISelLowering.cpp | 8 +- llvm/lib/Target/EVM/EVMISelLowering.h | 17 + llvm/lib/Target/EVM/EVMInstrFormats.td | 45 +- llvm/lib/Target/EVM/EVMInstrInfo.cpp | 10 + llvm/lib/Target/EVM/EVMInstrInfo.h | 22 + llvm/lib/Target/EVM/EVMInstrInfo.td | 602 +++++++-- llvm/lib/Target/EVM/EVMLinkRuntime.cpp | 12 +- llvm/lib/Target/EVM/EVMMCInstLower.cpp | 41 +- llvm/lib/Target/EVM/EVMMCInstLower.h | 4 +- .../lib/Target/EVM/EVMMachineFunctionInfo.cpp | 16 + llvm/lib/Target/EVM/EVMMachineFunctionInfo.h | 77 ++ .../Target/EVM/EVMOptimizeLiveIntervals.cpp | 111 ++ llvm/lib/Target/EVM/EVMRegColoring.cpp | 168 +++ llvm/lib/Target/EVM/EVMRegisterInfo.cpp | 22 +- llvm/lib/Target/EVM/EVMRegisterInfo.h | 3 + llvm/lib/Target/EVM/EVMRegisterInfo.td | 4 + .../lib/Target/EVM/EVMSingleUseExpression.cpp | 795 +++++++++++ llvm/lib/Target/EVM/EVMStackify.cpp | 1202 +++++++++++++++++ llvm/lib/Target/EVM/EVMSubtarget.h | 2 +- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 40 +- llvm/lib/Target/EVM/EVMTargetMachine.h | 4 + .../Target/EVM/MCTargetDesc/EVMAsmBackend.cpp | 208 ++- .../EVM/MCTargetDesc/EVMELFObjectWriter.cpp | 10 +- .../Target/EVM/MCTargetDesc/EVMFixupKinds.h | 18 +- .../EVM/MCTargetDesc/EVMInstPrinter.cpp | 1 + .../Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp | 2 +- .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 103 +- llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h | 1 - .../EVM/MCTargetDesc/EVMMCTargetDesc.cpp | 4 +- .../EVM/MCTargetDesc/EVMTargetStreamer.cpp | 2 +- .../EVM/MCTargetDesc/EVMTargetStreamer.h | 18 +- llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt | 2 + .../Target/EVM/TargetInfo/EVMTargetInfo.cpp | 98 ++ .../lib/Target/EVM/TargetInfo/EVMTargetInfo.h | 9 + llvm/test/CodeGen/EVM/add.ll | 2 +- llvm/test/CodeGen/EVM/aext.ll | 2 +- llvm/test/CodeGen/EVM/br.ll | 2 +- llvm/test/CodeGen/EVM/call.ll | 2 +- llvm/test/CodeGen/EVM/div.ll | 2 +- llvm/test/CodeGen/EVM/frameidx.ll | 17 +- llvm/test/CodeGen/EVM/globals.ll | 2 +- llvm/test/CodeGen/EVM/icmp.ll | 2 +- llvm/test/CodeGen/EVM/intrinsic.ll | 17 +- .../CodeGen/EVM/load-narrowing-disable.ll | 2 +- llvm/test/CodeGen/EVM/logical.ll | 2 +- llvm/test/CodeGen/EVM/mem_call_data.ll | 2 +- llvm/test/CodeGen/EVM/memintrinsics.ll | 2 +- llvm/test/CodeGen/EVM/memory.ll | 2 +- llvm/test/CodeGen/EVM/mod.ll | 2 +- llvm/test/CodeGen/EVM/mul.ll | 2 +- llvm/test/CodeGen/EVM/select.ll | 2 +- llvm/test/CodeGen/EVM/sext.ll | 2 +- llvm/test/CodeGen/EVM/shift.ll | 8 +- llvm/test/CodeGen/EVM/signextload.ll | 2 +- llvm/test/CodeGen/EVM/storage.ll | 2 +- llvm/test/CodeGen/EVM/sub.ll | 2 +- llvm/test/CodeGen/EVM/truncstore.ll | 2 +- llvm/test/CodeGen/EVM/tstorage.ll | 2 +- llvm/test/CodeGen/EVM/zero_any_extload.ll | 2 +- llvm/test/CodeGen/EVM/zext.ll | 2 +- llvm/test/TableGen/VarLenDecoder.td | 6 +- llvm/test/TableGen/long-offset-decode.td | 27 + llvm/test/TableGen/trydecode-emission.td | 12 +- llvm/test/TableGen/trydecode-emission2.td | 18 +- llvm/test/TableGen/trydecode-emission3.td | 12 +- llvm/test/TableGen/trydecode-emission4.td | 12 +- llvm/utils/TableGen/DecoderEmitter.cpp | 34 +- 75 files changed, 3933 insertions(+), 292 deletions(-) create mode 100644 llvm/lib/Target/EVM/Disassembler/CMakeLists.txt create mode 100644 llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp create mode 100644 llvm/lib/Target/EVM/EVMAllocaHoisting.cpp create mode 100644 llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp create mode 100644 llvm/lib/Target/EVM/EVMMachineFunctionInfo.h create mode 100644 llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp create mode 100644 llvm/lib/Target/EVM/EVMRegColoring.cpp create mode 100644 llvm/lib/Target/EVM/EVMSingleUseExpression.cpp create mode 100644 llvm/lib/Target/EVM/EVMStackify.cpp create mode 100644 llvm/test/TableGen/long-offset-decode.td diff --git a/llvm/include/llvm/MC/MCDecoderOps.h b/llvm/include/llvm/MC/MCDecoderOps.h index 3c0b68101e34..2b2467e4148c 100644 --- a/llvm/include/llvm/MC/MCDecoderOps.h +++ b/llvm/include/llvm/MC/MCDecoderOps.h @@ -15,9 +15,13 @@ namespace llvm { namespace MCD { // Disassembler state machine opcodes. enum DecoderOps { - OPC_ExtractField = 1, // OPC_ExtractField(uleb128 Start, uint8_t Len) + // EVM local begin + OPC_ExtractField = 1, // OPC_ExtractField(uleb128 Start, uint16_t Len) + // EVM local end OPC_FilterValue, // OPC_FilterValue(uleb128 Val, uint16_t NumToSkip) - OPC_CheckField, // OPC_CheckField(uleb128 Start, uint8_t Len, + // EVM local begin + OPC_CheckField, // OPC_CheckField(uleb128 Start, uint16_t Len, + // EVM local end // uleb128 Val, uint16_t NumToSkip) OPC_CheckPredicate, // OPC_CheckPredicate(uleb128 PIdx, uint16_t NumToSkip) OPC_Decode, // OPC_Decode(uleb128 Opcode, uleb128 DIdx) diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index f958905a26aa..89e70e42185f 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -966,7 +966,14 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { RevGroupMapTy RevGroupMap; // Write out the ELF header ... - writeHeader(Asm); + // 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 // ... then the sections ... SmallVector>, 0> Groups; @@ -984,6 +991,14 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { 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); + continue; + } + // EVM local end + writeSectionData(Asm, Section); uint64_t SecEnd = W.OS.tell(); @@ -1022,6 +1037,11 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { OWriter.TargetObjectWriter->addTargetSectionFlags(Ctx, Section); } + // EVM local begin + if (Ctx.getTargetTriple().isEVM()) + return W.OS.tell() - StartOffset; + // EVM local end + for (auto &[Group, Members] : 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 3f1af18fda43..49b1a2a7b00a 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -70,7 +70,9 @@ void MCExpr::print(raw_ostream &OS, const MCAsmInfo *MAI, bool InParens) const { return; } case MCExpr::SymbolRef: { - if (MAI->prependSymbolRefWithAt()) + // EVM local begin + if (MAI && MAI->prependSymbolRefWithAt()) + // EVM local end OS << '@'; const MCSymbolRefExpr &SRE = cast(*this); const MCSymbol &Sym = SRE.getSymbol(); diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 6fb6c9144f51..0d0158d8793e 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_TARGET_DEFINITIONS EVM.td) tablegen(LLVM EVMGenAsmWriter.inc -gen-asm-writer) tablegen(LLVM EVMGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM EVMGenDisassemblerTables.inc -gen-disassembler) tablegen(LLVM EVMGenInstrInfo.inc -gen-instr-info) tablegen(LLVM EVMGenMCCodeEmitter.inc -gen-emitter) tablegen(LLVM EVMGenRegisterInfo.inc -gen-register-info) @@ -35,6 +36,7 @@ add_custom_command( add_custom_target(EVMStdLib DEPENDS "${EVM_STDLIB_PATH_OUT}") add_llvm_target(EVMCodeGen + EVMAllocaHoisting.cpp EVMArgumentMove.cpp EVMAsmPrinter.cpp EVMCodegenPrepare.cpp @@ -44,8 +46,13 @@ add_llvm_target(EVMCodeGen EVMInstrInfo.cpp EVMLinkRuntime.cpp EVMLowerIntrinsics.cpp + EVMMachineFunctionInfo.cpp EVMMCInstLower.cpp + EVMOptimizeLiveIntervals.cpp + EVMRegColoring.cpp EVMRegisterInfo.cpp + EVMSingleUseExpression.cpp + EVMStackify.cpp EVMSubtarget.cpp EVMTargetMachine.cpp EVMTargetTransformInfo.cpp @@ -67,6 +74,8 @@ add_llvm_target(EVMCodeGen Target TargetParser TransformUtils + EVMDesc + EVMInfo ADD_TO_COMPONENT EVM @@ -76,5 +85,6 @@ add_llvm_target(EVMCodeGen intrinsics_gen ) -add_subdirectory(TargetInfo) +add_subdirectory(Disassembler) add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) diff --git a/llvm/lib/Target/EVM/Disassembler/CMakeLists.txt b/llvm/lib/Target/EVM/Disassembler/CMakeLists.txt new file mode 100644 index 000000000000..9a5568b7f7b2 --- /dev/null +++ b/llvm/lib/Target/EVM/Disassembler/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_component_library(LLVMEVMDisassembler + EVMDisassembler.cpp + + LINK_COMPONENTS + EVMDesc + MCDisassembler + EVMInfo + Support + MC + + ADD_TO_COMPONENT + EVM + ) diff --git a/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp b/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp new file mode 100644 index 000000000000..9f5015b7f9bf --- /dev/null +++ b/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp @@ -0,0 +1,172 @@ +//==----------- EVMDisassembler.cpp - Disassembler for EVM -*- 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 is part of the EVM Disassembler. +// +// It contains code to translate the data produced by the decoder into +// MCInsts. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/EVMMCExpr.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDecoderOps.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-disassembler" + +using DecodeStatus = MCDisassembler::DecodeStatus; + +namespace { + +class EVMDisassembler final : public MCDisassembler { + std::unique_ptr MCII; + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const override; + +public: + EVMDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, + std::unique_ptr MCII) + : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {} +}; + +} // end anonymous namespace + +// Decode PUSHN instructions, where N > 8, because target independent part +// of the Decoder can handle only 64-bit types. +template +static DecodeStatus decodePUSH(MCInst &Inst, APInt &Insn, uint64_t Address, + const MCDisassembler *Decoder) { + assert(ImmByteWidth > sizeof(uint64_t)); + assert(alignTo(Insn.getActiveBits(), 8) == (ImmByteWidth + 1) * 8); + MCContext &Ctx = Decoder->getContext(); + auto *Str = new (Ctx) SmallString<80>(); + Insn.extractBits(ImmByteWidth * 8, 0).toStringUnsigned(*Str); + Inst.addOperand(MCOperand::createExpr(EVMCImmMCExpr::create(*Str, Ctx))); + return MCDisassembler::Success; +} + +#include "EVMGenDisassemblerTables.inc" + +static const uint8_t *getDecoderTable(unsigned Size) { + switch (Size) { + case 1: + return static_cast(DecoderTable8); + case 2: + return static_cast(DecoderTable16); + case 3: + return static_cast(DecoderTable24); + case 4: + return static_cast(DecoderTable32); + case 5: + return static_cast(DecoderTable40); + case 6: + return static_cast(DecoderTable48); + case 7: + return static_cast(DecoderTable56); + case 8: + return static_cast(DecoderTable64); + case 9: + return static_cast(DecoderTable72); + case 10: + return static_cast(DecoderTable80); + case 11: + return static_cast(DecoderTable88); + case 12: + return static_cast(DecoderTable96); + case 13: + return static_cast(DecoderTable104); + case 14: + return static_cast(DecoderTable112); + case 15: + return static_cast(DecoderTable120); + case 16: + return static_cast(DecoderTable128); + case 17: + return static_cast(DecoderTable136); + case 18: + return static_cast(DecoderTable144); + case 19: + return static_cast(DecoderTable152); + case 20: + return static_cast(DecoderTable160); + case 21: + return static_cast(DecoderTable168); + case 22: + return static_cast(DecoderTable176); + case 23: + return static_cast(DecoderTable184); + case 24: + return static_cast(DecoderTable192); + case 25: + return static_cast(DecoderTable200); + case 26: + return static_cast(DecoderTable208); + case 27: + return static_cast(DecoderTable216); + case 28: + return static_cast(DecoderTable224); + case 29: + return static_cast(DecoderTable232); + case 30: + return static_cast(DecoderTable240); + case 31: + return static_cast(DecoderTable248); + case 32: + return static_cast(DecoderTable256); + case 33: + return static_cast(DecoderTable264); + default: + llvm_unreachable("Instructions must be from 1 to 33-bytes"); + } +} + +static MCDisassembler *createEVMDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + std::unique_ptr MCII(T.createMCInstrInfo()); + return new EVMDisassembler(STI, Ctx, std::move(MCII)); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheEVMTarget(), + createEVMDisassembler); +} + +MCDisassembler::DecodeStatus +EVMDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const { + + if (Bytes.empty()) + return MCDisassembler::Fail; + + for (unsigned InstSize = 1; InstSize <= 33; ++InstSize) { + Size = InstSize; + APInt Insn(33 * 8, toHex(ArrayRef(Bytes.begin(), Bytes.begin() + InstSize)), + 16); + DecodeStatus Result = decodeInstruction(getDecoderTable(InstSize), Instr, + Insn, Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + return MCDisassembler::Fail; +} diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index a0c1fe33342f..f43d0a036248 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -43,18 +43,35 @@ FunctionPass *createEVMCodegenPreparePass(); FunctionPass *createEVMISelDag(EVMTargetMachine &TM, CodeGenOptLevel OptLevel); FunctionPass *createEVMArgumentMove(); +FunctionPass *createEVMAllocaHoistingPass(); ModulePass *createEVMLinkRuntimePass(); +// Late passes. +FunctionPass *createEVMOptimizeLiveIntervals(); +FunctionPass *createEVMRegColoring(); +FunctionPass *createEVMSingleUseExpression(); +FunctionPass *createEVMStackify(); + // PassRegistry initialization declarations. void initializeEVMCodegenPreparePass(PassRegistry &); +void initializeEVMAllocaHoistingPass(PassRegistry &); void initializeEVMLowerIntrinsicsPass(PassRegistry &); void initializeEVMArgumentMovePass(PassRegistry &); void initializeEVMLinkRuntimePass(PassRegistry &); +void initializeEVMOptimizeLiveIntervalsPass(PassRegistry &); +void initializeEVMRegColoringPass(PassRegistry &); +void initializeEVMSingleUseExpressionPass(PassRegistry &); +void initializeEVMStackifyPass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { - EVMLinkRuntimePass() {} + EVMLinkRuntimePass() = default; PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; +struct EVMAllocaHoistingPass : PassInfoMixin { + EVMAllocaHoistingPass() = default; + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + } // namespace llvm #endif // LLVM_LIB_TARGET_EVM_EVM_H diff --git a/llvm/lib/Target/EVM/EVMAllocaHoisting.cpp b/llvm/lib/Target/EVM/EVMAllocaHoisting.cpp new file mode 100644 index 000000000000..b427e2847903 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAllocaHoisting.cpp @@ -0,0 +1,85 @@ +//===-- EVMAllocaHoisting.cpp - Hoist allocas to the entry ----*- 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 pass hoists the alloca instructions in the non-entry blocks to the +// entry blocks. +// Copied from NVPTX backend. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" + +#include "llvm/CodeGen/StackProtector.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" + +using namespace llvm; + +namespace { +// Hoisting the alloca instructions in the non-entry blocks to the entry +// block. +class EVMAllocaHoisting final : public FunctionPass { +public: + static char ID; // Pass ID + + EVMAllocaHoisting() : FunctionPass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addPreserved(); + } + + StringRef getPassName() const override { + return "EVM specific alloca hoisting"; + } + + bool runOnFunction(Function &F) override; +}; + +} // end anonymous namespace + +char EVMAllocaHoisting::ID = 0; + +static bool runImpl(Function &F) { + bool Modified = false; + Function::iterator I = F.begin(); + Instruction *FirstTerminatorInst = (I++)->getTerminator(); + + for (auto E = F.end(); I != E; ++I) { + for (auto BI = I->begin(), BE = I->end(); BI != BE;) { + auto *Alloca = dyn_cast(BI++); + if (Alloca && isa(Alloca->getArraySize())) { + Alloca->moveBefore(FirstTerminatorInst); + Modified = true; + } + } + } + + return Modified; +} + +bool EVMAllocaHoisting::runOnFunction(Function &F) { + if (skipFunction(F)) + return false; + + return runImpl(F); +} + +INITIALIZE_PASS( + EVMAllocaHoisting, "evm-alloca-hoisting", + "Hoisting alloca instructions in non-entry blocks to the entry block", + false, false) + +FunctionPass *llvm::createEVMAllocaHoistingPass() { + return new EVMAllocaHoisting; +} + +PreservedAnalyses EVMAllocaHoistingPass::run(Function &F, + FunctionAnalysisManager &AM) { + return runImpl(F) ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index 80bb2c8c2479..56eefceafa20 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -10,8 +10,10 @@ // //===----------------------------------------------------------------------===// -#include "EVMISelLowering.h" #include "EVM.h" + +#include "EVMISelLowering.h" +#include "EVMMachineFunctionInfo.h" #include "EVMTargetMachine.h" #include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/IR/DiagnosticInfo.h" @@ -351,6 +353,7 @@ SDValue EVMTargetLowering::LowerFormalArguments( fail(DL, DAG, "VarArg is not supported yet"); MachineFunction &MF = DAG.getMachineFunction(); + 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. @@ -374,6 +377,8 @@ SDValue EVMTargetLowering::LowerFormalArguments( DAG.getTargetConstant(InVals.size(), DL, MVT::i32)) : DAG.getUNDEF(In.VT)); + // Record the number of arguments. + MFI->addParam(); } return Chain; @@ -477,7 +482,6 @@ EVMTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { - assert((Outs.size() <= 1) && "EVM can only return up to one value"); if (!callingConvSupported(CallConv)) fail(DL, DAG, "EVM doesn't support non-C calling conventions"); diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 92d066c0b05e..16c7c1d68e57 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -104,6 +104,23 @@ class EVMTargetLowering final : public TargetLowering { return false; } + /// allowsMisalignedMemoryAccesses - Returns true if the target allows + /// unaligned memory accesses of the specified type. Returns whether it + /// is "fast" by reference in the second argument. + bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AddrSpace, + Align Alignment, + MachineMemOperand::Flags Flags, + unsigned *Fast) const override { + return AddrSpace != EVMAS::AS_STACK; + } + + // MVT::i256 is the only legal type, so DAG combiner should never reduce + // loads. + bool shouldReduceLoadWidth(SDNode *Load, ISD::LoadExtType ExtTy, + EVT NewVT) const override { + return false; + } + private: const EVMSubtarget *Subtarget; diff --git a/llvm/lib/Target/EVM/EVMInstrFormats.td b/llvm/lib/Target/EVM/EVMInstrFormats.td index 43becd9a2513..bd2e6b8b7b0b 100644 --- a/llvm/lib/Target/EVM/EVMInstrFormats.td +++ b/llvm/lib/Target/EVM/EVMInstrFormats.td @@ -25,37 +25,56 @@ def AS_storage : ASList<[AS.STORAGE]>; def AS_tstorage : ASList<[AS.TSTORAGE]>; // EVM general instruction format. -class EVMInst inst, string asmstr, int cost> - : Instruction { - bits<8> Inst = inst; // Instruction encoding. +class EVMInst + : StackRel, RegisterRel, Instruction { + // Instruction encoding. Bitwidth corresponds to the maximum + // size of possible EVM insturction. + // This is 'PUSH32 Imm'. 8 bits for opcode, and 256 bits for + // Imm. + bits<264> Inst; + bits<8> Opc; + bit StackBased = stack; + string BaseName = NAME; int GasCost = cost; let Namespace = "EVM"; let Pattern = []; let AsmString = asmstr; + let TSFlags{0} = stack; } // Normal instructions. Default instantiation of a EVMInst. -class NI pattern, +class NI pattern, bit stack, string asmstr = "", bits<8> inst = 0, int cost = 0> - : EVMInst { + : EVMInst { dag OutOperandList = oops; dag InOperandList = iops; let Pattern = pattern; + let Size = 1; + let Opc = inst; + let Inst{7-0} = Opc; let GasCost = cost; - let Defs = [ARGUMENTS]; } -// TODO: When implementing instructions stackification, this class -// will be extendent to define both register and stack-based instructions. -// As of now, all the instructions should be derived from it, which implies -// definition of only register-based ones. +// Generates both register and stack based versions of one actual instruction. multiclass I pattern_r, - string asmstr_r = "", bits<8> inst = 0, int cost = 0> { - def "" : NI; + string opcstr, string argstr_r, bits<8> inst, + int cost = 0, dag oops_s = (outs), dag iops_s = (ins), string argstr_s = ""> { + let isCodeGenOnly = 1 in + def "" : NI; + let BaseName = NAME in + def _S : NI; +} + +// For pseudo instructions that have no real counterparts. These instructions +// are used in codegen only and print out to assembly if "virtual register" mode +// is on. +let isCodeGenOnly = 1 in +class NRI pattern, string asmstr> + : NI { } class EVMPseudo pattern> - : NI { + : NI { let isPseudo = 1; let isCodeGenOnly = 1; } diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index 2fe98317cb44..69b98ae7152f 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -22,6 +22,16 @@ using namespace llvm; EVMInstrInfo::EVMInstrInfo() : EVMGenInstrInfo(EVM::ADJCALLSTACKDOWN, EVM::ADJCALLSTACKUP), RI() {} +bool EVMInstrInfo::isReallyTriviallyReMaterializable( + const MachineInstr &MI) const { + switch (MI.getOpcode()) { + case EVM::CONST_I256: + return true; + default: + return false; + } +} + void EVMInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h index ca559c83ca2e..8a015bdf2201 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.h +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -22,6 +22,16 @@ namespace llvm { +namespace EVMII { + +enum { + // TSF flag to check if this is a stack instruction. + IsStackPos = 0, + IsStackMask = 0x1, +}; + +} // namespace EVMII + class EVMInstrInfo final : public EVMGenInstrInfo { const EVMRegisterInfo RI; @@ -30,6 +40,8 @@ class EVMInstrInfo final : public EVMGenInstrInfo { const EVMRegisterInfo &getRegisterInfo() const { return RI; } + bool isReallyTriviallyReMaterializable(const MachineInstr &MI) const override; + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, bool KillSrc) const override; @@ -48,6 +60,16 @@ class EVMInstrInfo final : public EVMGenInstrInfo { bool reverseBranchCondition(SmallVectorImpl &Cond) const override; + + /// TSFlags extraction + static unsigned getTSFlag(const MachineInstr *MI, unsigned Pos, + unsigned Mask) { + return (MI->getDesc().TSFlags >> Pos) & Mask; + } + + static bool isStack(const MachineInstr *MI) { + return getTSFlag(MI, EVMII::IsStackPos, EVMII::IsStackMask); + } }; } // end namespace llvm diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index de95ef63d294..02d7d745f851 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -10,8 +10,6 @@ // //===----------------------------------------------------------------------===// -include "EVMInstrFormats.td" - //===----------------------------------------------------------------------===// // EVM-specific DAG Node Types. //===----------------------------------------------------------------------===// @@ -95,6 +93,40 @@ def imm128 : Operand, IntImmLeaf; +//===----------------------------------------------------------------------===// +// EVM Register to Stack instruction mapping +//===----------------------------------------------------------------------===// + +class StackRel; +def getStackOpcode : InstrMapping { + let FilterClass = "StackRel"; + let RowFields = ["BaseName"]; + let ColFields = ["StackBased"]; + let KeyCol = ["0"]; + let ValueCols = [["1"]]; +} + +//===----------------------------------------------------------------------===// +// EVM Stack to Register instruction mapping +//===----------------------------------------------------------------------===// + +class RegisterRel; +def getRegisterOpcode : InstrMapping { + let FilterClass = "RegisterRel"; + let RowFields = ["BaseName"]; + let ColFields = ["StackBased"]; + let KeyCol = ["1"]; + let ValueCols = [["0"]]; +} + + +//===----------------------------------------------------------------------===// +// EVM Instruction Format Definitions. +//===----------------------------------------------------------------------===// + +include "EVMInstrFormats.td" + + //===----------------------------------------------------------------------===// // Custom DAG Selection Operations. //===----------------------------------------------------------------------===// @@ -160,34 +192,34 @@ def SELECT // Call sequence markers. let isCodeGenOnly = 1, Defs = [SP], Uses = [SP] in { def ADJCALLSTACKDOWN - : NI<(outs), (ins i256imm:$amt1, i256imm:$amt2), - [(EVMcallseq_start timm:$amt1, timm:$amt2)], - "#ADJCALLSTACKDOWN $amt1 $amt2">; + : EVMPseudo<(outs), (ins i256imm:$amt1, i256imm:$amt2), + [(EVMcallseq_start timm:$amt1, timm:$amt2)]>; def ADJCALLSTACKUP - : NI<(outs), (ins i256imm:$amt1, i256imm:$amt2), - [(EVMcallseq_end timm:$amt1, timm:$amt2)], - "#ADJCALLSTACKUP $amt1 $amt2">; + : EVMPseudo<(outs), (ins i256imm:$amt1, i256imm:$amt2), + [(EVMcallseq_end timm:$amt1, timm:$amt2)]>; } -let hasSideEffects = 1, Uses = [ARGUMENTS] in +let isCodeGenOnly = 1 in { +let hasSideEffects = 1 in def ARGUMENT - : NI<(outs GPR:$res), (ins i256imm:$argno), + : NRI<(outs GPR:$res), (ins i256imm:$argno), [(set GPR:$res, (EVMargument timm:$argno))], "ARGUMENT $res, $argno">; // This is not real EVM instruction. It should be eliminted while // stackification. let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in -defm CONST_I256 - : I<(outs GPR:$res), (ins i256imm:$imm), [(set GPR:$res, imm:$imm)], +def CONST_I256 + : NRI<(outs GPR:$res), (ins i256imm:$imm), [(set GPR:$res, imm:$imm)], "CONST_I256 $res, $imm">; // This is not real EVM instruction. It should be eliminted while // stackification. let isAsCheapAsAMove = 1 in -defm COPY_I256 - : I<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; +def COPY_I256 + : NRI<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; +} def : Pat<(i256 (EVMTargetAddrWrapper tglobaladdr:$addr)), (CONST_I256 tglobaladdr:$addr)>; @@ -204,19 +236,19 @@ let Uses = [SP], isCall = 1 in { // observe CALL nodes with all of the expected variadic uses and defs. let isPseudo = 1 in def CALL_PARAMS - : NI<(outs), (ins jmptarget:$callee, variable_ops), [], + : NRI<(outs), (ins jmptarget:$callee, variable_ops), [], "CALL_PARAMS\t$callee">; let variadicOpsAreDefs = 1, usesCustomInserter = 1, isPseudo = 1 in def CALL_RESULTS - : NI<(outs), (ins variable_ops), [], "CALL_RESULTS">; + : NRI<(outs), (ins variable_ops), [], "CALL_RESULTS">; // Note that instructions with variable_ops have custom printers in // EVMInstPrinter.cpp. let variadicOpsAreDefs = 1 in def FCALL - : NI<(outs), (ins jmptarget:$callee, variable_ops), [], + : NRI<(outs), (ins jmptarget:$callee, variable_ops), [], "FCALL\t$callee">; } // Uses = [SP], isCall = 1 @@ -225,10 +257,10 @@ def FCALL // EVM arithmetic instructions. //===----------------------------------------------------------------------===// -multiclass BinaryInst inst, int cost> +multiclass BinaryInst inst, int cost> : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), [(set GPR:$dst, (node GPR:$lhs, GPR:$rhs))], - opcodeStr#" $dst, $lhs, $rhs", inst, cost>; + opcode_str, " $dst, $lhs, $rhs", inst, cost>; let isCommutable = 1 in { defm ADD : BinaryInst; @@ -244,23 +276,23 @@ defm ADDMOD : I<(outs GPR:$dst), (ins GPR:$add_op1, GPR:$add_op2, GPR:$denom), [(set GPR:$dst, (int_evm_addmod GPR:$add_op1, GPR:$add_op2, GPR:$denom))], - "ADDMOD $dst, $add_op1, $add_op2, $denom", 0x08, 8>; + "ADDMOD", " $dst, $add_op1, $add_op2, $denom", 0x08, 8>; defm MULMOD : I<(outs GPR:$dst), (ins GPR:$mul_op1, GPR:$mul_op2, GPR:$denom), [(set GPR:$dst, (int_evm_mulmod GPR:$mul_op1, GPR:$mul_op2, GPR:$denom))], - "MULMOD $dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; + "MULMOD", " $dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; defm EXP : I<(outs GPR:$dst), (ins GPR:$base, GPR:$exp), [(set GPR:$dst, (int_evm_exp GPR:$base, GPR:$exp))], - "EXP $dst, $base, $exp", 0x0A, 10>; + "EXP", " $dst, $base, $exp", 0x0A, 10>; defm SIGNEXTEND : I<(outs GPR:$dst), (ins GPR:$size, GPR:$src), [(set GPR:$dst, (int_evm_signextend GPR:$size, GPR:$src))], - "SIGNEXTEND $dst, $size, $src", 0x0B, 5>; + "SIGNEXTEND", " $dst, $size, $src", 0x0B, 5>; // The first operand of SIGNEXTEND is the type size in bytes of // the value being extendent minus one. @@ -291,11 +323,11 @@ def : Pat<(int_evm_smod GPR:$op1, GPR:$op2), (SMOD GPR:$op1, GPR:$op2)>; // EVM comparison instructions. //===----------------------------------------------------------------------===// -multiclass ComparisonInst inst, int cost> : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), [(set GPR:$dst, (setcc GPR:$lhs, GPR:$rhs, cond))], - opcodeStr#" $dst, $lhs, $rhs", inst, cost>; + opcode_str, " $dst, $lhs, $rhs", inst, cost>; let isCompare = 1 in { defm ULT : ComparisonInst; @@ -307,7 +339,7 @@ defm EQ : ComparisonInst; defm ISZERO : I<(outs GPR:$dst), (ins GPR:$src), [(set GPR:$dst, (setcc GPR:$src, 0, SETEQ))], - "ISZERO $dst, $src", 0x15, 3>; + "ISZERO", " $dst, $src", 0x15, 3>; } // isCompare = 1 // Patterns for comparison operations that have no @@ -332,27 +364,32 @@ let isCommutable = 1 in { } defm NOT : I<(outs GPR:$dst), (ins GPR:$src), [(set GPR:$dst, (not GPR:$src))], - "NOT $dst, $src", 0x19, 3>; + "NOT", " $dst, $src", 0x19, 3>; let mayLoad = 1 in defm SHA3 : I<(outs GPR:$dst), (ins GPR:$offset, GPR:$size), [(set GPR:$dst, (int_evm_sha3 GPR:$offset, GPR:$size))], - "SHA3 $dst, $offset, $size", 0x20, 30>; + "SHA3", " $dst, $offset, $size", 0x20, 30>; defm BYTE : I<(outs GPR:$dst), (ins GPR:$idx, GPR:$val), [(set GPR:$dst, (int_evm_byte GPR:$idx, GPR:$val))], - "BYTE $dst, $idx, $val", 0x1a, 3>; + "BYTE", " $dst, $idx, $val", 0x1a, 3>; //===----------------------------------------------------------------------===// // EVM shift instructions. //===----------------------------------------------------------------------===// -defm SHL : BinaryInst; -defm SHR : BinaryInst; -defm SAR : BinaryInst; +multiclass ShiftInst inst, int cost> + : I<(outs GPR:$dst), (ins GPR:$val, GPR:$shift), + [(set GPR:$dst, (node GPR:$shift, GPR:$val))], + opcode_str, " $dst, $shift, $val", inst, cost>; + +defm SHL : ShiftInst; +defm SHR : ShiftInst; +defm SAR : ShiftInst; def : Pat<(int_evm_shl GPR:$op1, GPR:$op2), (SHL GPR:$op1, GPR:$op2)>; def : Pat<(int_evm_shr GPR:$op1, GPR:$op2), (SHR GPR:$op1, GPR:$op2)>; @@ -367,19 +404,19 @@ let isBranch = 1, isTerminator = 1 in { // The condition operand is a boolean value which EVM represents as i256. defm JUMPI : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], - "JUMPI $dst, $cond", 0x56, 10>; + "JUMPI", " $dst, $cond", 0x57, 10>; let isBarrier = 1 in defm JUMP - : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP $dst", 0x57, 10>; + : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", " $dst", 0x56, 8>; } // isBranch = 1, isTerminator = 1 // This isn't really a control flow instruction, but it should be used to mark // destination of jump instructions. -defm : I<(outs), (ins), [], "JUMPDEST", 0x57, 10>; +defm JUMPDEST : I<(outs), (ins), [], "JUMPDEST", "", 0x5B, 1>; let isBarrier = 1, isTerminator = 1, isReturn = 1 in -def RET : NI<(outs), (ins variable_ops), [(EVMret)], "RET">; +def RET : NRI<(outs), (ins variable_ops), [(EVMret)], "RET">; //===----------------------------------------------------------------------===// @@ -390,57 +427,57 @@ let mayLoad = 1 in { defm MLOAD : I<(outs GPR:$dst), (ins GPR:$offset), [(set GPR:$dst, (load_heap GPR:$offset))], - "MLOAD $dst, $offset", 0x51, 3>; + "MLOAD", " $dst, $offset", 0x51, 3>; defm SLOAD : I<(outs GPR:$dst), (ins GPR:$key), [(set GPR:$dst, (load_storage GPR:$key))], - "SLOAD $dst, $key", 0x54, 100>; + "SLOAD", " $dst, $key", 0x54, 100>; defm TLOAD : I<(outs GPR:$dst), (ins GPR:$key), [(set GPR:$dst, (load_tstorage GPR:$key))], - "TLOAD $dst, $key", 0x5c, 100>; + "TLOAD", " $dst, $key", 0x5c, 100>; } let mayStore = 1 in { defm MSTORE : I<(outs), (ins GPR:$offset, GPR:$val), [(store_heap GPR:$val, GPR:$offset)], - "MSTORE $offset, $val", 0x52, 3>; + "MSTORE", " $offset, $val", 0x52, 3>; defm MSTORE8 : I<(outs), (ins GPR:$offset, GPR:$val), [(int_evm_mstore8 GPR:$offset, GPR:$val)], - "MSTORE8 $offset, $val", 0x53, 3>; + "MSTORE8", " $offset, $val", 0x53, 3>; defm SSTORE : I<(outs), (ins GPR:$key, GPR:$val), [(store_storage GPR:$val, GPR:$key)], - "SSTORE $key, $val", 0x55, 100>; + "SSTORE", " $key, $val", 0x55, 100>; defm TSTORE : I<(outs), (ins GPR:$key, GPR:$val), [(store_tstorage GPR:$val, GPR:$key)], - "TSTORE $key, $val", 0x5d, 100>; + "TSTORE", " $key, $val", 0x5d, 100>; } let mayStore = 1, mayLoad = 1 in defm MCOPY : I<(outs), (ins GPR:$dst, GPR:$src, GPR:$size), [], - "MCOPY $dst, $src, $size", 0x5E, 3>; + "MCOPY", " $dst, $src, $size", 0x5E, 3>; def : Pat<(EVMMemcpy_heap GPR:$dst, GPR:$src, GPR:$size), (MCOPY GPR:$dst, GPR:$src, GPR:$size)>; let hasSideEffects = 1 in { defm PC : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_pc))], - "PC $dst", 0x58, 2>; + "PC", " $dst", 0x58, 2>; defm GAS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gas))], - "GAS $dst", 0x5A, 2>; + "GAS", " $dst", 0x5A, 2>; } defm MSIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_msize))], - "MSIZE $dst", 0x59, 2>; + "MSIZE", " $dst", 0x59, 2>; // The i8 store is handled a speciall way, as EVM has a dedicated instruction // for this. @@ -463,27 +500,29 @@ def add_like: PatFrags<(ops node:$lhs, node:$rhs), let mayLoad = 1 in def STACK_LOAD - : NI<(outs GPR:$dst), (ins GPR:$fi), [], "STACK_LOAD $dst, $fi">; + : NRI<(outs GPR:$dst), (ins GPR:$fi, i256imm:$off), [], + "STACK_LOAD $dst, $fi, $off">; -let mayStore = 1, hasSideEffects = 1 in +let mayStore = 1 in def STACK_STORE - : NI<(outs), (ins GPR:$fi, GPR:$val), [], "STACK_STORE $fi, $val">; + : NRI<(outs), (ins GPR:$fi, i256imm:$off, GPR:$val), [], + "STACK_STORE $fi, $off, $val">; def : Pat<(i256 frameindex:$fi), (TargetFI $fi)>; -def : Pat<(load_stack frameindex:$fi), (STACK_LOAD (TargetFI $fi))>; -def : Pat<(load_stack (add_like frameindex:$fi, GPR:$off)), - (STACK_LOAD (ADD (TargetFI $fi), GPR:$off))>; +def : Pat<(load_stack frameindex:$fi), (STACK_LOAD (TargetFI $fi), 0)>; +def : Pat<(load_stack (add_like frameindex:$fi, imm:$off)), + (STACK_LOAD (TargetFI $fi), imm:$off)>; def : Pat<(store_stack GPR:$val, frameindex:$fi), - (STACK_STORE (TargetFI $fi), GPR:$val)>; -def : Pat<(store_stack GPR:$val, (add_like frameindex:$fi, GPR:$off)), - (STACK_STORE (ADD (TargetFI $fi), GPR:$off), GPR:$val)>; + (STACK_STORE (TargetFI $fi), 0, GPR:$val)>; +def : Pat<(store_stack GPR:$val, (add_like frameindex:$fi, imm:$off)), + (STACK_STORE (TargetFI $fi), imm:$off, GPR:$val)>; // The following patterns shouldn't be used for lowering a real code // generated by FE. We add them only to be able to compile target independent // LIT tests. -def : Pat<(load_stack GPR:$off), (STACK_LOAD GPR:$off)>; -def : Pat<(store_stack GPR:$val, GPR:$off), (STACK_STORE GPR:$off, GPR:$val)>; -def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, GPR:$val)>; +def : Pat<(load_stack GPR:$off), (STACK_LOAD GPR:$off, 0)>; +def : Pat<(store_stack GPR:$val, GPR:$off), (STACK_STORE GPR:$off, 0, GPR:$val)>; +def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, 0, GPR:$val)>; //===----------------------------------------------------------------------===// @@ -492,79 +531,80 @@ def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, GPR:$val)>; defm ADDRESS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_address))], - "ADDRESS $dst", 0x30, 2>; + "ADDRESS", " $dst", 0x30, 2>; defm BALANCE : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_balance GPR:$addr))], - "BALANCE $dst, $addr", 0x31, 100>; + "BALANCE", " $dst, $addr", 0x31, 100>; defm ORIGIN : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], - "ORIGIN $dst", 0x32, 2>; + "ORIGIN", " $dst", 0x32, 2>; defm CALLER : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_caller))], - "CALLER $dst", 0x32, 2>; + "CALLER", " $dst", 0x33, 2>; defm CALLVALUE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_callvalue))], - "CALLVALUE $dst", 0x34, 2>; + "CALLVALUE", " $dst", 0x34, 2>; +let mayLoad = 1 in defm CALLDATALOAD : I<(outs GPR:$dst), (ins GPR:$off), [(set GPR:$dst, (int_evm_calldataload GPR:$off))], - "CALLDATALOAD $dst, $off", 0x35, 2>; + "CALLDATALOAD", " $dst, $off", 0x35, 2>; def : Pat<(load_call_data GPR:$off), (CALLDATALOAD GPR:$off)>; defm CALLDATASIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_calldatasize))], - "CALLDATASIZE $dst", 0x36, 2>; + "CALLDATASIZE", " $dst", 0x36, 2>; -let mayStore = 1, hasSideEffects = 1 in +let mayStore = 1 in defm CALLDATACOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], - "CALLDATACOPY $dst_off, $src_off, $size", 0x37, 3>; + "CALLDATACOPY", " $dst_off, $src_off, $size", 0x37, 3>; def : Pat<(EVMMemcpy_call_data GPR:$dst, GPR:$src, GPR:$size), (CALLDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; defm CODESIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], - "CODESIZE $dst", 0x38, 2>; + "CODESIZE", " $dst", 0x38, 2>; -let mayStore = 1, hasSideEffects = 1 in +let mayStore = 1 in defm CODECOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], - "CODECOPY $dst_off, $src_off, $size", 0x39, 3>; + "CODECOPY", " $dst_off, $src_off, $size", 0x39, 3>; def : Pat<(EVMMemcpy_code GPR:$dst, GPR:$src, GPR:$size), (CODECOPY GPR:$dst, GPR:$src, GPR:$size)>; defm GASPRICE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], - "GASPRICE $dst", 0x3A, 2>; + "GASPRICE", " $dst", 0x3A, 2>; defm EXTCODESIZE : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_extcodesize GPR:$addr))], - "EXTCODESIZE $dst, $addr", 0x3B, 100>; + "EXTCODESIZE", " $dst, $addr", 0x3B, 100>; -let mayStore = 1, hasSideEffects = 1 in +let mayStore = 1 in defm EXTCODECOPY : I<(outs), (ins GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size), [(int_evm_extcodecopy GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size)], - "EXTCODECOPY $addr, $dst_off, $src_off, $size", 0x3C, 100>; + "EXTCODECOPY", " $addr, $dst_off, $src_off, $size", 0x3C, 100>; defm RETURNDATASIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_returndatasize))], - "RETURNDATASIZE $dst", 0x3D, 2>; + "RETURNDATASIZE", " $dst", 0x3D, 2>; -let mayStore = 1, hasSideEffects = 1 in +let mayStore = 1 in defm RETURNDATACOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], - "RETURNDATACOPY $dst_off, $src_off, $size", 0x3E, 3>; + "RETURNDATACOPY", " $dst_off, $src_off, $size", 0x3E, 3>; def : Pat<(EVMMemcpy_return_data GPR:$dst, GPR:$src, GPR:$size), (RETURNDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; @@ -572,54 +612,54 @@ def : Pat<(EVMMemcpy_return_data GPR:$dst, GPR:$src, GPR:$size), defm EXTCODEHASH : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_extcodehash GPR:$addr))], - "EXTCODEHASH $dst, $addr", 0x3F, 100>; + "EXTCODEHASH", " $dst, $addr", 0x3F, 100>; defm BLOCKHASH : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_blockhash GPR:$addr))], - "BLOCKHASH $dst, $addr", 0x40, 20>; + "BLOCKHASH", " $dst, $addr", 0x40, 20>; defm COINBASE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], - "COINBASE $dst", 0x41, 2>; + "COINBASE", " $dst", 0x41, 2>; defm TIMESTAMP : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], - "TIMESTAMP $dst", 0x42, 2>; + "TIMESTAMP", " $dst", 0x42, 2>; defm NUMBER : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], - "NUMBER $dst", 0x43, 2>; + "NUMBER", " $dst", 0x43, 2>; defm DIFFICULTY : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], - "DIFFICULTY $dst", 0x44, 2>; + "DIFFICULTY", " $dst", 0x44, 2>; defm GASLIMIT : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], - "GASLIMIT $dst", 0x45, 2>; + "GASLIMIT", " $dst", 0x45, 2>; defm CHAINID : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], - "CHAINID $dst", 0x46, 2>; + "CHAINID", " $dst", 0x46, 2>; let hasSideEffects = 1 in defm SELFBALANCE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_selfbalance))], - "SELFBALANCE $dst", 0x47, 5>; + "SELFBALANCE", " $dst", 0x47, 5>; defm BASEFEE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], - "BASEFEE $dst", 0x48, 2>; + "BASEFEE", " $dst", 0x48, 2>; defm BLOBHASH : I<(outs GPR:$dst), (ins GPR:$index), [(set GPR:$dst, (int_evm_blobhash GPR:$index))], - "BLOBHASH $dst, $index", 0x49, 3>; + "BLOBHASH", " $dst, $index", 0x49, 3>; defm BLOBBASEFEE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], - "BLOBBASEFEE $dst", 0x4A, 2>; + "BLOBBASEFEE", " $dst", 0x4A, 2>; //===----------------------------------------------------------------------===// @@ -630,28 +670,28 @@ let mayLoad = 1, hasSideEffects = 1 in { defm LOG0 : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_log0 GPR:$offset, GPR:$size)], - "LOG0 $offset, $size", 0xA0, 375>; + "LOG0", " $offset, $size", 0xA0, 375>; defm LOG1 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1), [(int_evm_log1 GPR:$offset, GPR:$size, GPR:$t1)], - "LOG1 $offset, $size, $t1", 0xA1, 750>; + "LOG1", " $offset, $size, $t1", 0xA1, 750>; defm LOG2 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2), [(int_evm_log2 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2)], - "LOG2 $offset, $size, $t1, $t2", 0xA2, 1125>; + "LOG2", " $offset, $size, $t1, $t2", 0xA2, 1125>; defm LOG3 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3), [(int_evm_log3 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3)], - "LOG3 $offset, $size, $t1, $t2, $t3", 0xA3, 1500>; + "LOG3", " $offset, $size, $t1, $t2, $t3", 0xA3, 1500>; defm LOG4 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, GPR:$t4), [(int_evm_log4 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, GPR:$t4)], - "LOG4 $offset, $size, $t1, $t2, $t3, $t4", 0xA4, 1875>; + "LOG4", " $offset, $size, $t1, $t2, $t3, $t4", 0xA4, 1875>; } //===----------------------------------------------------------------------===// @@ -662,7 +702,7 @@ let mayLoad = 1, hasSideEffects = 1 in defm CREATE : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size), [(set GPR:$dst, (int_evm_create GPR:$value, GPR:$offset, GPR:$size))], - "CREATE $dst, $value, $offset, $size", 0xF0, 32000>; + "CREATE", " $dst, $value, $offset, $size", 0xF0, 32000>; let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { defm CALL @@ -670,7 +710,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_call GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "CALL $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + "CALL", " $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", 0xF1, 100>; defm CALLCODE @@ -678,7 +718,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_callcode GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "CALLCODE $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + "CALLCODE", " $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", 0xF2, 100>; defm DELEGATECALL @@ -686,7 +726,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_delegatecall GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "DELEGATECALL $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + "DELEGATECALL", " $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", 0xF4, 100>; defm STATICCALL @@ -694,7 +734,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_staticcall GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "STATICCALL $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + "STATICCALL", " $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", 0xFA, 100>; } @@ -703,38 +743,380 @@ defm CREATE2 : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size, GPR:$salt), [(set GPR:$dst, (int_evm_create2 GPR:$value, GPR:$offset, GPR:$size, GPR:$salt))], - "CREATE2 $dst, $value, $offset, $size, $salt", 0xF5, 32000>; + "CREATE2", " $dst, $value, $offset, $size, $salt", 0xF5, 32000>; //===----------------------------------------------------------------------===// // EVM instructions to return with error. //===----------------------------------------------------------------------===// -let isTerminator = 1, isBarrier = 1 in { +let isTerminator = 1, isBarrier = 1, isReturn = 1 in { defm REVERT : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_revert GPR:$offset, GPR:$size)], - "REVERT $offset, $size", 0xFD, 0>; + "REVERT", " $offset, $size", 0xFD, 0>; defm RETURN : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_return GPR:$offset, GPR:$size)], - "RETURN $offset, $size", 0xF3, 0>; + "RETURN", " $offset, $size", 0xF3, 0>; } let hasSideEffects = 1 in { defm SELFDESTRUCT : I<(outs), (ins GPR:$addr), [(int_evm_selfdestruct GPR:$addr)], - "SELFDESTRUCT $addr", 0xFF, 5000>; + "SELFDESTRUCT", " $addr", 0xFF, 5000>; - defm STOP : I<(outs), (ins), [(int_evm_stop)], "STOP", 0x00, 0>; - - defm INVALID : I<(outs), (ins), [(int_evm_invalid)], "INVALID", 0xFE, 0>; + let isTerminator = 1, isBarrier = 1, isReturn = 1 in { + defm STOP : I<(outs), (ins), [(int_evm_stop)], "STOP", "", 0x00, 0>; + defm INVALID : I<(outs), (ins), [(int_evm_invalid)], "INVALID", "", 0xFE, 0>; + } } //===----------------------------------------------------------------------===// // EVM instructions for stack manipulation. //===----------------------------------------------------------------------===// -defm POP : I<(outs), (ins GPR:$val), [(int_evm_pop GPR:$val)], - "POP $val", 0x50, 2>; + +def POP_KILL : EVMPseudo<(outs), (ins GPR:$val), [(int_evm_pop GPR:$val)]>; + +defm POP : I<(outs), (ins), [], "POP", "", 0x50, 2>; + +foreach I = {1-16} in { + defm DUP#I : I<(outs), (ins), [], + "DUP"#I, "", !add(I, 0x7F), 3>; +} + +foreach I = {1-16} in { + defm SWAP#I : I<(outs), (ins), [], + "SWAP"#I, "", !add(I, 0x8F), 3>; +} + +defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; + +def PUSH8_LABEL : NI<(outs), (ins jmptarget:$dst), [], false, "", 0, 0> { + let isCodeGenOnly = 1; +} + +// Define register PUSH* instructions +foreach I = {1 - 32} in { + def PUSH#I : NI<(outs), (ins i256imm:$imm), [], false, "PUSH"#I#" $imm", + !add(I, 0x5F), 3> { + let BaseName = "PUSH"#I; + let Size = !add(I, 1); + let isCodeGenOnly = 1; + } +} + +// Define stack PUSH* instructions +def PUSH1_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH1 $imm", + 0x60, 3> { + bits<8> imm; + let Inst{15-8} = Opc; + let Inst{7-0} = imm{7-0}; + let Size = 2; + let BaseName = "PUSH1"; +} + +def PUSH2_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH2 $imm", + 0x61, 3> { + bits<16> imm; + let Inst{23-16} = Opc; + let Inst{15-0} = imm{15-0}; + let Size = 3; + let BaseName = "PUSH2"; +} + +def PUSH3_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH3 $imm", + 0x62, 3> { + bits<24> imm; + let Inst{31-24} = Opc; + let Inst{23-0} = imm{23-0}; + let Size = 4; + let BaseName = "PUSH3"; +} + +def PUSH4_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH4 $imm", + 0x63, 3> { + bits<32> imm; + let Inst{39-32} = Opc; + let Inst{31-0} = imm{31-0}; + let Size = 5; + let BaseName = "PUSH4"; +} + +def PUSH5_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH5 $imm", + 0x64, 3> { + bits<40> imm; + let Inst{47-40} = Opc; + let Inst{39-0} = imm{39-0}; + let Size = 6; + let BaseName = "PUSH5"; +} + +def PUSH6_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH6 $imm", + 0x65, 3> { + bits<48> imm; + let Inst{55-48} = Opc; + let Inst{47-0} = imm{47-0}; + let Size = 7; + let BaseName = "PUSH6"; +} + +def PUSH7_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH7 $imm", + 0x66, 3> { + bits<56> imm; + let Inst{63-56} = Opc; + let Inst{55-0} = imm{55-0}; + let Size = 8; + let BaseName = "PUSH7"; +} + +def PUSH8_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH8 $imm", + 0x67, 3> { + bits<64> imm; + let Inst{71-64} = Opc; + let Inst{63-0} = imm{63-0}; + let Size = 9; + let BaseName = "PUSH8"; +} + +def PUSH9_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH9 $imm", + 0x68, 3> { + bits<72> imm; + let Inst{79-72} = Opc; + let Inst{71-0} = imm{71-0}; + let Size = 10; + let BaseName = "PUSH9"; + let DecoderMethod = "decodePUSH<9>"; +} + +def PUSH10_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH10 $imm", + 0x69, 3> { + bits<80> imm; + let Inst{87-80} = Opc; + let Inst{79-0} = imm{79-0}; + let Size = 11; + let BaseName = "PUSH10"; + let DecoderMethod = "decodePUSH<10>"; +} + +def PUSH11_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH11 $imm", + 0x6a, 3> { + bits<88> imm; + let Inst{95-88} = Opc; + let Inst{87-0} = imm{87-0}; + let Size = 12; + let BaseName = "PUSH11"; + let DecoderMethod = "decodePUSH<11>"; +} + +def PUSH12_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH12 $imm", + 0x6b, 3> { + bits<96> imm; + let Inst{103-96} = Opc; + let Inst{95-0} = imm{95-0}; + let Size = 13; + let BaseName = "PUSH12"; + let DecoderMethod = "decodePUSH<12>"; +} + +def PUSH13_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH13 $imm", + 0x6c, 3> { + bits<104> imm; + let Inst{111-104} = Opc; + let Inst{103-0} = imm{103-0}; + let Size = 14; + let BaseName = "PUSH13"; + let DecoderMethod = "decodePUSH<13>"; +} + +def PUSH14_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH14 $imm", + 0x6d, 3> { + bits<112> imm; + let Inst{119-112} = Opc; + let Inst{111-0} = imm{111-0}; + let Size = 15; + let BaseName = "PUSH14"; + let DecoderMethod = "decodePUSH<14>"; +} + +def PUSH15_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH15 $imm", + 0x6e, 3> { + bits<120> imm; + let Inst{127-120} = Opc; + let Inst{119-0} = imm{119-0}; + let Size = 16; + let BaseName = "PUSH15"; + let DecoderMethod = "decodePUSH<15>"; +} + +def PUSH16_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH16 $imm", + 0x6f, 3> { + bits<128> imm; + let Inst{135-128} = Opc; + let Inst{127-0} = imm{127-0}; + let Size = 17; + let BaseName = "PUSH16"; + let DecoderMethod = "decodePUSH<16>"; +} + +def PUSH17_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH17 $imm", + 0x70, 3> { + bits<136> imm; + let Inst{143-136} = Opc; + let Inst{135-0} = imm{135-0}; + let Size = 18; + let BaseName = "PUSH17"; + let DecoderMethod = "decodePUSH<17>"; +} + +def PUSH18_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH18 $imm", + 0x71, 3> { + bits<144> imm; + let Inst{151-144} = Opc; + let Inst{143-0} = imm{143-0}; + let Size = 19; + let BaseName = "PUSH18"; + let DecoderMethod = "decodePUSH<18>"; +} + +def PUSH19_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH19 $imm", + 0x72, 3> { + bits<152> imm; + let Inst{159-152} = Opc; + let Inst{151-0} = imm{151-0}; + let Size = 20; + let BaseName = "PUSH19"; + let DecoderMethod = "decodePUSH<19>"; +} + +def PUSH20_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH20 $imm", + 0x73, 3> { + bits<160> imm; + let Inst{167-160} = Opc; + let Inst{159-0} = imm{159-0}; + let Size = 21; + let BaseName = "PUSH20"; + let DecoderMethod = "decodePUSH<20>"; +} + +def PUSH21_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH21 $imm", + 0x74, 3> { + bits<168> imm; + let Inst{175-168} = Opc; + let Inst{167-0} = imm{167-0}; + let Size = 22; + let BaseName = "PUSH21"; + let DecoderMethod = "decodePUSH<21>"; +} + +def PUSH22_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH22 $imm", + 0x75, 3> { + bits<176> imm; + let Inst{183-176} = Opc; + let Inst{175-0} = imm{175-0}; + let Size = 23; + let BaseName = "PUSH22"; + let DecoderMethod = "decodePUSH<22>"; +} + +def PUSH23_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH23 $imm", + 0x76, 3> { + bits<184> imm; + let Inst{191-184} = Opc; + let Inst{183-0} = imm{183-0}; + let Size = 24; + let BaseName = "PUSH23"; + let DecoderMethod = "decodePUSH<23>"; +} + +def PUSH24_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH24 $imm", + 0x77, 3> { + bits<192> imm; + let Inst{199-192} = Opc; + let Inst{191-0} = imm{191-0}; + let Size = 25; + let BaseName = "PUSH24"; + let DecoderMethod = "decodePUSH<24>"; +} + +def PUSH25_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH25 $imm", + 0x78, 3> { + bits<200> imm; + let Inst{207-200} = Opc; + let Inst{199-0} = imm{199-0}; + let Size = 26; + let BaseName = "PUSH25"; + let DecoderMethod = "decodePUSH<25>"; +} + +def PUSH26_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH26 $imm", + 0x79, 3> { + bits<208> imm; + let Inst{215-208} = Opc; + let Inst{207-0} = imm{207-0}; + let Size = 27; + let BaseName = "PUSH26"; + let DecoderMethod = "decodePUSH<26>"; +} + +def PUSH27_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH27 $imm", + 0x7a, 3> { + bits<216> imm; + let Inst{223-216} = Opc; + let Inst{215-0} = imm{215-0}; + let Size = 28; + let BaseName = "PUSH27"; + let DecoderMethod = "decodePUSH<27>"; +} + +def PUSH28_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH28 $imm", + 0x7b, 3> { + bits<224> imm; + let Inst{231-224} = Opc; + let Inst{223-0} = imm{223-0}; + let Size = 29; + let BaseName = "PUSH28"; + let DecoderMethod = "decodePUSH<28>"; +} + +def PUSH29_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH29 $imm", + 0x7c, 3> { + bits<232> imm; + let Inst{239-232} = Opc; + let Inst{231-0} = imm{231-0}; + let Size = 30; + let BaseName = "PUSH29"; + let DecoderMethod = "decodePUSH<29>"; +} + +def PUSH30_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH30 $imm", + 0x7d, 3> { + bits<240> imm; + let Inst{247-240} = Opc; + let Inst{239-0} = imm{239-0}; + let Size = 31; + let BaseName = "PUSH30"; + let DecoderMethod = "decodePUSH<30>"; +} + +def PUSH31_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH31 $imm", + 0x7e, 3> { + bits<248> imm; + let Inst{255-248} = Opc; + let Inst{247-0} = imm{247-0}; + let Size = 32; + let BaseName = "PUSH31"; + let DecoderMethod = "decodePUSH<31>"; +} + +def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32 $imm", + 0x7f, 3> { + bits<256> imm; + let Inst{263-256} = Opc; + let Inst{255-0} = imm{255-0}; + let Size = 33; + let BaseName = "PUSH32"; + let DecoderMethod = "decodePUSH<32>"; +} diff --git a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp index 65c5226da1c2..a6692fbe506f 100644 --- a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp +++ b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp @@ -36,11 +36,6 @@ using namespace llvm; -namespace llvm { -ModulePass *createEVMLinkRuntimePass(); -void initializeEVMLinkRuntimePass(PassRegistry &); -} // namespace llvm - static ExitOnError ExitOnErr; namespace { @@ -85,6 +80,13 @@ static bool EVMLinkRuntimeImpl(Module &M, const char *ModuleToLink) { Err.print("Unable to parse evm-stdlib.ll", errs()); exit(1); } + + for (auto &F : M.functions()) { + if (!F.isDeclaration()) { + F.addFnAttr(Attribute::NoInline); + } + } + bool LinkErr = false; LinkErr = L.linkInModule( std::move(RTM), Flags, [](Module &M, const StringSet<> &GVS) { diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index 06b73ed27d78..365734db49f2 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -12,7 +12,10 @@ //===----------------------------------------------------------------------===// #include "EVMMCInstLower.h" +#include "EVMInstrInfo.h" #include "MCTargetDesc/EVMMCExpr.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -22,8 +25,33 @@ using namespace llvm; -#define GET_REGINFO_HEADER -#include "EVMGenRegisterInfo.inc" +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. + 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); + } + + // Remove register operands. + for (auto I = OutMI.getNumOperands(); I; --I) { + auto &MO = OutMI.getOperand(I - 1); + if (MO.isReg()) { + OutMI.erase(&MO); + } + } +} MCSymbol * EVMMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { @@ -102,6 +130,10 @@ 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_MachineBasicBlock: MCOp = MCOperand::createExpr( MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx)); @@ -113,10 +145,11 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); break; } - OutMI.addOperand(MCOp); } - if (Desc.variadicOpsAreDefs()) + if (!EVMKeepRegisters) + removeRegisterOperands(MI, OutMI); + else if (Desc.variadicOpsAreDefs()) OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs())); } diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.h b/llvm/lib/Target/EVM/EVMMCInstLower.h index 3f4dbc84da90..071fa4dbfb71 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.h +++ b/llvm/lib/Target/EVM/EVMMCInstLower.h @@ -27,8 +27,8 @@ class TargetRegisterClass; class LLVM_LIBRARY_VISIBILITY EVMMCInstLower { // TODO: Once stackification is implemented this should be removed, // see the comments in EVMAsmPrinter. - typedef DenseMap VRegMap; - typedef DenseMap VRegRCMap; + using VRegMap = DenseMap; + using VRegRCMap = DenseMap; MCContext &Ctx; AsmPrinter &Printer; diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp new file mode 100644 index 000000000000..2ee66cb5f11a --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp @@ -0,0 +1,16 @@ +//=--------- EVMMachineFunctionInfo.cpp - EVM Machine Function Info ---------=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements EVM-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#include "EVMMachineFunctionInfo.h" +using namespace llvm; + +EVMFunctionInfo::~EVMFunctionInfo() = default; // anchor. diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h new file mode 100644 index 000000000000..900ac7cfc706 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h @@ -0,0 +1,77 @@ +//=------ EVMMachineFunctionInfo.h - EVM machine function info ----*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares EVM-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MIRYamlMapping.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/MC/MCSymbolWasm.h" + +namespace llvm { + +/// This class is derived from MachineFunctionInfo and contains private +/// EVM-specific information for each MachineFunction. +class EVMFunctionInfo 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: + /// - single use (per path) + /// - single def (per path) + /// - defined and used in LIFO order with other stack registers + BitVector VRegStackified; + + /// Number of parameters. Their type doesn't matter as it always is i256. + unsigned NumberOfParameters = 0; + +public: + explicit EVMFunctionInfo(MachineFunction &MF) {} + EVMFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {} + EVMFunctionInfo(const EVMFunctionInfo &) = delete; + EVMFunctionInfo(EVMFunctionInfo &&) = delete; + EVMFunctionInfo &operator=(const EVMFunctionInfo &) = delete; + EVMFunctionInfo &operator=(EVMFunctionInfo &&) = delete; + ~EVMFunctionInfo() override; + + void stackifyVReg(MachineRegisterInfo &MRI, unsigned VReg) { + assert(MRI.getUniqueVRegDef(VReg)); + auto I = Register::virtReg2Index(VReg); + if (I >= VRegStackified.size()) + VRegStackified.resize(I + 1); + VRegStackified.set(I); + } + + void unstackifyVReg(unsigned VReg) { + auto I = Register::virtReg2Index(VReg); + if (I < VRegStackified.size()) + VRegStackified.reset(I); + } + + bool isVRegStackified(unsigned VReg) const { + auto I = Register::virtReg2Index(VReg); + if (I >= VRegStackified.size()) + return false; + return VRegStackified.test(I); + } + + void addParam() { + ++NumberOfParameters; + } + + unsigned getNumParams() const { + return NumberOfParameters; + } +}; +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMMACHINEFUNCTIONINFO_H diff --git a/llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp b/llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp new file mode 100644 index 000000000000..90e62461aaea --- /dev/null +++ b/llvm/lib/Target/EVM/EVMOptimizeLiveIntervals.cpp @@ -0,0 +1,111 @@ +//===--- EVMOptimizeLiveIntervals.cpp - LiveInterval processing -*- 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 optimize LiveIntervals for use in a post-RA context. +// +// LiveIntervals normally runs before register allocation when the code is +// only recently lowered out of SSA form, so it's uncommon for registers to +// have multiple defs, and when they do, the defs are usually closely related. +// Later, after coalescing, tail duplication, and other optimizations, it's +// more common to see registers with multiple unrelated defs. This pass +// updates LiveIntervals to distribute the value numbers across separate +// LiveIntervals. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-optimize-live-intervals" + +namespace { +class EVMOptimizeLiveIntervals final : public MachineFunctionPass { + StringRef getPassName() const override { + return "EVM Optimize Live Intervals"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreservedID(LiveVariablesID); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::TracksLiveness); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + EVMOptimizeLiveIntervals() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char EVMOptimizeLiveIntervals::ID = 0; +INITIALIZE_PASS(EVMOptimizeLiveIntervals, DEBUG_TYPE, + "Optimize LiveIntervals for EVM", false, false) + +FunctionPass *llvm::createEVMOptimizeLiveIntervals() { + return new EVMOptimizeLiveIntervals(); +} + +bool EVMOptimizeLiveIntervals::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG(dbgs() << "********** Optimize LiveIntervals **********\n" + "********** Function: " + << MF.getName() << '\n'); + + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &LIS = getAnalysis().getLIS(); + + // We don't preserve SSA form. + MRI.leaveSSA(); + + assert(MRI.tracksLiveness() && "OptimizeLiveIntervals expects liveness"); + + // Split multiple-VN LiveIntervals into multiple LiveIntervals. + SmallVector SplitLIs; + for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) { + Register Reg = Register::index2VirtReg(I); + + if (MRI.reg_nodbg_empty(Reg)) + continue; + + LIS.splitSeparateComponents(LIS.getInterval(Reg), SplitLIs); + SplitLIs.clear(); + } + + // In FixIrreducibleControlFlow, we conservatively inserted IMPLICIT_DEF + // instructions to satisfy LiveIntervals' requirement that all uses be + // dominated by defs. Now that LiveIntervals has computed which of these + // defs are actually needed and which are dead, remove the dead ones. + for (MachineInstr &MI : llvm::make_early_inc_range(MF.front())) { + if (MI.isImplicitDef() && MI.getOperand(0).isDead()) { + LiveInterval &LI = LIS.getInterval(MI.getOperand(0).getReg()); + LIS.removeVRegDefAt(LI, LIS.getInstructionIndex(MI).getRegSlot()); + LIS.RemoveMachineInstrFromMaps(MI); + MI.eraseFromParent(); + } + } + + return true; +} diff --git a/llvm/lib/Target/EVM/EVMRegColoring.cpp b/llvm/lib/Target/EVM/EVMRegColoring.cpp new file mode 100644 index 000000000000..752bb18b609b --- /dev/null +++ b/llvm/lib/Target/EVM/EVMRegColoring.cpp @@ -0,0 +1,168 @@ +//===---------- EVMRegColoring.cpp - Register coloring -----*- 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 implements a virtual register coloring pass. +// +// EVM doesn't have a fixed number of registers, but it is still +// desirable to minimize the total number of registers used in each function. +// +// This code is modeled after lib/CodeGen/StackSlotColoring.cpp. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "evm-reg-coloring" + +namespace { +class EVMRegColoring final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + EVMRegColoring() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "EVM Register Coloring"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: +}; +} // end anonymous namespace + +char EVMRegColoring::ID = 0; +INITIALIZE_PASS(EVMRegColoring, DEBUG_TYPE, "Minimize number of registers used", + false, false) + +FunctionPass *llvm::createEVMRegColoring() { return new EVMRegColoring(); } + +// Compute the total spill weight for VReg. +static float computeWeight(const MachineRegisterInfo *MRI, + const MachineBlockFrequencyInfo *MBFI, + unsigned VReg) { + float Weight = 0.0F; + for (MachineOperand &MO : MRI->reg_nodbg_operands(VReg)) + Weight += LiveIntervals::getSpillWeight(MO.isDef(), MO.isUse(), MBFI, + *MO.getParent()); + return Weight; +} + +bool EVMRegColoring::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Register Coloring **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + if (MF.exposesReturnsTwice()) + return false; + + MachineRegisterInfo *MRI = &MF.getRegInfo(); + LiveIntervals *Liveness = &getAnalysis().getLIS(); + const MachineBlockFrequencyInfo *MBFI = + &getAnalysis().getMBFI(); + EVMFunctionInfo &MFI = *MF.getInfo(); + + // Gather all register intervals into a list and sort them. + unsigned NumVRegs = MRI->getNumVirtRegs(); + SmallVector SortedIntervals; + SortedIntervals.reserve(NumVRegs); + + LLVM_DEBUG(dbgs() << "Interesting register intervals:\n"); + for (unsigned I = 0; I < NumVRegs; ++I) { + Register VReg = Register::index2VirtReg(I); + if (MFI.isVRegStackified(VReg)) + continue; + // Skip unused registers, which can use $drop. + if (MRI->use_empty(VReg)) + continue; + + LiveInterval *LI = &Liveness->getInterval(VReg); + assert(LI->weight() == 0.0F); + LI->setWeight(computeWeight(MRI, MBFI, VReg)); + LLVM_DEBUG(LI->dump()); + SortedIntervals.push_back(LI); + } + LLVM_DEBUG(dbgs() << '\n'); + + // Sort them to put arguments first (since we don't want to rename live-in + // registers), by weight next, and then by position. + // TODO: Investigate more intelligent sorting heuristics. For starters, we + // should try to coalesce adjacent live intervals before non-adjacent ones. + llvm::sort(SortedIntervals, [MRI](LiveInterval *LHS, LiveInterval *RHS) { + if (MRI->isLiveIn(LHS->reg()) != MRI->isLiveIn(RHS->reg())) + return MRI->isLiveIn(LHS->reg()); + if (LHS->weight() != RHS->weight()) + return LHS->weight() > RHS->weight(); + if (LHS->empty() || RHS->empty()) + return !LHS->empty() && RHS->empty(); + return *LHS < *RHS; + }); + + LLVM_DEBUG(dbgs() << "Coloring register intervals:\n"); + SmallVector SlotMapping(SortedIntervals.size(), -1U); + SmallVector, 16> Assignments( + SortedIntervals.size()); + BitVector UsedColors(SortedIntervals.size()); + bool Changed = false; + for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { + LiveInterval *LI = SortedIntervals[I]; + Register Old = LI->reg(); + size_t Color = I; + const TargetRegisterClass *RC = MRI->getRegClass(Old); + + // Check if it's possible to reuse any of the used colors. + if (!MRI->isLiveIn(Old)) + for (unsigned C : UsedColors.set_bits()) { + if (MRI->getRegClass(SortedIntervals[C]->reg()) != RC) + continue; + for (LiveInterval *OtherLI : Assignments[C]) + if ((!OtherLI->empty() && OtherLI->overlaps(*LI)) || + Liveness->intervalIsInOneMBB(*OtherLI) != + Liveness->intervalIsInOneMBB(*LI)) + goto continue_outer; + Color = C; + break; + continue_outer:; + } + + Register New = SortedIntervals[Color]->reg(); + SlotMapping[I] = New; + Changed |= Old != New; + UsedColors.set(Color); + Assignments[Color].push_back(LI); + + LLVM_DEBUG(dbgs() << "Assigning vreg" << Register::virtReg2Index(LI->reg()) + << " to vreg" << Register::virtReg2Index(New) << "\n"); + } + if (!Changed) + return false; + + // Rewrite register operands. + for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { + Register Old = SortedIntervals[I]->reg(); + unsigned New = SlotMapping[I]; + if (Old != New) + MRI->replaceRegWith(Old, New); + } + return true; +} diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.cpp b/llvm/lib/Target/EVM/EVMRegisterInfo.cpp index 80890a08a622..e8a9314e9f01 100644 --- a/llvm/lib/Target/EVM/EVMRegisterInfo.cpp +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.cpp @@ -43,14 +43,11 @@ bool EVMRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, RegScavenger *RS) const { assert(SPAdj == 0); MachineInstr &MI = *II; - MachineBasicBlock &MBB = *MI.getParent(); MachineFunction &MF = *MBB.getParent(); - MachineRegisterInfo &MRI = MF.getRegInfo(); const int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); const MachineFrameInfo &MFI = MF.getFrameInfo(); const int64_t FrameOffset = MFI.getObjectOffset(FrameIndex); - const auto *TII = MF.getSubtarget().getInstrInfo(); assert(FrameOffset >= 0 && "FrameOffset < 0"); assert(FrameOffset < static_cast(MFI.getStackSize()) && @@ -59,19 +56,18 @@ bool EVMRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, "We assume that variable-sized objects have already been lowered, " "and don't use FrameIndex operands."); - const Register FrameRegister = getFrameRegister(MF); - Register FIRegOperand = FrameRegister; if (FrameOffset > 0) { - FIRegOperand = MRI.createVirtualRegister(&EVM::GPRRegClass); - const Register OffsetReg = MRI.createVirtualRegister(&EVM::GPRRegClass); - BuildMI(MBB, MI, II->getDebugLoc(), TII->get(EVM::CONST_I256), OffsetReg) - .addImm(FrameOffset); - BuildMI(MBB, MI, II->getDebugLoc(), TII->get(EVM::ADD), FIRegOperand) - .addReg(FrameRegister) - .addReg(OffsetReg); + MachineOperand &OffsetMO = MI.getOperand(FIOperandNum + 1); + const ConstantInt *ConstVal = OffsetMO.getCImm(); + APInt Offset = ConstVal->getValue(); + Offset += FrameOffset; + assert((Offset.getZExtValue() % 32) == 0); + OffsetMO.setCImm(ConstantInt::get(ConstVal->getContext(), Offset)); } - MI.getOperand(FIOperandNum).ChangeToRegister(FIRegOperand, /*isDef=*/false); + MI.getOperand(FIOperandNum) + .ChangeToRegister(getFrameRegister(MF), + /*isDef=*/false); return true; } diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.h b/llvm/lib/Target/EVM/EVMRegisterInfo.h index 5d5bf15ac375..4c895ca567ef 100644 --- a/llvm/lib/Target/EVM/EVMRegisterInfo.h +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.h @@ -37,6 +37,9 @@ class EVMRegisterInfo final : public EVMGenRegisterInfo { const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, unsigned Kind = 0) const override; + + // This does not apply to wasm. + const uint32_t *getNoPreservedMask() const override { return nullptr; } }; } // end namespace llvm diff --git a/llvm/lib/Target/EVM/EVMRegisterInfo.td b/llvm/lib/Target/EVM/EVMRegisterInfo.td index 001321a4ce6e..af8f6bd704d9 100644 --- a/llvm/lib/Target/EVM/EVMRegisterInfo.td +++ b/llvm/lib/Target/EVM/EVMRegisterInfo.td @@ -27,6 +27,10 @@ def SP : EVMReg<"%SP">; // we otherwise don't need a physical register in this class. def GPR256 : EVMReg<"%reg">; +// The value stack "register". This is an opaque entity which serves to order +// uses and defs that must remain in LIFO order. +def VALUE_STACK : EVMReg<"STACK">; + // The incoming arguments "register". This is an opaque entity which serves to // order the ARGUMENT instructions that are emulating live-in registers and // must not be scheduled below other instructions. diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp new file mode 100644 index 000000000000..32584fd4caee --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -0,0 +1,795 @@ +//===-- EVMSingleUseExpression.cpp - Register Stackification --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass reorders instructions to put register uses and defs in an order +// such that they form single-use expression trees. Registers fitting this form +// are then marked as "stackified", meaning references to them are replaced by +// "push" and "pop" from the stack. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" // for EVM::ARGUMENT_* +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfoImpls.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/GlobalAlias.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include +using namespace llvm; + +#define DEBUG_TYPE "evm-single-use-expressions" + +namespace { +class EVMSingleUseExpression final : public MachineFunctionPass { + StringRef getPassName() const override { + return "EVM Single use expressions"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreservedID(LiveVariablesID); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + EVMSingleUseExpression() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char EVMSingleUseExpression::ID = 0; +INITIALIZE_PASS(EVMSingleUseExpression, DEBUG_TYPE, + "Reorder instructions to use the EVM value stack", false, false) + +FunctionPass *llvm::createEVMSingleUseExpression() { + return new EVMSingleUseExpression(); +} + +// Decorate the given instruction with implicit operands that enforce the +// expression stack ordering constraints for an instruction which is on +// the expression stack. +static void imposeStackOrdering(MachineInstr *MI) { + // Write the opaque VALUE_STACK register. + if (!MI->definesRegister(EVM::VALUE_STACK, /*TRI*/nullptr)) + MI->addOperand(MachineOperand::CreateReg(EVM::VALUE_STACK, + /*isDef=*/true, + /*isImp=*/true)); + + // Also read the opaque VALUE_STACK register. + if (!MI->readsRegister(EVM::VALUE_STACK, /*TRI*/nullptr)) + MI->addOperand(MachineOperand::CreateReg(EVM::VALUE_STACK, + /*isDef=*/false, + /*isImp=*/true)); +} + +// Convert an IMPLICIT_DEF instruction into an instruction which defines +// a constant zero value. +static void convertImplicitDefToConstZero(MachineInstr *MI, + MachineRegisterInfo &MRI, + const TargetInstrInfo *TII, + MachineFunction &MF, + LiveIntervals &LIS) { + assert(MI->getOpcode() == TargetOpcode::IMPLICIT_DEF); + + assert(MRI.getRegClass(MI->getOperand(0).getReg()) == &EVM::GPRRegClass); + MI->setDesc(TII->get(EVM::CONST_I256)); + MI->addOperand(MachineOperand::CreateImm(0)); +} + +// Determine whether a call to the callee referenced by +// MI->getOperand(CalleeOpNo) reads memory, writes memory, and/or has side +// effects. +static void queryCallee(const MachineInstr &MI, bool &Read, bool &Write, + bool &Effects, bool &StackPointer) { + // All calls can use the stack pointer. + StackPointer = true; + + const MachineOperand &MO = MI.getOperand(MI.getNumExplicitDefs()); + if (MO.isGlobal()) { + const Constant *GV = MO.getGlobal(); + if (const auto *GA = dyn_cast(GV)) + if (!GA->isInterposable()) + GV = GA->getAliasee(); + + if (const auto *F = dyn_cast(GV)) { + if (!F->doesNotThrow()) + Effects = true; + if (F->doesNotAccessMemory()) + return; + if (F->onlyReadsMemory()) { + Read = true; + return; + } + } + } + + // Assume the worst. + Write = true; + Read = true; + Effects = true; +} + +// Determine whether MI reads memory, writes memory, has side effects, +// and/or uses the stack pointer value. +static void query(const MachineInstr &MI, bool &Read, bool &Write, + bool &Effects, bool &StackPointer) { + assert(!MI.isTerminator()); + + if (MI.isDebugInstr() || MI.isPosition()) + return; + + // Check for loads. + if (MI.mayLoad() && !MI.isDereferenceableInvariantLoad()) + Read = true; + + // Check for stores. + if (MI.mayStore()) { + Write = true; + } else if (MI.hasOrderedMemoryRef()) { + if (!MI.isCall()) { + Write = true; + Effects = true; + } + } + + // Check for side effects. + if (MI.hasUnmodeledSideEffects()) + Effects = true; + + // Analyze calls. + if (MI.isCall()) { + queryCallee(MI, Read, Write, Effects, StackPointer); + } +} + +// Identify the definition for this register at this point. This is a +// generalization of MachineRegisterInfo::getUniqueVRegDef that uses +// LiveIntervals to handle complex cases. +static MachineInstr *getVRegDef(unsigned Reg, const MachineInstr *Insert, + const MachineRegisterInfo &MRI, + const LiveIntervals &LIS) { + // Most registers are in SSA form here so we try a quick MRI query first. + if (MachineInstr *Def = MRI.getUniqueVRegDef(Reg)) + return Def; + + // MRI doesn't know what the Def is. Try asking LIS. + if (const VNInfo *ValNo = LIS.getInterval(Reg).getVNInfoBefore( + LIS.getInstructionIndex(*Insert))) + return LIS.getInstructionFromIndex(ValNo->def); + + return nullptr; +} + +// Test whether Reg, as defined at Def, has exactly one use. This is a +// generalization of MachineRegisterInfo::hasOneUse that uses LiveIntervals +// to handle complex cases. +static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI, + MachineDominatorTree &MDT, LiveIntervals &LIS) { + // Most registers are in SSA form here so we try a quick MRI query first. + if (MRI.hasOneUse(Reg)) + return true; + + bool HasOne = false; + const LiveInterval &LI = LIS.getInterval(Reg); + const VNInfo *DefVNI = + LI.getVNInfoAt(LIS.getInstructionIndex(*Def).getRegSlot()); + assert(DefVNI); + for (auto &I : MRI.use_nodbg_operands(Reg)) { + const auto &Result = LI.Query(LIS.getInstructionIndex(*I.getParent())); + if (Result.valueIn() == DefVNI) { + if (!Result.isKill()) + return false; + if (HasOne) + return false; + HasOne = true; + } + } + return HasOne; +} + +// Test whether Def is safe and profitable to rematerialize. +static bool shouldRematerialize(const MachineInstr &Def, + const MachineRegisterInfo &MRI, + LiveIntervals &LIS, + const EVMInstrInfo *TII) { + // FIXME: remateralization of the CALLDATALOAD and ADD instructions + // is just an ad-hoc solution to more aggressively form single use expressions + // to eventually decrease runtime stack height, but this can significantly + // increase a code size. + unsigned Opcode = Def.getOpcode(); + if (Opcode == EVM::CALLDATALOAD) { + MachineInstr *DefI = getVRegDef(Def.getOperand(1).getReg(), &Def, MRI, LIS); + if (!DefI) + return false; + + return DefI->getOpcode() == EVM::CONST_I256 ? true : false; + } + + if (Opcode == EVM::ADD) { + MachineInstr *DefI1 = + getVRegDef(Def.getOperand(1).getReg(), &Def, MRI, LIS); + MachineInstr *DefI2 = + getVRegDef(Def.getOperand(2).getReg(), &Def, MRI, LIS); + if (!DefI1 || !DefI2) + return false; + + MachineInstr *DefBase = nullptr; + if (DefI1->getOpcode() == EVM::CONST_I256) + DefBase = DefI2; + else if (DefI2->getOpcode() == EVM::CONST_I256) + DefBase = DefI1; + else + return false; + + const Register BaseReg = DefBase->getOperand(0).getReg(); + if (DefBase != MRI.getUniqueVRegDef(BaseReg)) + return false; + + const Register DefReg = Def.getOperand(0).getReg(); + if (&Def != MRI.getUniqueVRegDef(DefReg)) + return false; + + LiveInterval &DefLI = LIS.getInterval(DefReg); + LiveInterval &BaseLI = LIS.getInterval(BaseReg); + + LLVM_DEBUG(dbgs() << "Can rematerialize ADD(base, const): " << Def + << "DefLI: " << DefLI << ", BaseLI: " << BaseLI << '\n'); + + return BaseLI.covers(DefLI); + } + return Def.isAsCheapAsAMove() && TII->isTriviallyReMaterializable(Def); +} + +// Test whether it's safe to move Def to just before Insert. +// TODO: Compute memory dependencies in a way that doesn't require always +// walking the block. +// TODO: Compute memory dependencies in a way that uses AliasAnalysis to be +// more precise. +static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, + const MachineInstr *Insert, const EVMFunctionInfo &MFI, + const MachineRegisterInfo &MRI) { + const MachineInstr *DefI = Def->getParent(); + const MachineInstr *UseI = Use->getParent(); + assert(DefI->getParent() == Insert->getParent()); + assert(UseI->getParent() == Insert->getParent()); + + // The first def of a multivalue instruction can be stackified by moving, + // since the later defs can always be placed into locals if necessary. Later + // defs can only be stackified if all previous defs are already stackified + // since ExplicitLocals will not know how to place a def in a local if a + // subsequent def is stackified. But only one def can be stackified by moving + // the instruction, so it must be the first one. + // + // TODO: This could be loosened to be the first *live* def, but care would + // have to be taken to ensure the drops of the initial dead defs can be + // placed. This would require checking that no previous defs are used in the + // same instruction as subsequent defs. + if (Def != DefI->defs().begin()) + return false; + + // If any subsequent def is used prior to the current value by the same + // instruction in which the current value is used, we cannot + // stackify. Stackifying in this case would require that def moving below the + // current def in the stack, which cannot be achieved, even with locals. + for (const auto &SubsequentDef : drop_begin(DefI->defs())) { + for (const auto &PriorUse : UseI->uses()) { + if (&PriorUse == Use) + break; + if (PriorUse.isReg() && SubsequentDef.getReg() == PriorUse.getReg()) + return false; + } + } + + // If moving is a semantic nop, it is always allowed + const MachineBasicBlock *MBB = DefI->getParent(); + auto NextI = std::next(MachineBasicBlock::const_iterator(DefI)); + for (auto E = MBB->end(); NextI != E && NextI->isDebugInstr(); ++NextI) + ; + if (NextI == Insert) + return true; + + // Check for register dependencies. + SmallVector MutableRegisters; + for (const MachineOperand &MO : DefI->operands()) { + if (!MO.isReg() || MO.isUndef()) + continue; + Register Reg = MO.getReg(); + + // If the register is dead here and at Insert, ignore it. + if (MO.isDead() && Insert->definesRegister(Reg, /*TRI*/nullptr) && + !Insert->readsRegister(Reg, /*TRI*/nullptr)) + continue; + + if (Register::isPhysicalRegister(Reg)) { + // Ignore ARGUMENTS; it's just used to keep the ARGUMENT_* instructions + // from moving down, and we've already checked for that. + if (Reg == EVM::ARGUMENTS) + continue; + // If the physical register is never modified, ignore it. + if (!MRI.isPhysRegModified(Reg)) + continue; + // Otherwise, it's a physical register with unknown liveness. + return false; + } + + // If one of the operands isn't in SSA form, it has different values at + // different times, and we need to make sure we don't move our use across + // a different def. + if (!MO.isDef() && !MRI.hasOneDef(Reg)) + MutableRegisters.push_back(Reg); + } + + bool Read = false, Write = false, Effects = false, StackPointer = false; + query(*DefI, Read, Write, Effects, StackPointer); + + // If the instruction does not access memory and has no side effects, it has + // no additional dependencies. + bool HasMutableRegisters = !MutableRegisters.empty(); + if (!Read && !Write && !Effects && !StackPointer && !HasMutableRegisters) + return true; + + // Scan through the intervening instructions between DefI and Insert. + MachineBasicBlock::const_iterator D(DefI), I(Insert); + for (--I; I != D; --I) { + bool InterveningRead = false; + bool InterveningWrite = false; + bool InterveningEffects = false; + bool InterveningStackPointer = false; + query(*I, InterveningRead, InterveningWrite, InterveningEffects, + InterveningStackPointer); + if (Effects && InterveningEffects) + return false; + if (Read && InterveningWrite) + return false; + if (Write && (InterveningRead || InterveningWrite)) + return false; + if (StackPointer && InterveningStackPointer) + return false; + + for (unsigned Reg : MutableRegisters) + for (const MachineOperand &MO : I->operands()) + if (MO.isReg() && MO.isDef() && MO.getReg() == Reg) + return false; + } + + return true; +} + +// Shrink LI to its uses, cleaning up LI. +static void shrinkToUses(LiveInterval &LI, LiveIntervals &LIS) { + if (LIS.shrinkToUses(&LI)) { + SmallVector SplitLIs; + LIS.splitSeparateComponents(LI, SplitLIs); + } +} + +/// A single-use def in the same block with no intervening memory or register +/// dependencies; move the def down and nest it with the current instruction. +static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, + MachineInstr *Def, MachineBasicBlock &MBB, + MachineInstr *Insert, LiveIntervals &LIS, + EVMFunctionInfo &MFI, + MachineRegisterInfo &MRI) { + LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump()); + + MBB.splice(Insert, &MBB, Def); + LIS.handleMove(*Def); + + if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) { + // No one else is using this register for anything so we can just stackify + // it in place. + MFI.stackifyVReg(MRI, Reg); + } else { + // The register may have unrelated uses or defs; create a new register for + // just our one def and use so that we can stackify it. + Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); + Def->getOperand(0).setReg(NewReg); + Op.setReg(NewReg); + + // Tell LiveIntervals about the new register. + LIS.createAndComputeVirtRegInterval(NewReg); + + // Tell LiveIntervals about the changes to the old register. + LiveInterval &LI = LIS.getInterval(Reg); + LI.removeSegment(LIS.getInstructionIndex(*Def).getRegSlot(), + LIS.getInstructionIndex(*Op.getParent()).getRegSlot(), + /*RemoveDeadValNo=*/true); + + MFI.stackifyVReg(MRI, NewReg); + LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); + } + + imposeStackOrdering(Def); + return Def; +} + +/// A trivially cloneable instruction; clone it and nest the new copy with the +/// current instruction. +static MachineInstr *rematerializeCheapDef( + unsigned Reg, MachineOperand &Op, MachineInstr &Def, MachineBasicBlock &MBB, + MachineBasicBlock::instr_iterator Insert, LiveIntervals &LIS, + EVMFunctionInfo &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()); + + Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); + TII->reMaterialize(MBB, Insert, NewReg, 0, Def, *TRI); + Op.setReg(NewReg); + MachineInstr *Clone = &*std::prev(Insert); + LIS.InsertMachineInstrInMaps(*Clone); + LIS.createAndComputeVirtRegInterval(NewReg); + MFI.stackifyVReg(MRI, NewReg); + imposeStackOrdering(Clone); + + LLVM_DEBUG(dbgs() << " - Cloned to "; Clone->dump()); + + // Shrink the interval. + bool IsDead = MRI.use_empty(Reg); + if (!IsDead) { + LiveInterval &LI = LIS.getInterval(Reg); + shrinkToUses(LI, LIS); + IsDead = !LI.liveAt(LIS.getInstructionIndex(Def).getDeadSlot()); + } + + // If that was the last use of the original, delete the original. + // Move or clone corresponding DBG_VALUEs to the 'Insert' location. + if (IsDead) { + LLVM_DEBUG(dbgs() << " - Deleting original\n"); + SlotIndex Idx = LIS.getInstructionIndex(Def).getRegSlot(); + LIS.removePhysRegDefAt(MCRegister::from(EVM::ARGUMENTS), Idx); + LIS.removeInterval(Reg); + LIS.RemoveMachineInstrFromMaps(Def); + Def.eraseFromParent(); + } + + return Clone; +} + +namespace { +/// A stack for walking the tree of instructions being built, visiting the +/// MachineOperands in DFS order. +class TreeWalkerState { + using mop_iterator = MachineInstr::mop_iterator; + using RangeTy = iterator_range; + SmallVector Worklist; + +public: + explicit TreeWalkerState(MachineInstr *Insert) { + const iterator_range &Range = Insert->explicit_uses(); + if (!Range.empty()) + Worklist.push_back(Range); + } + + bool done() const { return Worklist.empty(); } + + MachineOperand &pop() { + RangeTy &Range = Worklist.back(); + MachineOperand &Op = *Range.begin(); + Range = drop_begin(Range); + if (Range.empty()) + Worklist.pop_back(); + assert((Worklist.empty() || !Worklist.back().empty()) && + "Empty ranges shouldn't remain in the worklist"); + return Op; + } + + /// Push Instr's operands onto the stack to be visited. + void pushOperands(MachineInstr *Instr) { + const iterator_range &Range(Instr->explicit_uses()); + if (!Range.empty()) + Worklist.push_back(Range); + } + + /// Some of Instr's operands are on the top of the stack; remove them and + /// re-insert them starting from the beginning (because we've commuted them). + void resetTopOperands(MachineInstr *Instr) { + assert(hasRemainingOperands(Instr) && + "Reseting operands should only be done when the instruction has " + "an operand still on the stack"); + Worklist.back() = Instr->explicit_uses(); + } + + /// Test whether Instr has operands remaining to be visited at the top of + /// the stack. + bool hasRemainingOperands(const MachineInstr *Instr) const { + if (Worklist.empty()) + return false; + const RangeTy &Range = Worklist.back(); + return !Range.empty() && Range.begin()->getParent() == Instr; + } + + /// Test whether the given register is present on the stack, indicating an + /// operand in the tree that we haven't visited yet. Moving a definition of + /// Reg to a point in the tree after that would change its value. + /// + /// This is needed as a consequence of using implicit local.gets for + /// uses and implicit local.sets for defs. + bool isOnStack(unsigned Reg) const { + for (const RangeTy &Range : Worklist) + for (const MachineOperand &MO : Range) + if (MO.isReg() && MO.getReg() == Reg) + return true; + return false; + } +}; + +/// State to keep track of whether commuting is in flight or whether it's been +/// tried for the current instruction and didn't work. +class CommutingState { + /// There are effectively three states: the initial state where we haven't + /// started commuting anything and we don't know anything yet, the tentative + /// state where we've commuted the operands of the current instruction and are + /// revisiting it, and the declined state where we've reverted the operands + /// back to their original order and will no longer commute it further. + bool TentativelyCommuting = false; + bool Declined = false; + + /// During the tentative state, these hold the operand indices of the commuted + /// operands. + unsigned Operand0 = 0, Operand1 = 0; + +public: + /// Stackification for an operand was not successful due to ordering + /// constraints. If possible, and if we haven't already tried it and declined + /// it, commute Insert's operands and prepare to revisit it. + void maybeCommute(MachineInstr *Insert, TreeWalkerState &TreeWalker, + const EVMInstrInfo *TII) { + if (TentativelyCommuting) { + assert(!Declined && + "Don't decline commuting until you've finished trying it"); + // Commuting didn't help. Revert it. + TII->commuteInstruction(*Insert, /*NewMI=*/false, Operand0, Operand1); + TentativelyCommuting = false; + Declined = true; + } else if (!Declined && TreeWalker.hasRemainingOperands(Insert)) { + Operand0 = TargetInstrInfo::CommuteAnyOperandIndex; + Operand1 = TargetInstrInfo::CommuteAnyOperandIndex; + if (TII->findCommutedOpIndices(*Insert, Operand0, Operand1)) { + // Tentatively commute the operands and try again. + TII->commuteInstruction(*Insert, /*NewMI=*/false, Operand0, Operand1); + TreeWalker.resetTopOperands(Insert); + TentativelyCommuting = true; + Declined = false; + } + } + } + + /// Stackification for some operand was successful. Reset to the default + /// state. + void reset() { + TentativelyCommuting = false; + Declined = false; + } +}; +} // end anonymous namespace + +bool EVMSingleUseExpression::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG(dbgs() << "********** Single-use expressions **********\n" + "********** Function: " + << MF.getName() << '\n'); + + bool Changed = false; + MachineRegisterInfo &MRI = MF.getRegInfo(); + EVMFunctionInfo &MFI = *MF.getInfo(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + const auto *TRI = MF.getSubtarget().getRegisterInfo(); + auto &MDT = getAnalysis().getDomTree(); + auto &LIS = getAnalysis().getLIS(); + + // Walk the instructions from the bottom up. Currently we don't look past + // block boundaries, and the blocks aren't ordered so the block visitation + // order isn't significant, but we may want to change this in the future. + for (MachineBasicBlock &MBB : MF) { + // Don't use a range-based for loop, because we modify the list as we're + // iterating over it and the end iterator may change. + for (auto MII = MBB.rbegin(); MII != MBB.rend(); ++MII) { + MachineInstr *Insert = &*MII; + // Don't nest anything inside an inline asm, because we don't have + // constraints for $push inputs. + if (Insert->isInlineAsm()) + continue; + + // Ignore debugging intrinsics. + if (Insert->isDebugValue()) + continue; + + // Iterate through the inputs in reverse order, since we'll be pulling + // operands off the stack in LIFO order. + CommutingState Commuting; + TreeWalkerState TreeWalker(Insert); + while (!TreeWalker.done()) { + MachineOperand &Use = TreeWalker.pop(); + + // We're only interested in explicit virtual register operands. + if (!Use.isReg()) + continue; + + Register Reg = Use.getReg(); + assert(Use.isUse() && "explicit_uses() should only iterate over uses"); + assert(!Use.isImplicit() && + "explicit_uses() should only iterate over explicit operands"); + if (Register::isPhysicalRegister(Reg)) + continue; + + // Identify the definition for this register at this point. + MachineInstr *DefI = getVRegDef(Reg, Insert, MRI, LIS); + if (!DefI) + continue; + + // Don't nest an INLINE_ASM def into anything, because we don't have + // constraints for $pop outputs. + if (DefI->isInlineAsm()) + continue; + + MachineOperand *Def = DefI->findRegisterDefOperand(Reg, TRI); + assert(Def != nullptr); + + // Decide which strategy to take. Prefer to move a single-use value + // over cloning it, and prefer cloning over introducing a tee. + // For moving, we require the def to be in the same block as the use; + // this makes things simpler (LiveIntervals' handleMove function only + // supports intra-block moves) and it's MachineSink's job to catch all + // the sinking opportunities anyway. + bool SameBlock = DefI->getParent() == &MBB; + bool CanMove = SameBlock && + isSafeToMove(Def, &Use, Insert, MFI, MRI) && + !TreeWalker.isOnStack(Reg); + if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) { + Insert = + moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); + } else if (shouldRematerialize(*DefI, MRI, LIS, TII)) { + if (DefI->getOpcode() == EVM::CALLDATALOAD) { + MachineInstr *OffsetI = getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, + LIS); + assert(OffsetI); + Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, + Insert->getIterator(), LIS, MFI, + MRI, TII, TRI); + + MachineOperand &OffsetMO = Insert->getOperand(1); + Insert = rematerializeCheapDef(OffsetMO.getReg(), OffsetMO, *OffsetI, MBB, + Insert->getIterator(), LIS, MFI, + MRI, TII, TRI); + } else if (DefI->getOpcode() == EVM::ADD) { + MachineInstr *DefI1 = + getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, LIS); + MachineInstr *DefI2 = + getVRegDef(DefI->getOperand(2).getReg(), DefI, MRI, LIS); + assert(DefI1 && DefI2); + + MachineInstr *OffsetI = + (DefI1->getOpcode() == EVM::CONST_I256) ? DefI1 : DefI2; + Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, + Insert->getIterator(), LIS, MFI, MRI, + TII, TRI); + + MachineOperand &OffsetMO = (DefI1->getOpcode() == EVM::CONST_I256) + ? Insert->getOperand(1) + : Insert->getOperand(2); + Insert = rematerializeCheapDef(OffsetMO.getReg(), OffsetMO, + *OffsetI, MBB, Insert->getIterator(), + LIS, MFI, MRI, TII, TRI); + } else { + Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, + Insert->getIterator(), LIS, MFI, + MRI, TII, TRI); + } + } else { + // We failed to stackify the operand. If the problem was ordering + // constraints, Commuting may be able to help. + if (!CanMove && SameBlock) + Commuting.maybeCommute(Insert, TreeWalker, TII); + // Proceed to the next operand. + continue; + } + + // Stackifying a multivalue def may unlock in-place stackification of + // subsequent defs. TODO: Handle the case where the consecutive uses are + // not all in the same instruction. + auto *SubsequentDef = Insert->defs().begin(); + auto *SubsequentUse = &Use; + while (SubsequentDef != Insert->defs().end() && + SubsequentUse != Use.getParent()->uses().end()) { + if (!SubsequentDef->isReg() || !SubsequentUse->isReg()) + break; + Register DefReg = SubsequentDef->getReg(); + Register UseReg = SubsequentUse->getReg(); + // TODO: This single-use restriction could be relaxed by using tees + if (DefReg != UseReg || !MRI.hasOneUse(DefReg)) + break; + MFI.stackifyVReg(MRI, DefReg); + ++SubsequentDef; + ++SubsequentUse; + } + + // If the instruction we just stackified is an IMPLICIT_DEF, convert it + // to a constant 0 so that the def is explicit, and the push/pop + // correspondence is maintained. + if (Insert->getOpcode() == TargetOpcode::IMPLICIT_DEF) + convertImplicitDefToConstZero(Insert, MRI, TII, MF, LIS); + + // We stackified an operand. Add the defining instruction's operands to + // the worklist stack now to continue to build an ever deeper tree. + Commuting.reset(); + TreeWalker.pushOperands(Insert); + } + + // If we stackified any operands, skip over the tree to start looking for + // the next instruction we can build a tree on. + if (Insert != &*MII) { + imposeStackOrdering(&*MII); + MII = MachineBasicBlock::iterator(Insert).getReverse(); + Changed = true; + } + } + } + + // If we used VALUE_STACK anywhere, add it to the live-in sets everywhere so + // that it never looks like a use-before-def. + if (Changed) { + MF.getRegInfo().addLiveIn(EVM::VALUE_STACK); + for (MachineBasicBlock &MBB : MF) + MBB.addLiveIn(EVM::VALUE_STACK); + } + +#ifndef NDEBUG + // Verify that pushes and pops are performed in LIFO order. + SmallVector Stack; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (MI.isDebugInstr()) + continue; + for (MachineOperand &MO : MI.explicit_uses()) { + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (MFI.isVRegStackified(Reg)) { + LLVM_DEBUG(dbgs() << "Use stackified reg: " << MO << '\n'); + assert(Stack.back() == Reg && + "Register stack pop should be paired with a push"); + Stack.pop_back(); + } + } + for (MachineOperand &MO : reverse(MI.defs())) { + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (MFI.isVRegStackified(Reg)) { + LLVM_DEBUG(dbgs() << "Stackify reg: " << MO << '\n'); + Stack.push_back(MO.getReg()); + } + } + } + // TODO: Generalize this code to support keeping values on the stack across + // basic block boundaries. + assert(Stack.empty() && + "Register stack pushes and pops should be balanced"); + } +#endif + + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp new file mode 100644 index 000000000000..cece363bdcb2 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackify.cpp @@ -0,0 +1,1202 @@ +//===---------- EVMStackify.cpp - Replace phys regs with virt regs --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The pass works right before the AsmPrinter and performs code stackification, +// so it can be executed on the Ethereum virtual stack machine. Physically, +// this means adding stack manipulation instructions (POP, PUSH, DUP or SWAP) +// to the Machine IR and removing stack operands of instructions. Logically, +// this corresponds to allocation of stack slots and mapping them on virtual +// registers. +// +// Current implementation is based on the article M. Shannon, C. Bailey +// "Global Stack Allocation – Register Allocation for Stack Machines", +// but in quite simplified form. +// +// than that required for the stackification itself. It's expected that +// previous passes perform required optimizations: +// - splitting live intervals that contain unrelated register values. +// - a code scheduling to form single-expression-use. +// - coloring of virtual registers. This is a kind of register coalescing. +// +// Main conception borrowed from the article is a logical view of the physical +// stack in which stack consists of 3 main regions. Starting from the top, +// these are: +// - The evaluation region (E-stack) +// - The local region (L-stack) +// - The transfer region (X-stack) +// +// E-stack is used for holding instruction arguments/results right before and +// after its execution. In all other cases it should be empty. +// +// The L-stack is always empty at the beginning and end of any basic block, +// but may contain values between instructions. The values in L-stack are live +// inside one basic block. +// +// The X-stack is used to store values both during basic blocks and on edges in +// the flow graph. The x-stack need only be empty at procedure exit. Unlike the +// article, in current implementation X-stack is statically formed and fixed +// for the entire function. This makes an implementation easier, but leads +// to non-optimal stack usage, which, in turn, breaks the stack limits. +// FIXME: try to implement "Global Stack Allocation" approach. +// +// The algorithm maintains programmatic model of the stack to know, at any +// given point of the code: a) height of physical stack, b) content of both +// the L and X-stacks. +// +// The algorithm runs, in outline, as follows (on a function level): +// +// 1. Determine content of the X-stack. Here we include all the virtual +// registers, whose live intervals cross a BB boundary. Its content is fixed +// during the whole function. See, allocateXStack() +// +// 2. Perform handling of each instruction. See handleInstrUses(). +// As an example let's see how +// +// %vreg7 = ADD %vreg1 kill, %vreg2 kill +// +// is handled. Suppose the stack layout before the instruction is +// +// |vreg2| - depth 1 +// L |vreg3| +// |vreg9| +// |vreg1| - depth 4 +// --------- +// |vreg13| +// X |vreg7| - depth 6 +// |vreg11| +// --------- +// |raddr| - function return address +// |farg1| - first function argument +// |farg2| +// ....... +// |fargN| +// +// In order to execute the instruction its arguments should be placed +// on top of the stack for which we can use (DUP1 - DUP16) instructions. +// In terms of the stack model we load them to E-stack. So, to load +// arguments and execute the instructions: +// +// DUP4 // load %vreg1; No need to load %vreg2, as it's already located +// // in the right place (depth 1) and it's killed at ADD. +// ADD +// +// Stack layout rigth after loading arguments on top of the stack: +// +// +// |vreg1| - depth 1 +// E |vreg2| - depth 2 // It was logically migrated from L -> E stack +// --------- +// L |vreg3| +// |vreg9| +// |vreg1| - depth 5 +// --------- +// |vreg13| +// X |vreg7| - depth 7 +// |vreg11| +// --------- +// |raddr| - function return address +// |farg1| - first function argument +// |farg2| +// ....... +// |fargN| +// +// Stack layout right after instruction execution: +// +// E |vreg7| +// --------- +// L |vreg3| +// |vreg9| +// |vreg1| - depth 4, is dead +// --------- +// |vreg13| +// X |vreg7| - depth 6 +// |vreg11| +// --------- +// |raddr| - function return address +// |farg1| - first function argument +// |farg2| +// ....... +// |fargN| +// +// The resulting register %vreg7 should be placed to its final location in +// X-stack. To do this we can use SWAP + POP pair, so the final "stackified" +// code is: +// +// DUP4 // Duplicated the 4th stack item +// ADD +// SWAP5 // Swap top item with 6th (SWAP opcodes have shifted names) +// POP // Remove top item from the stack +// +// Note: +// +// - resulted code has actually 25% of payload instructions. One the +// goals of stackification is to minimize the stack manipulation +// instructions. +// +// - %vreg1 is dead after the instruction, so it theoretically should be +// removed from the stack. Unfortunately, EVM has no efficient +// instructions fot this, so we need emulate it via a sequence of +// SWAP + DUP + POP instructions which may lead to quite inefficient +// code. As an experiment, this is implemented in handleInstrDefs(). +// A better strategy would be to wait when a dead register is near +// the stack top. +// +// - Several instructions requires special handling (FCALL, RET, so on). +// +// 3. Once all the instructions are handled perform some clean up: +// replace, remove dead instructions. +// +// +// The main drawback of this algorithm is that X-stack is fixed during the +// whole function, which, for example, means we cannot remove dead registers +// from it. On more-or-less complicated programs this may lead to an attempt +// to access too far from stack top (> 16 element), which triggers an assertion +// in the code. +// More sophisticated algorithm is required, as described in the article. +// +// TODO: CPR-1562. Current implementation is a draft stackification version +// which needs to be refactored, cleaned and documented. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/MC/MCContext.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-stackify" + +namespace { + +enum class StackType { X, L, E }; + +struct StackLocation { + StackType Type; + // Depth counted from the top of this stack type + // (not a physical depth). + unsigned Depth; +}; + +using MIIter = MachineBasicBlock::iterator; +using RegisterLocation = std::pair; + +class StackModel { + [[maybe_unused]] static constexpr unsigned StackAccessDepthLimit = 16; + const LiveIntervals &LIS; + const EVMInstrInfo *TII; + MachineFunction *MF; + + // Frame objects area which is the result of + // alloca's lowering. + unsigned NumFrameObjs = 0; + std::deque XStack; + std::deque LStack; + // E-stack is only used to properly calculate physical stack + // height, so in the code below we usually push there '0' + // register. + std::deque EStack; + SmallVector ToErase; + +public: + StackModel(MachineFunction *MF, const LiveIntervals &LIS, + const EVMInstrInfo *TII) + : LIS(LIS), TII(TII), MF(MF) {} + + // Loads instruction arguments to the stack top and stores + // results. + void handleInstruction(MachineInstr *MI); + + // Allocate space for both X stack and allocas. + void preProcess(); + + // Special handling of some instructions. + void postProcess(); + + void enterBB(MachineBasicBlock *BB) { + assert(EStack.empty() && LStack.empty()); + } + + void leaveBB(MachineBasicBlock *BB) { + // Clear L-stack. + assert(EStack.empty()); + peelPhysStack(StackType::L, LStack.size(), BB, BB->end()); + } + + void dumpState() const; + +private: + StackLocation allocateStackModelLoc(const Register &Reg); + + void allocateXStack(); + + void allocateFrameObjects(); + + std::optional getStackLocation(const Register &Reg) const; + + bool isRegisterKill(const Register &Reg, const MachineInstr *MI) const; + + void migrateToEStack(const Register &Reg, const StackLocation &Loc); + + unsigned getPhysRegDepth(const StackLocation &Loc) const; + + unsigned getPhysRegDepth(const Register &Reg) const; + + MachineInstr *loadRegToPhysStackTop(unsigned Depth, MachineBasicBlock *MBB, + MIIter Insert, const DebugLoc &DL); + + MachineInstr *loadRegToPhysStackTop(const Register &Reg, + MachineBasicBlock *MBB, MIIter Insert, + const DebugLoc &DL); + + MachineInstr *loadRegToPhysStackTop(const Register &Reg, MachineInstr *MI) { + return loadRegToPhysStackTop(Reg, MI->getParent(), MI, MI->getDebugLoc()); + } + + MachineInstr *loadRegToPhysStackTop(unsigned Depth, MachineInstr *MI) { + return loadRegToPhysStackTop(Depth, MI->getParent(), MI, MI->getDebugLoc()); + } + + MachineInstr *storeRegToPhysStack(const Register &Reg, MIIter Insert); + + MachineInstr *storeRegToPhysStackAt(unsigned Depth, MachineBasicBlock *MBB, + MIIter Insert, const DebugLoc &DL); + + SmallVector::const_iterator + migrateTopRegsToEStack(const SmallVector &RegLoc, + MachineInstr *MI); + + bool migrateTopRegToEStack(const Register &Reg, MachineInstr *MI); + + StackLocation pushRegToStackModel(StackType Type, const Register &Reg); + + void handleInstrUses(MachineInstr *MI); + + void handleInstrDefs(MachineInstr *MI); + + void handleArgument(MachineInstr *MI); + + void handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, + const Register &Reg); + + void handleCondJump(MachineInstr *MI); + + void handleJump(MachineInstr *MI); + + void handleReturn(MachineInstr *MI); + + void handleCall(MachineInstr *MI); + + void handleStackLoad(MachineInstr *MI); + + void handleStackStore(MachineInstr *MI); + + void clearFrameObjsAtInst(MachineInstr *MI); + + void clearPhysStackAtInst(StackType TargetStackType, MachineInstr *MI, + const Register &Reg); + + void peelPhysStack(StackType Type, unsigned NumItems, MachineBasicBlock *BB, + MIIter Pos); + + static unsigned getDUPOpcode(unsigned Depth); + + static unsigned getSWAPOpcode(unsigned Depth); + + unsigned getStackSize() const { + return NumFrameObjs + XStack.size() + LStack.size() + EStack.size(); + } + + std::deque &getStack(StackType Type) { + return (Type == StackType::X) ? XStack : LStack; + } +}; + +} // end anonymous namespace + +// FIXME: this needs some kind of hashing. +std::optional +StackModel::getStackLocation(const Register &Reg) const { + auto It = std::find(LStack.begin(), LStack.end(), Reg); + if (It != LStack.end()) { + assert(std::count(LStack.begin(), LStack.end(), Reg) == 1); + unsigned Depth = std::distance(LStack.begin(), It); + return {{StackType::L, Depth}}; + } + + It = std::find(XStack.begin(), XStack.end(), Reg); + if (It != XStack.end()) { + assert(std::count(XStack.begin(), XStack.end(), Reg) == 1); + unsigned Depth = std::distance(XStack.begin(), It); + return {{StackType::X, Depth}}; + } + + return std::nullopt; +} + +void StackModel::handleInstruction(MachineInstr *MI) { + // Handle special cases. + switch (MI->getOpcode()) { + default: + break; + case EVM::JUMP: + handleJump(MI); + return; + case EVM::JUMPI: + handleCondJump(MI); + return; + case EVM::ARGUMENT: + handleArgument(MI); + return; + case EVM::RET: + // TODO: handle RETURN, REVERT, STOP + handleReturn(MI); + return; + } + + handleInstrUses(MI); + + assert(EStack.empty()); + + if (MI->getOpcode() == EVM::STACK_LOAD) + handleStackLoad(MI); + + handleInstrDefs(MI); +} + +void StackModel::handleInstrUses(MachineInstr *MI) { + // Handle a general case. Collect use registers. + SmallVector> RegLocs; + for (const auto &MO : MI->explicit_uses()) { + if (!MO.isReg()) + continue; + + const auto Reg = MO.getReg(); + // SP is not used anyhow. + if (Reg == EVM::SP) + continue; + + const auto &Loc = getStackLocation(Reg); + assert(Loc); + RegLocs.push_back({Reg, *Loc}); + LLVM_DEBUG(dbgs() << "Use: " << Reg << ", Depth: " << getPhysRegDepth(Reg) + << '\n'); + } + + // If MI has two args, both are on stack top and at least one of them is + // killed, try to swap them to minimize the number of used DUP instructions. + bool NeedsSwap = false; + if (RegLocs.size() == 2) { + const Register &Use1 = RegLocs[0].first; + const Register &Use2 = RegLocs[1].first; + const unsigned Depth1 = getPhysRegDepth(Use1); + const unsigned Depth2 = getPhysRegDepth(Use2); + if (Depth1 == 1 && Depth2 == 0 && isRegisterKill(Use1, MI) && + isRegisterKill(Use2, MI)) + NeedsSwap = true; + + if (Depth1 == 0 && isRegisterKill(Use1, MI) && + (Depth2 > 1 || !isRegisterKill(Use2, MI))) + NeedsSwap = true; + + if (NeedsSwap) { + std::swap(RegLocs[0], RegLocs[1]); + LLVM_DEBUG(dbgs() << "Swap top stack items\n"); + } + + // If we can swap instruction operands then no need to swap them in the + // physical stack. + if (MI->isCommutable()) + NeedsSwap = false; + } + + SmallVector::const_iterator B = RegLocs.begin(), + StartUse = + migrateTopRegsToEStack( + RegLocs, MI); + for (const auto &Loc : reverse(make_range(B, StartUse))) + loadRegToPhysStackTop(Loc.first, MI); + + if (NeedsSwap) + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::SWAP1)); + + if (MI->getOpcode() == EVM::STACK_STORE) + handleStackStore(MI); + + // Pop from the E-stack the registers consumed by the MI. + EStack.erase(EStack.begin(), EStack.begin() + RegLocs.size()); +} + +void StackModel::handleInstrDefs(MachineInstr *MI) { + unsigned NumXLStackRegs = 0; + MachineBasicBlock *MBB = MI->getParent(); + const DebugLoc &DL = MI->getDebugLoc(); + for (const auto &MO : reverse(MI->defs())) { + assert(MO.isReg()); + + LLVM_DEBUG(dbgs() << "Def(rev): " << MO.getReg() << '\n'); + // Push instruction's result into E-stack + EStack.push_front(MO.getReg()); + if (getStackLocation(MO.getReg())) + ++NumXLStackRegs; + } + + if (EStack.empty()) + return; + + // The MI pushed on the EStack several registers. Some of them need to be + // placed onto existing X/L stack locations. For others we need to allocate + // new slots on X/L stack. + auto RevIt = std::prev(EStack.end()); + MachineInstr *MII = MI; + for (unsigned Iter = 0; Iter < NumXLStackRegs; ++Iter) { + const Register TopReg = EStack.front(); + // If there exists a slot for the register, just move it there. + if (getStackLocation(TopReg)) { + MII = storeRegToPhysStack(TopReg, MII); + continue; + } + // Try to find a register for which there is s slot starting from the bottom + // of the ESatck. + const auto Begin = EStack.begin(); + while (RevIt != Begin && !getStackLocation(*RevIt)) + --RevIt; + + // Swap just found register with the one on the stack top and move it + // to existing location. + unsigned Depth = std::distance(Begin, RevIt); + const unsigned SWAPOpc = getSWAPOpcode(Depth); + MII = BuildMI(*MBB, std::next(MIIter(MII)), DL, TII->get(SWAPOpc)); + std::swap(*Begin, *RevIt); + MII = storeRegToPhysStack(EStack.front(), MII); + } + + // Handle the registers for which we need to allocate X/L stack, performing + // migration E -> L/X stack. + for (const auto Reg : reverse(EStack)) { + assert(!getStackLocation(Reg)); + allocateStackModelLoc(Reg); + EStack.pop_back(); + } + + const unsigned NumDeadRegs = std::count_if( + LStack.begin(), LStack.end(), + [this, MI](const Register &Reg) { return isRegisterKill(Reg, MI); }); + + const auto E = LStack.rend(); + for (unsigned I = 0; I < NumDeadRegs; ++I) { + auto DeadIt = + std::find_if(LStack.rbegin(), E, [this, MI](const Register &Reg) { + return isRegisterKill(Reg, MI); + }); + for (; DeadIt < E;) { + auto LiveIt = + std::find_if(std::next(DeadIt), E, [this, MI](const Register &Reg) { + return !isRegisterKill(Reg, MI); + }); + if (LiveIt == E) + break; + + // Swap live and dead stack locations. + const unsigned LiveDepth = getPhysRegDepth(*LiveIt); + MII = loadRegToPhysStackTop(LiveDepth, MBB, std::next(MIIter(MII)), DL); + const unsigned DeadDepth = getPhysRegDepth(*DeadIt); + MII = storeRegToPhysStackAt(DeadDepth, MBB, std::next(MIIter(MII)), DL); + + LLVM_DEBUG(dbgs() << "Swapping regs: dead: " << *DeadIt << " " + << DeadDepth << ", live: " << *LiveIt << " " + << LiveDepth << '\n'); + + std::swap(*DeadIt, *LiveIt); + DeadIt = LiveIt; + } + } + + LLVM_DEBUG(dbgs() << "Dumping stack before removing dead regs\n"); + dumpState(); + + for (unsigned I = 0; I < NumDeadRegs; ++I) { + assert(isRegisterKill(LStack.front(), MI)); + LStack.pop_front(); + MII = BuildMI(*MBB, std::next(MIIter(MII)), DL, TII->get(EVM::POP)); + } +} + +bool StackModel::isRegisterKill(const Register &Reg, + const MachineInstr *MI) const { + const LiveInterval *LI = &LIS.getInterval(Reg); + const SlotIndex SI = LIS.getInstructionIndex(*MI).getRegSlot(); + bool IsEnd = LI->expiredAt(SI); + LLVM_DEBUG(dbgs() << "LI:" << *LI << ", reg: " << Reg << '\n' + << (IsEnd ? "ends at: " : "doesn't end at: ") << SI << ",\n" + << *MI << '\n'); + return IsEnd; +} + +void StackModel::migrateToEStack(const Register &Reg, + const StackLocation &Loc) { + auto &Stack = getStack(Loc.Type); + assert(Reg == Stack.front()); + Stack.pop_front(); + // Put the register at the bottom of the E-stack. + EStack.push_back(Reg); +} + +unsigned StackModel::getPhysRegDepth(const StackLocation &Loc) const { + unsigned Depth = EStack.size(); + switch (Loc.Type) { + case StackType::X: + Depth += (Loc.Depth + LStack.size()); + break; + case StackType::L: + Depth += Loc.Depth; + break; + default: + llvm_unreachable("Unexpected stack type"); + break; + } + return Depth; +} + +unsigned StackModel::getPhysRegDepth(const Register &Reg) const { + const auto &Loc = getStackLocation(Reg); + assert(Loc); + return getPhysRegDepth(*Loc); +} + +MachineInstr *StackModel::loadRegToPhysStackTop(unsigned Depth, + MachineBasicBlock *MBB, + MIIter Insert, + const DebugLoc &DL) { + EStack.emplace_front(0); + return BuildMI(*MBB, Insert, DL, TII->get(getDUPOpcode(Depth))); +} + +MachineInstr *StackModel::loadRegToPhysStackTop(const Register &Reg, + MachineBasicBlock *MBB, + MIIter Insert, + const DebugLoc &DL) { + if (!migrateTopRegToEStack(Reg, &*Insert)) + return loadRegToPhysStackTop(getPhysRegDepth(Reg), MBB, Insert, DL); + + return &*Insert; +} + +SmallVector::const_iterator +StackModel::migrateTopRegsToEStack(const SmallVector &RegLoc, + MachineInstr *MI) { + assert(EStack.empty()); + + const auto B = RegLoc.begin(), E = RegLoc.end(); + if (RegLoc.empty()) + return E; + + const auto *const StartUse = std::find_if(B, E, [this](const auto &Loc) { + return getPhysRegDepth(Loc.first) == 0; + }); + for (const auto *It = StartUse; It != E; ++It) { + const Register &Reg = It->first; + const unsigned Depth = getPhysRegDepth(Reg); + if (It->second.Type == StackType::X || + Depth != std::distance(StartUse, It) || !isRegisterKill(Reg, MI)) + return E; + } + + for (const auto *It = StartUse; It != E; ++It) + migrateToEStack(It->first, It->second); + + return StartUse; +} + +bool StackModel::migrateTopRegToEStack(const Register &Reg, MachineInstr *MI) { + const auto Uses = MI->explicit_uses(); + const unsigned NumUses = + std::count_if(Uses.begin(), Uses.end(), [Reg](const MachineOperand &MO) { + if (MO.isReg()) + return Reg == MO.getReg(); + return false; + }); + + if (NumUses > 1) + return false; + + const auto &Loc = getStackLocation(Reg); + assert(Loc); + + if (Loc->Type == StackType::X || getPhysRegDepth(Reg) != 0 || + !isRegisterKill(Reg, MI)) + return false; + + migrateToEStack(Reg, *Loc); + return true; +} + +MachineInstr *StackModel::storeRegToPhysStack(const Register &Reg, + MIIter Insert) { + // Check if the reg is already located in either X or L stack. + // If so, just return its location. + const auto LocOpt = getStackLocation(Reg); + + const StackLocation &Loc = LocOpt ? *LocOpt : allocateStackModelLoc(Reg); + unsigned Depth = getPhysRegDepth(Loc); + assert(Depth > 0 && Loc.Type != StackType::E); + + // Perform migration E -> L/X stack. + // If the reg should be put on a newly created L/X stack top location, + // just do nothing. + if (Loc.Depth == 0 && !LocOpt) { + assert(EStack.size() == 1); + EStack.pop_back(); + return &*Insert; + } + return storeRegToPhysStackAt(Depth, Insert->getParent(), std::next(Insert), + Insert->getDebugLoc()); +} + +MachineInstr *StackModel::storeRegToPhysStackAt(unsigned Depth, + MachineBasicBlock *MBB, + MIIter Insert, + const DebugLoc &DL) { + // Use the SWAP + POP instructions pair to store the register + // in the physical stack on the given depth. + const unsigned SWAPOpc = getSWAPOpcode(Depth); + Insert = BuildMI(*MBB, Insert, DL, TII->get(SWAPOpc)); + Insert = BuildMI(*MBB, std::next(Insert), DL, TII->get(EVM::POP)); + EStack.pop_front(); + return &*Insert; +} + +StackLocation StackModel::pushRegToStackModel(StackType Type, + const Register &Reg) { + StackLocation Loc{Type, 0}; + auto &Stack = getStack(Type); + assert(std::find(Stack.begin(), Stack.end(), Reg) == std::end(Stack)); + Stack.push_front(Reg); + return Loc; +} + +StackLocation StackModel::allocateStackModelLoc(const Register &Reg) { + // We need to add the register to either X or L-stack, depending + // on reg's live interval. The register goes to X stack if its live interval + // spawns across a BB boundary, otherwise put it to L-stack. + const auto *LI = &LIS.getInterval(Reg); + if (const auto *BB = LIS.intervalIsInOneMBB(*LI)) + return pushRegToStackModel(StackType::L, Reg); + + return pushRegToStackModel(StackType::X, Reg); +} + +void StackModel::handleArgument(MachineInstr *MI) { + const auto ArgIdx = MI->getOperand(1).getImm(); + // 1 - because of the return address. + unsigned Depth = 1 + static_cast(ArgIdx) + getStackSize(); + loadRegToPhysStackTop(Depth, MI); + storeRegToPhysStack(MI->getOperand(0).getReg(), MI); + ToErase.push_back(MI); +} + +void StackModel::handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, + const Register &Reg) { + // If the condition register is in the L-stack, we need to move it to + // the bottom of the L-stack. After that we should clean clean the L-stack. + // In case of an unconditional jump, the Reg value should be + // EVM::NoRegister. + clearPhysStackAtInst(StackType::L, MI, Reg); + + // Insert "PUSH8_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)) + .addMBB(MBB); + + // Add JUMPDEST at the beginning of the target MBB. + if (MBB->empty() || MBB->begin()->getOpcode() != EVM::JUMPDEST) + BuildMI(*MBB, MBB->begin(), DebugLoc(), TII->get(EVM::JUMPDEST)); +} + +void StackModel::handleCondJump(MachineInstr *MI) { + MachineBasicBlock *TargetMBB = MI->getOperand(0).getMBB(); + const Register &CondReg = MI->getOperand(1).getReg(); + loadRegToPhysStackTop(CondReg, MI); + handleLStackAtJump(TargetMBB, MI, CondReg); +} + +void StackModel::handleJump(MachineInstr *MI) { + MachineBasicBlock *TargetMBB = MI->getOperand(0).getMBB(); + handleLStackAtJump(TargetMBB, MI, EVM::NoRegister); +} + +void StackModel::handleReturn(MachineInstr *MI) { + ToErase.push_back(MI); + BuildMI(*MI->getParent(), std::next(MIIter(MI)), DebugLoc(), + TII->get(EVM::JUMP)); + + // Collect the use registers of the RET instruction. + SmallVector ReturnRegs; + for (const auto &MO : MI->explicit_uses()) { + assert(MO.isReg()); + ReturnRegs.push_back(MO.getReg()); + } + + auto *MFI = MF->getInfo(); + if (MFI->getNumParams() >= ReturnRegs.size()) { + // Move the return registers to the stack location where + // arguments were resided. + unsigned RegCount = 0; + for (const auto &Reg : reverse(ReturnRegs)) { + loadRegToPhysStackTop(Reg, MI); + unsigned Depth = getStackSize() + MFI->getNumParams() - RegCount; + storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); + ++RegCount; + } + + if (MFI->getNumParams() != ReturnRegs.size()) { + // Now move the return address to the final location. + loadRegToPhysStackTop(getStackSize(), MI); + storeRegToPhysStackAt(getStackSize() + MFI->getNumParams() - RegCount, + MI->getParent(), MI, MI->getDebugLoc()); + } + + // Clear the phys stack. + // Save both NumFrameObjs and X-tack model to restore after clearing the + // stack, because this BB may not be the last in the CFG layout. + std::deque CopyXStack = XStack; + unsigned CopyNumFrameObjs = NumFrameObjs; + + // First clear the L and X areas. + clearPhysStackAtInst(StackType::X, MI, EVM::NoRegister); + clearFrameObjsAtInst(MI); + // Then clear a stack part corresponding to the arguments area. + unsigned NumSlotsToPop = MFI->getNumParams() - RegCount; + while (NumSlotsToPop--) + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::POP)); + + XStack = CopyXStack; + NumFrameObjs = CopyNumFrameObjs; + } else { + // Load return address to the top, as its old location will be overwritten. + loadRegToPhysStackTop(getStackSize(), MI); + + // Move the last 'MFI->getNumParams() + 1' return registers to the final + // locations. + // (1 - because we also overwrite the return address stack location) + for (unsigned RegCount = 0; RegCount < MFI->getNumParams() + 1; + ++RegCount) { + loadRegToPhysStackTop(ReturnRegs.pop_back_val(), MI); + unsigned Depth = getStackSize() + MFI->getNumParams() - RegCount; + storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); + } + + // Save X-tack model and restore it later, because this BB may not be the + // last in the CFG layout. + std::deque CopyXStack = XStack; + unsigned CopyNumFrameObjs = NumFrameObjs; + + // Copy all the remaining return registers to the top + // locations of the stack. + for (const auto &Reg : ReturnRegs) + loadRegToPhysStackTop(Reg, MI); + + // Now copy them and the return address to the final locations. + for (unsigned RegCount = 0; RegCount < ReturnRegs.size() + 1; ++RegCount) { + unsigned Depth = getStackSize() - RegCount - 1; + storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); + } + + // Clear the phys stack. + assert(getStackSize() >= ReturnRegs.size() + 1); + unsigned NumSlotsToPop = getStackSize() - ReturnRegs.size() - 1; + while (NumSlotsToPop--) + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::POP)); + + LStack.clear(); + XStack = CopyXStack; + NumFrameObjs = CopyNumFrameObjs; + } +} + +void StackModel::handleStackLoad(MachineInstr *MI) { + APInt Offset = MI->getOperand(2).getCImm()->getValue(); + assert((Offset.getZExtValue() % 32) == 0); + unsigned Depth = + getStackSize() - static_cast(Offset.getZExtValue() / 32) - 1; + BuildMI(*MI->getParent(), *MI, MI->getDebugLoc(), + TII->get(getDUPOpcode(Depth))); + ToErase.push_back(MI); +} + +void StackModel::handleStackStore(MachineInstr *MI) { + APInt Offset = MI->getOperand(1).getCImm()->getValue(); + assert((Offset.getZExtValue() % 32) == 0); + unsigned Depth = + getStackSize() - static_cast(Offset.getZExtValue() / 32) - 1; + const unsigned SWAPOpc = getSWAPOpcode(Depth); + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(SWAPOpc)); + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::POP)); + ToErase.push_back(MI); +} + +void StackModel::handleCall(MachineInstr *MI) { + // Calling convention: caller first pushes arguments then the return address. + // Callee removes them form the stack and pushes return values. + + MachineBasicBlock &MBB = *MI->getParent(); + // Create return destination. + MIIter It = BuildMI(MBB, MI, MI->getDebugLoc(), TII->get(EVM::JUMPDEST)); + + // Add symbol just after the jump that will be used as the return + // address from the function. + MCSymbol *RetSym = MF->getContext().createTempSymbol("FUNC_RET", true); + + // Create jump to the callee. + It = BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::JUMP)); + It->setPostInstrSymbol(*MF, RetSym); + + // Create push of the return address. + BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::PUSH8_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)) + .addGlobalAddress(CalleeOp->getGlobal()); +} + +void StackModel::clearFrameObjsAtInst(MachineInstr *MI) { + while (NumFrameObjs--) { + BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(EVM::POP)); + } +} + +void StackModel::clearPhysStackAtInst(StackType TargetStackType, + MachineInstr *MI, const Register &Reg) { + auto &Stack = getStack(TargetStackType); + // Completely clear the phys stack till the target stack. + if (Reg == EVM::NoRegister) { + if (TargetStackType == StackType::X) + peelPhysStack(StackType::L, LStack.size(), MI->getParent(), MI); + + peelPhysStack(TargetStackType, Stack.size(), MI->getParent(), MI); + return; + } + + // If the X/L-stack is empty, that means the reg is located in the E-stack, + // that means no need to store it deeper, as it can be directly consumed by + // the MI. + if (Stack.empty()) { + assert(EStack.size() == 1); + EStack.pop_front(); + return; + } + + // Assign the Reg (in terms of the stack model) to the bottom of the + // L or X stack. + StackLocation NewLoc = {TargetStackType, + static_cast(Stack.size() - 1)}; + + // Store the Reg to the physical stack at the new location. + unsigned Depth = getPhysRegDepth(NewLoc); + storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); + assert(EStack.empty()); + + // If the Reg is placed at the bottom of the X stack, first we need to + // completely clear L stack. + if (TargetStackType == StackType::X) + peelPhysStack(StackType::L, LStack.size(), MI->getParent(), MI); + + // Remove all stack items, but the last one from the target stack. + peelPhysStack(TargetStackType, NewLoc.Depth, MI->getParent(), MI); + + // Pop CondReg from the L-stack, as JUMPI consumes CondReg. + Stack.pop_front(); +} + +void StackModel::peelPhysStack(StackType Type, unsigned NumItems, + MachineBasicBlock *BB, MIIter Pos) { + auto &Stack = getStack(Type); + while (NumItems--) { + BuildMI(*BB, Pos, DebugLoc(), TII->get(EVM::POP)); + Stack.pop_front(); + } +} + +void StackModel::allocateXStack() { + MachineRegisterInfo &MRI = MF->getRegInfo(); + MachineBasicBlock &Entry = MF->front(); + for (unsigned I = 0; I < MRI.getNumVirtRegs(); ++I) { + const Register &Reg = Register::index2VirtReg(I); + const LiveInterval *LI = &LIS.getInterval(Reg); + if (LI->empty()) + continue; + + if (!LIS.intervalIsInOneMBB(*LI)) { + LLVM_DEBUG(dbgs() << "\tallocation X-stack for: " << *LI << '\n'); + BuildMI(Entry, Entry.begin(), DebugLoc(), TII->get(EVM::PUSH0)); + XStack.push_front(Reg); + } + } +} + +void StackModel::allocateFrameObjects() { + MachineBasicBlock &Entry = MF->front(); + const MachineFrameInfo &MFI = MF->getFrameInfo(); + unsigned FrameSize = 0; + for (unsigned I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) { + // We don't have variable sized objects that have size 0. + assert(MFI.getObjectSize(I)); + FrameSize += static_cast(MFI.getObjectSize(I)); + } + assert((FrameSize % 32) == 0); + + NumFrameObjs = FrameSize / 32; + for (unsigned I = 0; I < NumFrameObjs; ++I) { + LLVM_DEBUG(dbgs() << "\tallocating frame object\n"); + BuildMI(Entry, Entry.begin(), DebugLoc(), TII->get(EVM::PUSH0)); + } +} + +void StackModel::preProcess() { + assert(!MF->empty()); + allocateFrameObjects(); + allocateXStack(); + // Add JUMPDEST at the beginning of the first MBB, + // so this function can be jumped to. + MachineBasicBlock &MBB = MF->front(); + BuildMI(MBB, MBB.begin(), DebugLoc(), TII->get(EVM::JUMPDEST)); +} + +void StackModel::postProcess() { + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + if (MI.getOpcode() == EVM::CONST_I256) { + // Replace with PUSH* opcode and remove register operands + const APInt Imm = MI.getOperand(1).getCImm()->getValue(); + unsigned Opc = EVM::getPUSHOpcode(Imm); + MI.setDesc(TII->get(Opc)); + MI.removeOperand(0); + if (Opc == EVM::PUSH0) + MI.removeOperand(0); + } else if (MI.getOpcode() == EVM::COPY_I256 || + MI.getOpcode() == EVM::POP_KILL) { + // Just remove the copy instruction, as the code that + // loads the argument and stores the result performs copying. + // TODO: POP_KILL should be handled before stackification pass. + ToErase.push_back(&MI); + } else if (MI.getOpcode() == EVM::FCALL) { + handleCall(&MI); + ToErase.push_back(&MI); + } + } + } + + for (auto *MI : ToErase) + MI->eraseFromParent(); +} + +void StackModel::dumpState() const { +#ifndef NDEBUG + LLVM_DEBUG(dbgs() << "X: " << XStack.size() << ", L: " << LStack.size() + << ", E: " << EStack.size() << '\n'); + for (const auto &Reg : LStack) { + const auto Loc = *getStackLocation(Reg); + LLVM_DEBUG(dbgs() << "reg: " << Reg + << ", Type: " << static_cast(Loc.Type) + << ", Depth: " << getPhysRegDepth(Reg) << '\n'); + } + for (const auto &Reg : XStack) { + const auto Loc = *getStackLocation(Reg); + LLVM_DEBUG(dbgs() << "reg: " << Reg + << ", Type: " << static_cast(Loc.Type) + << ", Depth: " << getPhysRegDepth(Reg) << '\n'); + } + LLVM_DEBUG(dbgs() << '\n'); +#endif +} + +unsigned StackModel::getDUPOpcode(unsigned Depth) { + assert(Depth < StackAccessDepthLimit); + switch (Depth) { + case 0: + return EVM::DUP1; + case 1: + return EVM::DUP2; + case 2: + return EVM::DUP3; + case 3: + return EVM::DUP4; + case 4: + return EVM::DUP5; + case 5: + return EVM::DUP6; + case 6: + return EVM::DUP7; + case 7: + return EVM::DUP8; + case 8: + return EVM::DUP9; + case 9: + return EVM::DUP10; + case 10: + return EVM::DUP11; + case 11: + return EVM::DUP12; + case 12: + return EVM::DUP13; + case 13: + return EVM::DUP14; + case 14: + return EVM::DUP15; + case 15: + return EVM::DUP16; + default: + llvm_unreachable("Unexpected stack depth"); + } +} + +unsigned StackModel::getSWAPOpcode(unsigned Depth) { + assert(Depth < StackAccessDepthLimit); + switch (Depth) { + case 1: + return EVM::SWAP1; + case 2: + return EVM::SWAP2; + case 3: + return EVM::SWAP3; + case 4: + return EVM::SWAP4; + case 5: + return EVM::SWAP5; + case 6: + return EVM::SWAP6; + case 7: + return EVM::SWAP7; + case 8: + return EVM::SWAP8; + case 9: + return EVM::SWAP9; + case 10: + return EVM::SWAP10; + case 11: + return EVM::SWAP11; + case 12: + return EVM::SWAP12; + case 13: + return EVM::SWAP13; + case 14: + return EVM::SWAP14; + case 15: + return EVM::SWAP15; + case 16: + return EVM::SWAP16; + default: + llvm_unreachable("Unexpected stack depth"); + } +} + +namespace { +class EVMStackify final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + EVMStackify() : MachineFunctionPass(ID) {} + +private: + StringRef getPassName() const override { return "EVM stackification"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreserved(); + AU.addPreservedID(LiveVariablesID); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::TracksLiveness); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void setStackOpcodes(MachineFunction &MF); +}; +} // end anonymous namespace + +char EVMStackify::ID = 0; +INITIALIZE_PASS(EVMStackify, DEBUG_TYPE, + "Insert stack manipulation instructions to execute the code in " + "stack environment", + false, false) + +FunctionPass *llvm::createEVMStackify() { return new EVMStackify(); } + +bool EVMStackify::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Perform EVM stackification **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + MachineRegisterInfo &MRI = MF.getRegInfo(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + auto &LIS = getAnalysis().getLIS(); + + // We don't preserve SSA form. + MRI.leaveSSA(); + + assert(MRI.tracksLiveness() && "Stackify expects liveness"); + + LLVM_DEBUG(dbgs() << "ALL register intervals:\n"); + for (unsigned I = 0; I < MRI.getNumVirtRegs(); ++I) { + const Register &VReg = Register::index2VirtReg(I); + LiveInterval *LI = &LIS.getInterval(VReg); + if (LI->empty()) + continue; + + LIS.shrinkToUses(LI); + LLVM_DEBUG(LI->dump()); + if (const auto *BB = LIS.intervalIsInOneMBB(*LI)) { + LLVM_DEBUG(dbgs() << "\tIs live in: (" << BB->getName() << ")\n"); + } + } + LLVM_DEBUG(dbgs() << '\n'); + + StackModel Model(&MF, LIS, TII); + Model.preProcess(); + for (MachineBasicBlock &MBB : MF) { + Model.enterBB(&MBB); + for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) { + LLVM_DEBUG(dbgs() << MI); + Model.handleInstruction(&MI); + Model.dumpState(); + } + Model.leaveBB(&MBB); + } + Model.postProcess(); + + return true; +} diff --git a/llvm/lib/Target/EVM/EVMSubtarget.h b/llvm/lib/Target/EVM/EVMSubtarget.h index b161f90ee29f..331fb28ad5bd 100644 --- a/llvm/lib/Target/EVM/EVMSubtarget.h +++ b/llvm/lib/Target/EVM/EVMSubtarget.h @@ -48,7 +48,7 @@ class EVMSubtarget final : public EVMGenSubtargetInfo { } const EVMInstrInfo *getInstrInfo() const override { return &InstrInfo; } - const TargetRegisterInfo *getRegisterInfo() const override { + const EVMRegisterInfo *getRegisterInfo() const override { return &InstrInfo.getRegisterInfo(); } const EVMTargetLowering *getTargetLowering() const override { diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 1ae15e8a71c9..fb37129c1380 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -10,8 +10,10 @@ // //===----------------------------------------------------------------------===// -#include "EVMTargetMachine.h" #include "EVM.h" + +#include "EVMMachineFunctionInfo.h" +#include "EVMTargetMachine.h" #include "EVMTargetTransformInfo.h" #include "TargetInfo/EVMTargetInfo.h" #include "llvm/CodeGen/Passes.h" @@ -22,16 +24,31 @@ #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Transforms/IPO/GlobalDCE.h" +#include "llvm/Transforms/Utils.h" using namespace llvm; +// Disable stackification pass and keep a register form +// form of instructions. Is only used for debug purposes +// when assembly printing. +cl::opt + EVMKeepRegisters("evm-keep-registers", cl::Hidden, + cl::desc("EVM: output stack registers in" + " instruction output for test purposes only."), + cl::init(false)); + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { // Register the target. const RegisterTargetMachine X(getTheEVMTarget()); auto &PR = *PassRegistry::getPassRegistry(); initializeEVMCodegenPreparePass(PR); + initializeEVMAllocaHoistingPass(PR); initializeEVMLinkRuntimePass(PR); initializeEVMLowerIntrinsicsPass(PR); + initializeEVMOptimizeLiveIntervalsPass(PR); + initializeEVMRegColoringPass(PR); + initializeEVMSingleUseExpressionPass(PR); + initializeEVMStackifyPass(PR); } static std::string computeDataLayout() { @@ -64,11 +81,18 @@ EVMTargetMachine::getTargetTransformInfo(const Function &F) const { return TargetTransformInfo(EVMTTIImpl(this, F)); } +MachineFunctionInfo *EVMTargetMachine::createMachineFunctionInfo( + BumpPtrAllocator &Allocator, const Function &F, + const TargetSubtargetInfo *STI) const { + return EVMFunctionInfo::create(Allocator, F, STI); +} + void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PB.registerPipelineStartEPCallback( [](ModulePassManager &PM, OptimizationLevel Level) { PM.addPass(EVMLinkRuntimePass()); PM.addPass(GlobalDCEPass()); + PM.addPass(createModuleToFunctionPassAdaptor(EVMAllocaHoistingPass())); }); } @@ -98,6 +122,7 @@ class EVMPassConfig final : public TargetPassConfig { bool addGCPasses() override { return false; } bool addInstSelector() override; void addPostRegAlloc() override; + void addPreEmitPass() override; }; } // namespace @@ -143,6 +168,19 @@ void EVMPassConfig::addPostRegAlloc() { TargetPassConfig::addPostRegAlloc(); } +void EVMPassConfig::addPreEmitPass() { + TargetPassConfig::addPreEmitPass(); + + // FIXME: enable all the passes below, but the Stackify with EVMKeepRegisters. + if (!EVMKeepRegisters) { + addPass(createEVMOptimizeLiveIntervals()); + addPass(createEVMSingleUseExpression()); + // Run the register coloring pass to reduce the total number of registers. + addPass(createEVMRegColoring()); + addPass(createEVMStackify()); + } +} + TargetPassConfig *EVMTargetMachine::createPassConfig(PassManagerBase &PM) { return new EVMPassConfig(*this, PM); } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.h b/llvm/lib/Target/EVM/EVMTargetMachine.h index 08ecdd9ed7d0..9b962f59742f 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.h +++ b/llvm/lib/Target/EVM/EVMTargetMachine.h @@ -39,6 +39,10 @@ class EVMTargetMachine final : public LLVMTargetMachine { TargetTransformInfo getTargetTransformInfo(const Function &F) const override; + MachineFunctionInfo * + createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F, + const TargetSubtargetInfo *STI) const override; + TargetLoweringObjectFile *getObjFileLowering() const override { return TLOF.get(); } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp index 43c485845314..0bb82526dd88 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp @@ -10,21 +10,37 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/EVMFixupKinds.h" #include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/APInt.h" #include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCValue.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Debug.h" using namespace llvm; +#define DEBUG_TYPE "evm-asm-backend" + namespace { class EVMAsmBackend final : public MCAsmBackend { uint8_t OSABI; + std::unique_ptr MCII; public: - EVMAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI) - : MCAsmBackend(llvm::endianness::little), OSABI(OSABI) {} - ~EVMAsmBackend() override {} + EVMAsmBackend(const Target &T, const MCSubtargetInfo &STI, uint8_t OSABI) + : MCAsmBackend(llvm::endianness::big), OSABI(OSABI), MCII(T.createMCInstrInfo()) {} + EVMAsmBackend(const EVMAsmBackend &) = delete; + EVMAsmBackend(EVMAsmBackend &&) = delete; + EVMAsmBackend &operator=(const EVMAsmBackend &) = delete; + EVMAsmBackend &operator=(EVMAsmBackend &&) = delete; + ~EVMAsmBackend() override = default; void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, @@ -36,45 +52,203 @@ class EVMAsmBackend final : public MCAsmBackend { return createEVMELFObjectWriter(OSABI); } + unsigned getNumFixupKinds() const override { + return EVM::NumTargetFixupKinds; + } + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + bool evaluateTargetFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCFragment *DF, const MCValue &Target, + const MCSubtargetInfo *STI, + uint64_t &Value, bool &WasForced) override; + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value) const override { - return false; + llvm_unreachable("Handled by fixupNeedsRelaxationAdvanced"); } bool fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, const MCFixup &Fixup, bool Resolved, uint64_t Value, const MCRelaxableFragment *DF, - const bool WasForced) const override { - return false; - } + const bool WasForced) const override; - unsigned getNumFixupKinds() const override { return 0; } + void relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const override; bool mayNeedRelaxation(const MCInst &Inst, - const MCSubtargetInfo &STI) const override { - return false; - } + const MCSubtargetInfo &STI) const override; bool writeNopData(raw_ostream &OS, uint64_t Count, - const MCSubtargetInfo *STI) const override { - return false; - } + const MCSubtargetInfo *STI) const override; }; +} // end anonymous namespace + +bool EVMAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const { + for (uint64_t I = 0; I < Count; ++I) + OS << char(EVM::INVALID); + + return true; +} + +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. + // + // 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}}}; + + if (Kind < FirstTargetFixupKind) + llvm_unreachable("Unexpected fixup kind"); + + assert(static_cast(Kind - FirstTargetFixupKind) < + getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + +bool EVMAsmBackend::evaluateTargetFixup(const MCAssembler &Asm, + const MCFixup &Fixup, + const MCFragment *DF, + const MCValue &Target, + const MCSubtargetInfo *STI, + uint64_t &Value, bool &WasForced) { + assert(unsigned(Fixup.getTargetKind() - FirstTargetFixupKind) < + getNumFixupKinds() && + "Invalid kind!"); + Value = Target.getConstant(); + if (const MCSymbolRefExpr *A = Target.getSymA()) { + const MCSymbol &Sym = A->getSymbol(); + assert(Sym.isDefined()); + Value += Asm.getSymbolOffset(Sym); + return true; + } + llvm_unreachable("Unexpect target MCValue"); +} void EVMAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { - return; + const MCFixupKindInfo &Info = getFixupKindInfo(Fixup.getKind()); + unsigned NumBytes = alignTo(Info.TargetSize, 8) / 8; + + // Doesn't change encoding. + if (Value == 0) + return; + + unsigned Offset = Fixup.getOffset() + Info.TargetOffset / 8; + assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); + + LLVM_DEBUG(dbgs() << "applyFixup: value: " << Value + << ", nbytes: " << NumBytes << ", sym: "); + LLVM_DEBUG(Target.getSymA()->print(dbgs(), nullptr)); + LLVM_DEBUG(dbgs() << '\n'); + + // For each byte of the fragment that the fixup touches, mask in the + // bits from the fixup value. + for (unsigned I = 0; I != NumBytes; ++I) + Data[Offset + I] |= uint8_t((Value >> ((NumBytes - I - 1) * 8)) & 0xff); } -} // end anonymous namespace +void EVMAsmBackend::relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const { + // On each iteration of the relaxation process we try to decrease on one the + // byte width of the value to be pushed. + 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; + case EVM::PUSH3_S: + Inst.setOpcode(EVM::PUSH2_S); + break; + case EVM::PUSH2_S: + Inst.setOpcode(EVM::PUSH1_S); + break; + } +} + +bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, + const MCFixup &Fixup, + bool Resolved, uint64_t Value, + const MCRelaxableFragment *DF, + const bool WasForced) const { + assert(Resolved); + unsigned Opcode = EVM::getPUSHOpcode(APInt(256, Value)); + // The first byte of an instruction is an opcode, so + // subtract it from the total size to get size of an immediate. + unsigned OffsetByteWidth = MCII->get(Opcode).getSize() - 1; + + LLVM_DEBUG(dbgs() << "fixupNeedsRelaxationAdvanced : value: " << Value + << ", OffsetByteWidth: " << OffsetByteWidth << ", sym: "); + LLVM_DEBUG(Fixup.getValue()->print(dbgs(), nullptr)); + LLVM_DEBUG(dbgs() << '\n'); + + switch (Fixup.getTargetKind()) { + 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: + return OffsetByteWidth < 3; + case EVM::fixup_SecRel_i16: + return OffsetByteWidth < 2; + } +} + +bool EVMAsmBackend::mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const { + 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: + return Inst.getOperand(0).isExpr(); + } +} MCAsmBackend *llvm::createEVMMCAsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, const MCTargetOptions &Options) { - return new EVMAsmBackend(STI, ELF::ELFOSABI_STANDALONE); + return new EVMAsmBackend(T, STI, ELF::ELFOSABI_STANDALONE); } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp index 50a696cd1d4e..143f2be2aaea 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp @@ -19,11 +19,15 @@ using namespace llvm; namespace { class EVMELFObjectWriter final : public MCELFObjectTargetWriter { public: - EVMELFObjectWriter(uint8_t OSABI) + explicit EVMELFObjectWriter(uint8_t OSABI) : MCELFObjectTargetWriter(false, OSABI, ELF::EM_NONE, - /*HasRelocationAddend*/ true) {} + /*HasRelocationAddend*/ true){}; - ~EVMELFObjectWriter() override {} + EVMELFObjectWriter(const EVMELFObjectWriter &) = delete; + EVMELFObjectWriter(EVMELFObjectWriter &&) = delete; + EVMELFObjectWriter &operator=(EVMELFObjectWriter &&) = delete; + EVMELFObjectWriter &operator=(const EVMELFObjectWriter &) = delete; + ~EVMELFObjectWriter() override = default; protected: unsigned getRelocType(MCContext &Ctx, const MCValue &Target, diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h index 9f2b26f4fcc3..fa16da8cb23f 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h @@ -1,4 +1,4 @@ -//===-------- EVMFixupKinds.h - EVM Specific Fixup Entries ------*- C++ -*-===// +//===------ EVMFixupKinds.h - EVM Specific Fixup Entries --------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,16 +11,18 @@ #include "llvm/MC/MCFixup.h" -#undef EVM - namespace llvm { namespace EVM { - -// This table must be in the same order of -// MCFixupKindInfo Infos[EVM::NumTargetFixupKinds] -// in EVMAsmBackend.cpp. -// 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_i24, // 24-bit unsigned + fixup_SecRel_i16, // 16-bit unsigned + fixup_SecRel_i8, // 8-bit unsigned + // Marker LastTargetFixupKind, NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp index 195c1b5c8481..9ac4b2bf78e0 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "EVMInstPrinter.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp index cab4152201e8..658a617b81e0 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp @@ -15,7 +15,7 @@ using namespace llvm; -EVMMCAsmInfo::EVMMCAsmInfo(const Triple &TheTriple) { +EVMMCAsmInfo::EVMMCAsmInfo(const Triple &TT) { IsLittleEndian = false; HasFunctionAlignment = false; HasDotTypeDotSizeDirective = false; diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp index 3f593b9deb66..c36350210557 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -10,40 +10,123 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/EVMFixupKinds.h" +#include "MCTargetDesc/EVMMCExpr.h" #include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" #include "llvm/Support/raw_ostream.h" -#define DEBUG_TYPE "mccodeemitter" +using namespace llvm; -namespace llvm { +#define DEBUG_TYPE "mccodeemitter" +namespace { class EVMMCCodeEmitter final : public MCCodeEmitter { + const MCInstrInfo &MCII; + // Implementation generated by tablegen. - uint64_t getBinaryCodeForInstr(const MCInst &MI, - SmallVectorImpl &Fixups, - const MCSubtargetInfo &STI) const; + void getBinaryCodeForInstr(const MCInst &MI, SmallVectorImpl &Fixups, + APInt &Inst, APInt &Scratch, + const MCSubtargetInfo &STI) const; + + // Return binary encoding of operand. + unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, APInt &Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; void encodeInstruction(const MCInst &MI, SmallVectorImpl &CB, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const override; public: - EVMMCCodeEmitter(MCContext &ctx, MCInstrInfo const &MCII) {} + EVMMCCodeEmitter(MCContext &Ctx, MCInstrInfo const &MCII) : MCII(MCII) {} }; +EVM::Fixups getFixupForOpc(unsigned Opcode) { + 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; + case EVM::PUSH3_S: + return EVM::fixup_SecRel_i24; + case EVM::PUSH2_S: + return EVM::fixup_SecRel_i16; + case EVM::PUSH1_S: + return EVM::fixup_SecRel_i8; + } +} + +} // end anonymous namespace + void EVMMCCodeEmitter::encodeInstruction(const MCInst &MI, SmallVectorImpl &CB, SmallVectorImpl &Fixups, - const MCSubtargetInfo &STI) const {} + const MCSubtargetInfo &STI) const { + APInt Inst, Scratch; + getBinaryCodeForInstr(MI, Fixups, Inst, Scratch, STI); + + constexpr unsigned ByteSize = sizeof(std::byte) * 8; + unsigned InstSize = MCII.get(MI.getOpcode()).getSize(); -MCCodeEmitter *createEVMMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) { +#ifndef NDEBUG + const unsigned ActBitWidth = + MI.getOpcode() != EVM::STOP_S ? Inst.getActiveBits() : 1; + assert(ActBitWidth > 0 && ActBitWidth <= InstSize * ByteSize); +#endif // NDEBUG + + unsigned BitNum = InstSize * ByteSize; + while (BitNum > 0) { + std::byte ByteVal{0}; + for (unsigned I = 0; I < ByteSize; ++I, --BitNum) + ByteVal |= std::byte(Inst[BitNum - 1]) << (ByteSize - I - 1); + + support::endian::write(CB, ByteVal, llvm::endianness::big); + } +} + +unsigned EVMMCCodeEmitter::getMachineOpValue(const MCInst &MI, + const MCOperand &MO, APInt &Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + if (MO.isImm()) { + Op = MO.getImm(); + } else if (MO.isExpr()) { + auto Kind = MO.getExpr()->getKind(); + if (Kind == MCExpr::ExprKind::Target) { + 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()); + Fixups.push_back( + MCFixup::create(0, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); + } + } else { + llvm_unreachable("Unexpected MC operand type"); + } + + return 0; +} + +MCCodeEmitter *llvm::createEVMMCCodeEmitter(const MCInstrInfo &MCII, + MCContext &Ctx) { return new EVMMCCodeEmitter(Ctx, MCII); } #include "EVMGenMCCodeEmitter.inc" - -} // end of namespace llvm diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h index a75ce042ca39..4d99c9508462 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.h @@ -27,7 +27,6 @@ class EVMCImmMCExpr final : public MCTargetExpr { public: static const EVMCImmMCExpr *create(const StringRef &Data, MCContext &Ctx); - /// getOpcode - Get the kind of this expression. StringRef getString() const { return Data; } void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp index 0292734bb47f..c9e24e746f81 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -32,13 +32,13 @@ using namespace llvm; #include "EVMGenRegisterInfo.inc" static MCInstrInfo *createEVMMCInstrInfo() { - MCInstrInfo *X = new MCInstrInfo(); + auto *X = new MCInstrInfo(); InitEVMMCInstrInfo(X); return X; } static MCRegisterInfo *createEVMMCRegisterInfo(const Triple &TT) { - MCRegisterInfo *X = new MCRegisterInfo(); + auto *X = new MCRegisterInfo(); InitEVMMCRegisterInfo(X, 0); return X; } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp index fcbd764a556e..c505d06c95bd 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp @@ -14,7 +14,7 @@ using namespace llvm; -// EVMTargetStreamer implemenations +// EVMTargetStreamer implementations EVMTargetStreamer::EVMTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h index c13e7aef6503..06afc76d8fc2 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h @@ -19,21 +19,33 @@ namespace llvm { // EVM streamer interface to support EVM assembly directives class EVMTargetStreamer : public MCTargetStreamer { public: - EVMTargetStreamer(MCStreamer &S); + explicit EVMTargetStreamer(MCStreamer &S); + EVMTargetStreamer(const EVMTargetStreamer &) = delete; + EVMTargetStreamer(EVMTargetStreamer &&) = delete; + EVMTargetStreamer &operator=(const EVMTargetStreamer &) = delete; + EVMTargetStreamer &operator=(EVMTargetStreamer &&) = delete; ~EVMTargetStreamer() override; }; /// This part is for ASCII assembly output class EVMTargetAsmStreamer final : public EVMTargetStreamer { public: - EVMTargetAsmStreamer(MCStreamer &S); + explicit EVMTargetAsmStreamer(MCStreamer &S); + EVMTargetAsmStreamer(const EVMTargetAsmStreamer &) = delete; + EVMTargetAsmStreamer(EVMTargetAsmStreamer &&) = delete; + EVMTargetAsmStreamer &operator=(const EVMTargetAsmStreamer &) = delete; + EVMTargetAsmStreamer &operator=(EVMTargetAsmStreamer &&) = delete; ~EVMTargetAsmStreamer() override; }; // This part is for EVM object output class EVMTargetObjStreamer final : public EVMTargetStreamer { public: - EVMTargetObjStreamer(MCStreamer &S); + explicit EVMTargetObjStreamer(MCStreamer &S); + EVMTargetObjStreamer(const EVMTargetObjStreamer &) = delete; + EVMTargetObjStreamer(EVMTargetObjStreamer &&) = delete; + EVMTargetObjStreamer &operator=(const EVMTargetObjStreamer &) = delete; + EVMTargetObjStreamer &operator=(EVMTargetObjStreamer &&) = delete; ~EVMTargetObjStreamer() override; }; } // namespace llvm diff --git a/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt b/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt index 3c293647e05c..fc178574a43c 100644 --- a/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt +++ b/llvm/lib/Target/EVM/TargetInfo/CMakeLists.txt @@ -8,3 +8,5 @@ add_llvm_component_library(LLVMEVMInfo ADD_TO_COMPONENT EVM ) + +add_dependencies(LLVMEVMInfo EVMCommonTableGen) diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp index aa6518a11b3e..d9f73e7a1de7 100644 --- a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/APInt.h" #include "llvm/MC/TargetRegistry.h" + using namespace llvm; Target &llvm::getTheEVMTarget() { @@ -24,3 +26,99 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTargetInfo() { getTheEVMTarget(), "evm", "Ethereum Virtual Machine [experimental] (256-bit big-endian)", "EVM"); } + +#define GET_INSTRMAP_INFO +#define GET_INSTRINFO_ENUM +#define GET_INSTRINFO_MC_HELPER_DECLS +#include "EVMGenInstrInfo.inc" + +unsigned llvm::EVM::getRegisterOpcode(unsigned Opcode) { + assert(Opcode <= std::numeric_limits::max()); + auto Opc = static_cast(Opcode); + int Res = EVM::getRegisterOpcode(Opc); + assert(Res >= 0); + return static_cast(Res); +} + +unsigned llvm::EVM::getStackOpcode(unsigned Opcode) { + assert(Opcode <= std::numeric_limits::max()); + auto Opc = static_cast(Opcode); + int Res = EVM::getStackOpcode(Opc); + assert(Res >= 0); + return static_cast(Res); +} + +unsigned llvm::EVM::getPUSHOpcode(const APInt &Imm) { + if (Imm.isZero()) + return EVM::PUSH0; + + const unsigned ByteWidth = alignTo(Imm.getActiveBits(), 8) / 8; + switch (ByteWidth) { + case 1: + return EVM::PUSH1; + case 2: + return EVM::PUSH2; + case 3: + return EVM::PUSH3; + case 4: + return EVM::PUSH4; + case 5: + return EVM::PUSH5; + case 6: + return EVM::PUSH6; + case 7: + return EVM::PUSH7; + case 8: + return EVM::PUSH8; + case 9: + return EVM::PUSH9; + case 10: + return EVM::PUSH10; + case 11: + return EVM::PUSH11; + case 12: + return EVM::PUSH12; + case 13: + return EVM::PUSH13; + case 14: + return EVM::PUSH14; + case 15: + return EVM::PUSH15; + case 16: + return EVM::PUSH16; + case 17: + return EVM::PUSH17; + case 18: + return EVM::PUSH18; + case 19: + return EVM::PUSH19; + case 20: + return EVM::PUSH20; + case 21: + return EVM::PUSH21; + case 22: + return EVM::PUSH22; + case 23: + return EVM::PUSH23; + case 24: + return EVM::PUSH24; + case 25: + return EVM::PUSH25; + case 26: + return EVM::PUSH26; + case 27: + return EVM::PUSH27; + case 28: + return EVM::PUSH28; + case 29: + return EVM::PUSH29; + case 30: + return EVM::PUSH30; + case 31: + return EVM::PUSH31; + case 32: + return EVM::PUSH32; + default: + llvm_unreachable("Unexpected stack depth"); + } +} diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h index fbd317381831..5b4abc84d6cc 100644 --- a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h @@ -16,9 +16,18 @@ namespace llvm { class Target; +class APInt; Target &getTheEVMTarget(); +namespace EVM { + +unsigned getStackOpcode(unsigned Opcode); +unsigned getRegisterOpcode(unsigned Opcode); +unsigned getPUSHOpcode(const APInt &Imm); + +} // namespace EVM + } // namespace llvm #endif // LLVM_LIB_TARGET_EVM_TARGETINFO_EVMTARGETINFO_H diff --git a/llvm/test/CodeGen/EVM/add.ll b/llvm/test/CodeGen/EVM/add.ll index 4af8c0c52f05..a4a49063d0b9 100644 --- a/llvm/test/CodeGen/EVM/add.ll +++ b/llvm/test/CodeGen/EVM/add.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/aext.ll b/llvm/test/CodeGen/EVM/aext.ll index 9dbb26ffa408..4d473cd3af8b 100644 --- a/llvm/test/CodeGen/EVM/aext.ll +++ b/llvm/test/CodeGen/EVM/aext.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index d141fb6901a0..f7538ca177f2 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/call.ll b/llvm/test/CodeGen/EVM/call.ll index 7cdcaaa419a6..ab6b309e16bc 100644 --- a/llvm/test/CodeGen/EVM/call.ll +++ b/llvm/test/CodeGen/EVM/call.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/div.ll b/llvm/test/CodeGen/EVM/div.ll index cccbdac7ff77..af3296475505 100644 --- a/llvm/test/CodeGen/EVM/div.ll +++ b/llvm/test/CodeGen/EVM/div.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/frameidx.ll b/llvm/test/CodeGen/EVM/frameidx.ll index e6bec71dc3c9..b1325535a8e0 100644 --- a/llvm/test/CodeGen/EVM/frameidx.ll +++ b/llvm/test/CodeGen/EVM/frameidx.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" @@ -14,9 +14,7 @@ define i256 @alloca() nounwind { define i256 @alloca2() nounwind { ; CHECK-LABEL: alloca2: -; CHECK: CONST_I256 [[FILL:\$[0-9]+]], 4096 -; CHECK: ADD [[PTR:\$[0-9]+]], %SP, [[FILL]] -; CHECK: STACK_LOAD [[RES:\$[0-9]+]], [[PTR]] +; CHECK: STACK_LOAD [[RES:\$[0-9]+]], %SP, 4096 %fill = alloca i256, i32 128 %var = alloca i256, align 1 @@ -27,7 +25,7 @@ define i256 @alloca2() nounwind { define void @alloca3(i256 %val) nounwind { ; CHECK-LABEL: alloca3: ; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 -; CHECK: STACK_STORE %SP, [[VAL]] +; CHECK: STACK_STORE %SP, 0, [[VAL]] %fill = alloca i256, align 1 store i256 %val, ptr %fill @@ -36,9 +34,7 @@ define void @alloca3(i256 %val) nounwind { define i256 @alloca4() nounwind { ; CHECK-LABEL: alloca4: -; CHECK: CONST_I256 [[OFF:\$[0-9]+]], 64 -; CHECK: ADD [[PTR:\$[0-9]+]], %SP, [[OFF]] -; CHECK: STACK_LOAD [[RES:\$[0-9]+]], [[PTR]] +; CHECK: STACK_LOAD [[RES:\$[0-9]+]], %SP, 64 %alloca_ptr = alloca i256, i32 128 %elm = getelementptr i256, ptr %alloca_ptr, i256 2 @@ -48,10 +44,7 @@ define i256 @alloca4() nounwind { define void @alloca5(i256 %val) nounwind { ; CHECK-LABEL: alloca5: -; CHECK: ARGUMENT [[ARG1:\$[0-9]]], 0 -; CHECK: CONST_I256 [[OFF:\$[0-9]+]], 64 -; CHECK: ADD [[PTR:\$[0-9]+]], %SP, [[OFF]] -; CHECK: STACK_STORE [[PTR]], [[RES:\$[0-9]+]] +; CHECK: STACK_STORE %SP, 64, [[RES:\$[0-9]+]] %alloca_ptr = alloca i256, i32 128 %elm = getelementptr i256, ptr %alloca_ptr, i256 2 diff --git a/llvm/test/CodeGen/EVM/globals.ll b/llvm/test/CodeGen/EVM/globals.ll index b075f04aa261..996282f7b566 100644 --- a/llvm/test/CodeGen/EVM/globals.ll +++ b/llvm/test/CodeGen/EVM/globals.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/icmp.ll b/llvm/test/CodeGen/EVM/icmp.ll index 64819212f6c8..9dd6caaf4390 100644 --- a/llvm/test/CodeGen/EVM/icmp.ll +++ b/llvm/test/CodeGen/EVM/icmp.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/intrinsic.ll b/llvm/test/CodeGen/EVM/intrinsic.ll index 27d13c7115b8..d871191f3ab5 100644 --- a/llvm/test/CodeGen/EVM/intrinsic.ll +++ b/llvm/test/CodeGen/EVM/intrinsic.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" @@ -47,7 +47,7 @@ define i256 @shl(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @shl ; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: SHL [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK: SHL [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] %res = call i256 @llvm.evm.shl(i256 %rs1, i256 %rs2) ret i256 %res @@ -57,7 +57,7 @@ define i256 @shr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @shr ; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: SHR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK: SHR [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] %res = call i256 @llvm.evm.shr(i256 %rs1, i256 %rs2) ret i256 %res @@ -67,7 +67,7 @@ define i256 @sar(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @sar ; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: SAR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK: SAR [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] %res = call i256 @llvm.evm.sar(i256 %rs1, i256 %rs2) ret i256 %res @@ -516,15 +516,6 @@ define void @invalid() nounwind { ret void } -define void @pop(i256 %val) nounwind { -; CHECK-LABEL: @pop -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: POP [[IN1]] - - call void @llvm.evm.pop(i256 %val) - ret void -} - declare i256 @llvm.evm.sdiv(i256, i256) declare i256 @llvm.evm.div(i256, i256) declare i256 @llvm.evm.mod(i256, i256) diff --git a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll index 34a55daa25e3..526773d0a943 100644 --- a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll +++ b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/logical.ll b/llvm/test/CodeGen/EVM/logical.ll index feccb239f44d..4839fb4e6ffa 100644 --- a/llvm/test/CodeGen/EVM/logical.ll +++ b/llvm/test/CodeGen/EVM/logical.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/mem_call_data.ll b/llvm/test/CodeGen/EVM/mem_call_data.ll index 998ccf483899..8a57cb22a9cb 100644 --- a/llvm/test/CodeGen/EVM/mem_call_data.ll +++ b/llvm/test/CodeGen/EVM/mem_call_data.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/memintrinsics.ll b/llvm/test/CodeGen/EVM/memintrinsics.ll index 8972b095a92b..f224ae74343c 100644 --- a/llvm/test/CodeGen/EVM/memintrinsics.ll +++ b/llvm/test/CodeGen/EVM/memintrinsics.ll @@ -1,4 +1,4 @@ -; RUN: llc -O3 < %s | FileCheck %s +; RUN: llc -O3 --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/memory.ll b/llvm/test/CodeGen/EVM/memory.ll index ecaf175e3610..2cfbf3f906c4 100644 --- a/llvm/test/CodeGen/EVM/memory.ll +++ b/llvm/test/CodeGen/EVM/memory.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/mod.ll b/llvm/test/CodeGen/EVM/mod.ll index 68dd697be437..c0b4e9976952 100644 --- a/llvm/test/CodeGen/EVM/mod.ll +++ b/llvm/test/CodeGen/EVM/mod.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll index 5c538882b2b4..9c286d256707 100644 --- a/llvm/test/CodeGen/EVM/mul.ll +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll index c534b0e0af95..084ec33481a4 100644 --- a/llvm/test/CodeGen/EVM/select.ll +++ b/llvm/test/CodeGen/EVM/select.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/sext.ll b/llvm/test/CodeGen/EVM/sext.ll index 18c5a89ef16b..6f57ab33ab58 100644 --- a/llvm/test/CodeGen/EVM/sext.ll +++ b/llvm/test/CodeGen/EVM/sext.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/shift.ll b/llvm/test/CodeGen/EVM/shift.ll index 33c7c3d53ced..db9eb65614d9 100644 --- a/llvm/test/CodeGen/EVM/shift.ll +++ b/llvm/test/CodeGen/EVM/shift.ll @@ -1,12 +1,12 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @shl(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @shl -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SHL [[RES:\$[0-9]+]], [[IN1]], [[IN2]] %res = shl i256 %rs1, %rs2 @@ -15,8 +15,8 @@ define i256 @shl(i256 %rs1, i256 %rs2) nounwind { define i256 @shr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @shr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SHR [[RES:\$[0-9]+]], [[IN1]], [[IN2]] %res = lshr i256 %rs1, %rs2 @@ -25,8 +25,8 @@ define i256 @shr(i256 %rs1, i256 %rs2) nounwind { define i256 @sar(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @sar -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SAR [[RES:\$[0-9]+]], [[IN1]], [[IN2]] %res = ashr i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/signextload.ll b/llvm/test/CodeGen/EVM/signextload.ll index 72ab9b6bbd7f..06da46715708 100644 --- a/llvm/test/CodeGen/EVM/signextload.ll +++ b/llvm/test/CodeGen/EVM/signextload.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/storage.ll b/llvm/test/CodeGen/EVM/storage.ll index be51d65b027f..24336cd0164f 100644 --- a/llvm/test/CodeGen/EVM/storage.ll +++ b/llvm/test/CodeGen/EVM/storage.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/sub.ll b/llvm/test/CodeGen/EVM/sub.ll index 7699781a1ff5..6b88285ef48d 100644 --- a/llvm/test/CodeGen/EVM/sub.ll +++ b/llvm/test/CodeGen/EVM/sub.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/truncstore.ll b/llvm/test/CodeGen/EVM/truncstore.ll index 9c29961b5711..ef71df6530ea 100644 --- a/llvm/test/CodeGen/EVM/truncstore.ll +++ b/llvm/test/CodeGen/EVM/truncstore.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/tstorage.ll b/llvm/test/CodeGen/EVM/tstorage.ll index 2c719c539d04..c100d67d9992 100644 --- a/llvm/test/CodeGen/EVM/tstorage.ll +++ b/llvm/test/CodeGen/EVM/tstorage.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/zero_any_extload.ll b/llvm/test/CodeGen/EVM/zero_any_extload.ll index 9f9b34c7ddf4..51971aa83a22 100644 --- a/llvm/test/CodeGen/EVM/zero_any_extload.ll +++ b/llvm/test/CodeGen/EVM/zero_any_extload.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/CodeGen/EVM/zext.ll b/llvm/test/CodeGen/EVM/zext.ll index 203803f782d0..b73f810fd440 100644 --- a/llvm/test/CodeGen/EVM/zext.ll +++ b/llvm/test/CodeGen/EVM/zext.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s | FileCheck %s +; RUN: llc --evm-keep-registers < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" diff --git a/llvm/test/TableGen/VarLenDecoder.td b/llvm/test/TableGen/VarLenDecoder.td index 5cf0bf891185..3bf2ecd3a688 100644 --- a/llvm/test/TableGen/VarLenDecoder.td +++ b/llvm/test/TableGen/VarLenDecoder.td @@ -46,10 +46,10 @@ def FOO32 : MyVarInst { ); } -// CHECK: MCD::OPC_ExtractField, 3, 5, // Inst{7-3} ... -// CHECK-NEXT: MCD::OPC_FilterValue, 8, 4, 0, 0, // Skip to: 12 +// CHECK: MCD::OPC_ExtractField, 3, 5, 0, // Inst{7-3} ... +// CHECK-NEXT: MCD::OPC_FilterValue, 8, 4, 0, 0, // Skip to: 13 // CHECK-NEXT: MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: FOO16 -// CHECK-NEXT: MCD::OPC_FilterValue, 9, 4, 0, 0, // Skip to: 21 +// CHECK-NEXT: MCD::OPC_FilterValue, 9, 4, 0, 0, // Skip to: 22 // CHECK-NEXT: MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: FOO32 // CHECK-NEXT: MCD::OPC_Fail, diff --git a/llvm/test/TableGen/long-offset-decode.td b/llvm/test/TableGen/long-offset-decode.td new file mode 100644 index 000000000000..65a00fdc6922 --- /dev/null +++ b/llvm/test/TableGen/long-offset-decode.td @@ -0,0 +1,27 @@ +// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s | FileCheck %s + +// Checks decoding of long instructions with the bit width > 256. + +include "llvm/Target/Target.td" + +def archInstrInfo : InstrInfo { } + +def arch : Target { + let InstructionSet = archInstrInfo; +} + +class TestInstruction : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins); + field bits<264> Inst; +} + +def InstA : TestInstruction { + let Size = 33; + let Inst{263-256} = 0x7E; + let AsmString = "InstA"; +} + +// CHECK: /* 0 */ MCD::OPC_CheckField, 128, 2, 8, 0, 126, 4, 0, 0, // Skip to: 13 +// CHECK-NEXT: /* 9 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 0, // Opcode: InstA +// CHECK-NEXT: /* 13 */ MCD::OPC_Fail, diff --git a/llvm/test/TableGen/trydecode-emission.td b/llvm/test/TableGen/trydecode-emission.td index 20d2446eeac7..e7c3194e1f24 100644 --- a/llvm/test/TableGen/trydecode-emission.td +++ b/llvm/test/TableGen/trydecode-emission.td @@ -33,11 +33,11 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 18, 0, 0, // Skip to: 26 -// CHECK-NEXT: /* 8 */ MCD::OPC_CheckField, 2, 2, 0, 7, 0, 0, // Skip to: 22 -// CHECK-NEXT: /* 15 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 22 -// CHECK-NEXT: /* 22 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA -// CHECK-NEXT: /* 26 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, 0, // Inst{7-4} ... +// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 19, 0, 0, // Skip to: 28 +// CHECK-NEXT: /* 9 */ MCD::OPC_CheckField, 2, 2, 0, 0, 7, 0, 0, // Skip to: 24 +// CHECK-NEXT: /* 17 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 24 +// CHECK-NEXT: /* 24 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA +// CHECK-NEXT: /* 28 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/trydecode-emission2.td b/llvm/test/TableGen/trydecode-emission2.td index 0584034e4123..012deec76d1e 100644 --- a/llvm/test/TableGen/trydecode-emission2.td +++ b/llvm/test/TableGen/trydecode-emission2.td @@ -30,15 +30,15 @@ def InstB : TestInstruction { let hasCompleteDecoder = 0; } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 2, 1, // Inst{2} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 36, 0, 0, // Skip to: 44 -// CHECK-NEXT: /* 8 */ MCD::OPC_ExtractField, 5, 3, // Inst{7-5} ... -// CHECK-NEXT: /* 11 */ MCD::OPC_FilterValue, 0, 28, 0, 0, // Skip to: 44 -// CHECK-NEXT: /* 16 */ MCD::OPC_CheckField, 0, 2, 3, 7, 0, 0, // Skip to: 30 -// CHECK-NEXT: /* 23 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 30 -// CHECK-NEXT: /* 30 */ MCD::OPC_CheckField, 3, 2, 0, 7, 0, 0, // Skip to: 44 -// CHECK-NEXT: /* 37 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 1, 0, 0, 0, // Opcode: InstA, skip to: 44 -// CHECK-NEXT: /* 44 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 2, 1, 0, // Inst{2} ... +// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 39, 0, 0, // Skip to: 48 +// CHECK-NEXT: /* 9 */ MCD::OPC_ExtractField, 5, 3, 0, // Inst{7-5} ... +// CHECK-NEXT: /* 13 */ MCD::OPC_FilterValue, 0, 30, 0, 0, // Skip to: 48 +// CHECK-NEXT: /* 18 */ MCD::OPC_CheckField, 0, 2, 0, 3, 7, 0, 0, // Skip to: 33 +// CHECK-NEXT: /* 26 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 33 +// CHECK-NEXT: /* 33 */ MCD::OPC_CheckField, 3, 2, 0, 0, 7, 0, 0, // Skip to: 48 +// CHECK-NEXT: /* 41 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 1, 0, 0, 0, // Opcode: InstA, skip to: 48 +// CHECK-NEXT: /* 48 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } // CHECK: if (!Check(S, DecodeInstA(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/trydecode-emission3.td b/llvm/test/TableGen/trydecode-emission3.td index 4c5be7e1af22..f986218f929a 100644 --- a/llvm/test/TableGen/trydecode-emission3.td +++ b/llvm/test/TableGen/trydecode-emission3.td @@ -34,11 +34,11 @@ def InstB : TestInstruction { let AsmString = "InstB"; } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... -// CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 18, 0, 0, // Skip to: 26 -// CHECK-NEXT: /* 8 */ MCD::OPC_CheckField, 2, 2, 0, 7, 0, 0, // Skip to: 22 -// CHECK-NEXT: /* 15 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 22 -// CHECK-NEXT: /* 22 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA -// CHECK-NEXT: /* 26 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, 0, // Inst{7-4} ... +// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 19, 0, 0, // Skip to: 28 +// CHECK-NEXT: /* 9 */ MCD::OPC_CheckField, 2, 2, 0, 0, 7, 0, 0, // Skip to: 24 +// CHECK-NEXT: /* 17 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 24 +// CHECK-NEXT: /* 24 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA +// CHECK-NEXT: /* 28 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstBOp(MI, tmp, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/test/TableGen/trydecode-emission4.td b/llvm/test/TableGen/trydecode-emission4.td index 1e51ba5e4076..162405947865 100644 --- a/llvm/test/TableGen/trydecode-emission4.td +++ b/llvm/test/TableGen/trydecode-emission4.td @@ -33,12 +33,12 @@ def InstB : TestInstruction { } -// CHECK: /* 0 */ MCD::OPC_ExtractField, 250, 3, 4, // Inst{509-506} ... -// CHECK-NEXT: /* 4 */ MCD::OPC_FilterValue, 0, 19, 0, 0, // Skip to: 28 -// CHECK-NEXT: /* 9 */ MCD::OPC_CheckField, 248, 3, 2, 0, 7, 0, 0, // Skip to: 24 -// CHECK-NEXT: /* 17 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 24 -// CHECK-NEXT: /* 24 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA -// CHECK-NEXT: /* 28 */ MCD::OPC_Fail, +// CHECK: /* 0 */ MCD::OPC_ExtractField, 250, 3, 4, 0, // Inst{509-506} ... +// CHECK-NEXT: /* 5 */ MCD::OPC_FilterValue, 0, 20, 0, 0, // Skip to: 30 +// CHECK-NEXT: /* 10 */ MCD::OPC_CheckField, 248, 3, 2, 0, 0, 7, 0, 0, // Skip to: 26 +// CHECK-NEXT: /* 19 */ MCD::OPC_TryDecode, {{[0-9]+}}, {{[0-9]+}}, 0, 0, 0, 0, // Opcode: InstB, skip to: 26 +// CHECK-NEXT: /* 26 */ MCD::OPC_Decode, {{[0-9]+}}, {{[0-9]+}}, 1, // Opcode: InstA +// CHECK-NEXT: /* 30 */ MCD::OPC_Fail, // CHECK: if (!Check(S, DecodeInstB(MI, insn, Address, Decoder))) { DecodeComplete = false; return MCDisassembler::Fail; } diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index f46a8d1e9f08..eaea8d2d6aa0 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -707,7 +707,11 @@ void Filter::emitTableEntry(DecoderTableInfo &TableInfo) const { raw_svector_ostream S(SBytes); encodeULEB128(StartBit, S); TableInfo.Table.insert(TableInfo.Table.end(), SBytes.begin(), SBytes.end()); - TableInfo.Table.push_back(NumBits); + + // EVM local begin + TableInfo.Table.push_back((uint8_t)NumBits); + TableInfo.Table.push_back((uint8_t)(NumBits >> 8)); + // EVM local end // A new filter entry begins a new scope for fixup resolution. TableInfo.FixupStack.emplace_back(); @@ -861,8 +865,12 @@ void DecoderEmitter::emitTable(formatted_raw_ostream &OS, DecoderTable &Table, assert(ErrMsg == nullptr && "ULEB128 value too large!"); I += emitULEB128(I, OS); + // EVM local begin + // 16-bit Len value unsigned Len = *I++; - OS << Len << ", // Inst{"; + Len |= (*I++) << 8; + OS << (Len & 0xFF) << ", " << (Len >> 8) << ", // Inst{"; + // EVM local end if (Len > 1) OS << (Start + Len - 1) << "-"; OS << Start << "} ...\n"; @@ -885,9 +893,12 @@ void DecoderEmitter::emitTable(formatted_raw_ostream &OS, DecoderTable &Table, OS.indent(Indentation) << "MCD::OPC_CheckField, "; // ULEB128 encoded start value. I += emitULEB128(I, OS); - // 8-bit length. + // EVM local begin + // 16-bit Len value unsigned Len = *I++; - OS << Len << ", "; + Len |= (*I++) << 8; + OS << (Len & 0xFF) << ", " << (Len >> 8) << ", "; + // EVM local begin // ULEB128 encoded field value. I += emitULEB128(I, OS); @@ -1470,7 +1481,10 @@ void FilterChooser::emitSingletonTableEntry(DecoderTableInfo &TableInfo, for (P = Buffer; *P >= 128; ++P) TableInfo.Table.push_back(*P); TableInfo.Table.push_back(*P); - TableInfo.Table.push_back(NumBits); + // EVM local begin + TableInfo.Table.push_back((uint8_t)NumBits); + TableInfo.Table.push_back((uint8_t)(NumBits >> 8)); + // EVM local end encodeULEB128(FieldVals[I - 1], Buffer); for (P = Buffer; *P >= 128; ++P) TableInfo.Table.push_back(*P); @@ -2270,7 +2284,10 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, case MCD::OPC_ExtractField: { // Decode the start value. unsigned Start = decodeULEB128AndIncUnsafe(++Ptr); - unsigned Len = *Ptr++;)"; + // EVM local begin + unsigned Len = *Ptr++; + Len |= (*Ptr++) << 8;)"; + // EVM local end if (IsVarLenInst) OS << "\n makeUp(insn, Start + Len);"; OS << R"( @@ -2299,7 +2316,10 @@ static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, case MCD::OPC_CheckField: { // Decode the start value. unsigned Start = decodeULEB128AndIncUnsafe(++Ptr); - unsigned Len = *Ptr;)"; + // EVM local begin + unsigned Len = *Ptr++; + Len |= (*Ptr) << 8;)"; + // EVM local end if (IsVarLenInst) OS << "\n makeUp(insn, Start + Len);"; OS << R"( From df859e3613b54585b422e7ffc6c6b88b1e4cace2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Fri, 19 Apr 2024 14:43:53 +0200 Subject: [PATCH 060/203] [EVM] Fix analyses invalidation in runtime linking passes `EVMLinkRuntime` don't invalidate analyses when update a module. The patch invalidates all analyses if there were changes. --- llvm/lib/Target/EVM/EVMLinkRuntime.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp index a6692fbe506f..79cd13b0b385 100644 --- a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp +++ b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp @@ -110,6 +110,7 @@ ModulePass *llvm::createEVMLinkRuntimePass() { return new EVMLinkRuntime(); } PreservedAnalyses EVMLinkRuntimePass::run(Module &M, ModuleAnalysisManager &AM) { - EVMLinkRuntimeImpl(M, STDLIB_DATA); + if (EVMLinkRuntimeImpl(M, STDLIB_DATA)) + return PreservedAnalyses::none(); return PreservedAnalyses::all(); } From 751aae569c911fe35f0e79934056438b31691013 Mon Sep 17 00:00:00 2001 From: Alan Li Date: Thu, 20 Jul 2023 14:57:54 -0400 Subject: [PATCH 061/203] [EVM] General improvements and fix for code quality * quenched compiler warnings (zero length array, using deprecated functions) * addressed static analyzer warnings * avoid assigning def property to input operands --- llvm/lib/Support/CommandLine.cpp | 3 ++- llvm/lib/Target/EVM/EVM.td | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index b0993bc8eb8d..46f08d26537c 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -2850,6 +2850,7 @@ void LLVMParseCommandLineOptions(int argc, const char *const *argv, // EVM local begin int LLVMPrintCommitIDTo(char* Buf) { - return sprintf(Buf, "%s", EVM_LLVM_COMMIT_ID); + return snprintf(Buf, sizeof(EVM_LLVM_COMMIT_ID), "%s", + EVM_LLVM_COMMIT_ID); } // EVM local end diff --git a/llvm/lib/Target/EVM/EVM.td b/llvm/lib/Target/EVM/EVM.td index 770b989b8763..5852108fce25 100644 --- a/llvm/lib/Target/EVM/EVM.td +++ b/llvm/lib/Target/EVM/EVM.td @@ -16,6 +16,9 @@ include "llvm/Target/Target.td" +class Proc Features> + : Processor; +def : Proc<"generic", []>; //===----------------------------------------------------------------------===// // Register File Description From 38c12ea83bd5ab8a7dcf907a89312aeb08cb270f Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 31 Jul 2023 12:59:36 +0200 Subject: [PATCH 062/203] [Clang][LLDB] Fix handling of 256-bit types Signed-off-by: Vladimir Radosavljevic --- clang/lib/AST/ASTImporter.cpp | 10 +++++++ .../TypeSystem/Clang/TypeSystemClang.cpp | 30 +++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index e95992b99f7e..853492964c8b 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -371,6 +371,9 @@ namespace clang { #define TYPE(Class, Base) \ ExpectedType Visit##Class##Type(const Class##Type *T); #include "clang/AST/TypeNodes.inc" + // EVM local begin + ExpectedType VisitBitIntType(const BitIntType *T); + // EVM local end // Importing declarations Error ImportDeclParts(NamedDecl *D, DeclarationName &Name, NamedDecl *&ToD, @@ -1073,6 +1076,13 @@ ExpectedType ASTNodeImporter::VisitAtomicType(const AtomicType *T){ return Importer.getToContext().getAtomicType(*UnderlyingTypeOrErr); } +// EVM local begin +ExpectedType ASTNodeImporter::VisitBitIntType(const BitIntType *T) { + return Importer.getToContext().getBitIntType(T->isUnsigned(), + T->getNumBits()); +} +// EVM local end + ExpectedType ASTNodeImporter::VisitBuiltinType(const BuiltinType *T) { switch (T->getKind()) { #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index f70efe5ed57e..9d08137046d2 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -764,7 +764,9 @@ TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, return GetType(ast.UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.UnsignedInt128Ty)) return GetType(ast.UnsignedInt128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(true, bit_size)); + // EVM local end case eEncodingSint: if (QualTypeMatchesBitSize(bit_size, ast, ast.SignedCharTy)) @@ -779,7 +781,9 @@ TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, return GetType(ast.LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.Int128Ty)) return GetType(ast.Int128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(false, bit_size)); + // EVM local end case eEncodingIEEE754: if (QualTypeMatchesBitSize(bit_size, ast, ast.FloatTy)) @@ -1004,7 +1008,9 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( return GetType(ast.LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.Int128Ty)) return GetType(ast.Int128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(false, bit_size)); + // EVM local end case DW_ATE_signed_char: if (type_name == "char") { @@ -1056,7 +1062,9 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( return GetType(ast.UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.UnsignedInt128Ty)) return GetType(ast.UnsignedInt128Ty); - break; + // EVM local begin + return GetType(ast.getBitIntType(true, bit_size)); + // EVM local end case DW_ATE_unsigned_char: if (type_name == "char") { @@ -2369,7 +2377,9 @@ CompilerType TypeSystemClang::GetIntTypeFromBitSize(size_t bit_size, if (bit_size == ast.getTypeSize(ast.UnsignedInt128Ty)) return GetType(ast.UnsignedInt128Ty); } - return CompilerType(); + // EVM local begin + return GetType(ast.getBitIntType(!is_signed, bit_size)); + // EVM local end } CompilerType TypeSystemClang::GetPointerSizedIntType(bool is_signed) { @@ -3832,6 +3842,16 @@ TypeSystemClang::GetTypeInfo(lldb::opaque_compiler_type_t type, ->getModifiedType() .getAsOpaquePtr(), pointee_or_element_clang_type); + // EVM local begin + case clang::Type::BitInt: + case clang::Type::DependentBitInt: { + uint32_t bitint_type_flags = + eTypeIsBuiltIn | eTypeHasValue | eTypeIsScalar | eTypeIsInteger; + if (qual_type->isSignedIntegerType()) + bitint_type_flags |= eTypeIsSigned; + return bitint_type_flags; + } + // EVM local end case clang::Type::Builtin: { const clang::BuiltinType *builtin_type = llvm::cast(qual_type->getCanonicalTypeInternal()); From ad8dfd782345ce947ae268c5f235339096628f9b Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 21 Aug 2024 18:16:10 +0200 Subject: [PATCH 063/203] [EVM] Add 'clone' implementation of EVMMachineFunctionInfo. --- llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp | 7 +++++++ llvm/lib/Target/EVM/EVMMachineFunctionInfo.h | 9 +++++---- llvm/test/tools/llvm-reduce/file-output-type.test | 2 -- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp index 2ee66cb5f11a..63574319e396 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp @@ -14,3 +14,10 @@ using namespace llvm; EVMFunctionInfo::~EVMFunctionInfo() = default; // anchor. + +MachineFunctionInfo * +EVMFunctionInfo::clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap + &Src2DstMBB) const { + return DestMF.cloneInfo(*this); +} diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h index 900ac7cfc706..b39709960358 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h @@ -37,12 +37,13 @@ class EVMFunctionInfo final : public MachineFunctionInfo { public: explicit EVMFunctionInfo(MachineFunction &MF) {} EVMFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {} - EVMFunctionInfo(const EVMFunctionInfo &) = delete; - EVMFunctionInfo(EVMFunctionInfo &&) = delete; - EVMFunctionInfo &operator=(const EVMFunctionInfo &) = delete; - EVMFunctionInfo &operator=(EVMFunctionInfo &&) = delete; ~EVMFunctionInfo() override; + MachineFunctionInfo * + clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF, + const DenseMap &Src2DstMBB) + const override; + void stackifyVReg(MachineRegisterInfo &MRI, unsigned VReg) { assert(MRI.getUniqueVRegDef(VReg)); auto I = Register::virtReg2Index(VReg); diff --git a/llvm/test/tools/llvm-reduce/file-output-type.test b/llvm/test/tools/llvm-reduce/file-output-type.test index 74d280d07803..93b9ca4acb08 100644 --- a/llvm/test/tools/llvm-reduce/file-output-type.test +++ b/llvm/test/tools/llvm-reduce/file-output-type.test @@ -1,5 +1,3 @@ -# TODO: CPR-1629 EVM does not implement MachineFunctionInfo cloning -# XFAIL: target=evm{{.*}} # REQUIRES: default_triple # RUN: rm -rf %t.dir && mkdir %t.dir && cd %t.dir From caea83c525d2f5b075a0ea00310b1bd7a3f797e2 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 1 Feb 2024 12:39:01 +0100 Subject: [PATCH 064/203] [EVM] Add datasize/dataoffset intrinsics This includes: - datasize/dataoffset intrinsics - EVM specific ELF relocations --- llvm/include/llvm/BinaryFormat/ELF.h | 10 ++++ .../llvm/BinaryFormat/ELFRelocs/EVM.def | 7 +++ llvm/include/llvm/IR/IntrinsicsEVM.td | 16 ++++++ llvm/include/llvm/MC/MCExpr.h | 5 +- llvm/include/module.modulemap | 1 + llvm/lib/BinaryFormat/ELF.cpp | 7 +++ llvm/lib/MC/ELFObjectWriter.cpp | 20 +------- llvm/lib/MC/MCExpr.cpp | 6 +++ llvm/lib/MC/MCObjectFileInfo.cpp | 4 ++ llvm/lib/Object/ELF.cpp | 13 +++++ llvm/lib/Target/EVM/CMakeLists.txt | 1 + llvm/lib/Target/EVM/EVMISelLowering.cpp | 34 +++++++++++++ llvm/lib/Target/EVM/EVMISelLowering.h | 5 ++ llvm/lib/Target/EVM/EVMInstrInfo.td | 6 +++ llvm/lib/Target/EVM/EVMMCInstLower.cpp | 16 ++++-- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 4 +- llvm/lib/Target/EVM/EVMTargetObjectFile.cpp | 20 ++++++++ llvm/lib/Target/EVM/EVMTargetObjectFile.h | 30 +++++++++++ .../Target/EVM/MCTargetDesc/EVMAsmBackend.cpp | 43 +++++++++++----- .../EVM/MCTargetDesc/EVMELFObjectWriter.cpp | 35 +++++++++---- .../Target/EVM/MCTargetDesc/EVMFixupKinds.h | 1 + .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 20 ++++++-- llvm/test/MC/EVM/datasize_relocation.ll | 51 +++++++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 3 ++ 24 files changed, 305 insertions(+), 53 deletions(-) create mode 100644 llvm/include/llvm/BinaryFormat/ELFRelocs/EVM.def create mode 100644 llvm/lib/Target/EVM/EVMTargetObjectFile.cpp create mode 100644 llvm/lib/Target/EVM/EVMTargetObjectFile.h create mode 100644 llvm/test/MC/EVM/datasize_relocation.ll diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 456cffff6b4a..1769a4427462 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -321,6 +321,9 @@ enum { EM_VE = 251, // NEC SX-Aurora VE EM_CSKY = 252, // C-SKY 32-bit processor EM_LOONGARCH = 258, // LoongArch + // EVM local begin + EM_EVM = 261 // EVM + // EVM local end }; // Object file classes. @@ -427,6 +430,13 @@ enum { #include "ELFRelocs/AArch64.def" }; +// EVM local begin +// ELF Relocation types for EVM +enum { +#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 6a0f6bf29425..0d8ecf00f5f5 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 118b1dd88525..784ea521f6d0 100644 --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -362,7 +362,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 b00da6d7cd28..5ff2382f5a38 100644 --- a/llvm/include/module.modulemap +++ b/llvm/include/module.modulemap @@ -103,6 +103,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 9878f5769087..519e417487eb 100644 --- a/llvm/lib/BinaryFormat/ELF.cpp +++ b/llvm/lib/BinaryFormat/ELF.cpp @@ -197,6 +197,9 @@ uint16_t ELF::convertArchNameToEMachine(StringRef Arch) { .Case("ve", EM_VE) .Case("csky", EM_CSKY) .Case("loongarch", EM_LOONGARCH) + // EVM local begin + .Case("evm", EM_EVM) + // EVM local end .Default(EM_NONE); } @@ -563,6 +566,10 @@ StringRef ELF::convertEMachineToArchName(uint16_t EMachine) { return "csky"; case EM_LOONGARCH: return "loongarch"; + // 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 89e70e42185f..f29cc92394cf 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -966,14 +966,7 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { RevGroupMapTy RevGroupMap; // 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 ... SmallVector>, 0> Groups; @@ -991,13 +984,6 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { 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); - continue; - } - // EVM local end writeSectionData(Asm, Section); @@ -1037,10 +1023,6 @@ uint64_t ELFWriter::writeObject(MCAssembler &Asm) { OWriter.TargetObjectWriter->addTargetSectionFlags(Ctx, Section); } - // EVM local begin - if (Ctx.getTargetTriple().isEVM()) - return W.OS.tell() - StartOffset; - // EVM local end for (auto &[Group, Members] : Groups) { // Remember the offset into the file for this section. diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp index 49b1a2a7b00a..6f207bb23b9a 100644 --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -400,6 +400,9 @@ 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 // clang-format on } llvm_unreachable("Invalid variant kind"); @@ -538,6 +541,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 6dadd9752646..bb7cc4e70b05 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -392,6 +392,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 e47a40b8715d..c5c16850bd55 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -181,6 +181,15 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, break; } break; + // EVM local begin + case ELF::EM_EVM: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/EVM.def" + default: + break; + } + break; + // EVM local end default: break; } @@ -233,6 +242,10 @@ uint32_t llvm::object::getELFRelativeRelocationType(uint32_t Machine) { break; case ELF::EM_LOONGARCH: return ELF::R_LARCH_RELATIVE; + // EVM local begin + case ELF::EM_EVM: + break; + // EVM local end default: break; } diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 0d0158d8793e..df537c407c3c 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -55,6 +55,7 @@ add_llvm_target(EVMCodeGen EVMStackify.cpp EVMSubtarget.cpp EVMTargetMachine.cpp + EVMTargetObjectFile.cpp EVMTargetTransformInfo.cpp LINK_COMPONENTS diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index 56eefceafa20..f4f65d50ed28 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); 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.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 02d7d745f851..0ece41690a4b 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -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 in +defm DATA + : I<(outs GPR:$dst), (ins jmptarget:$reloc), [], "", + "", 0, 0>; diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index 365734db49f2..ceea05b7f723 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -51,6 +51,9 @@ static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) { OutMI.erase(&MO); } } + + if (RegOpcode == EVM::DATA) + OutMI.setOpcode(EVM::PUSH4_S); } MCSymbol * @@ -130,10 +133,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) + 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)); diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index fb37129c1380..f38e511c064d 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -14,10 +14,10 @@ #include "EVMMachineFunctionInfo.h" #include "EVMTargetMachine.h" +#include "EVMTargetObjectFile.h" #include "EVMTargetTransformInfo.h" #include "TargetInfo/EVMTargetInfo.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 +70,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(); diff --git a/llvm/lib/Target/EVM/EVMTargetObjectFile.cpp b/llvm/lib/Target/EVM/EVMTargetObjectFile.cpp new file mode 100644 index 000000000000..1da524fbe949 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetObjectFile.cpp @@ -0,0 +1,20 @@ +//===------------- EVMTargetObjectFile.cpp - EVM Object Info --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the functions of the EVM-specific subclass +// of TargetLoweringObjectFile. +// +//===----------------------------------------------------------------------===// + +#include "EVMTargetObjectFile.h" + +using namespace llvm; + +// Code sections need to be aligned on 1, otherwise linker will add padding +// between .text sections of the object files being linked. +unsigned EVMELFTargetObjectFile::getTextSectionAlignment() const { return 1; } diff --git a/llvm/lib/Target/EVM/EVMTargetObjectFile.h b/llvm/lib/Target/EVM/EVMTargetObjectFile.h new file mode 100644 index 000000000000..7de553b2480e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMTargetObjectFile.h @@ -0,0 +1,30 @@ +//===---------- 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; + + unsigned getTextSectionAlignment() const override; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp index 0bb82526dd88..433e446ff1d1 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp @@ -96,18 +96,18 @@ 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_i64", 0, 8 * 8, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i56", 0, 8 * 7, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i48", 0, 8 * 6, MCFixupKindInfo::FKF_IsTarget}, + {"fixup_SecRel_i40", 0, 8 * 5, 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"); @@ -127,6 +127,13 @@ 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 (const MCSymbolRefExpr *A = Target.getSymA()) { const MCSymbol &Sym = A->getSymbol(); @@ -142,14 +149,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 @@ -199,6 +208,12 @@ bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, bool Resolved, uint64_t Value, const MCRelaxableFragment *DF, 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 @@ -210,7 +225,7 @@ bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, 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: diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMELFObjectWriter.cpp index 143f2be2aaea..de3d796b2214 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,33 @@ class EVMELFObjectWriter final : public MCELFObjectTargetWriter { ~EVMELFObjectWriter() override = default; protected: + bool needsRelocateWithSymbol(const MCValue &Val, 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 MCValue &Val, + 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..8d63558bd5ef 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h @@ -22,6 +22,7 @@ enum Fixups { 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 c36350210557..76ddedd8539e 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -50,7 +50,7 @@ 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"); @@ -63,7 +63,15 @@ EVM::Fixups getFixupForOpc(unsigned Opcode) { 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: @@ -113,9 +121,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/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 f0a22f1568be..a0f164cb7359 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1284,6 +1284,9 @@ const EnumEntry ElfMachineType[] = { ENUM_ENT(EM_BPF, "EM_BPF"), ENUM_ENT(EM_VE, "NEC SX-Aurora Vector Engine"), ENUM_ENT(EM_LOONGARCH, "LoongArch"), + // EVM local begin + ENUM_ENT(EM_EVM, "EVM"), + // EVM local end }; const EnumEntry ElfSymbolBindings[] = { From 43f45b9bc24d289d8a8d38b173cc45040d698c56 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 1 Mar 2024 11:22:50 +0100 Subject: [PATCH 065/203] [LLD] Adding support of in/out memory buffers to LLD. This extends lld-as-library functionality and allows passing to and receiving from the lld files(object, archives, shared libs) via memory buffers. Input files are passes as an array of memory buffers, where files names represent indexes into this array. The implementation defines following new API function (not C-API): lld::lldMainMemBuf(llvm::ArrayRef inBuffers, llvm::raw_pwrite_stream *outBufffer, llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, llvm::ArrayRef drivers) In simple case, where there is only one input object file and one linker script, the 'args' may look: {"ld.lld", "-T", "0", "1", "--oformat=binary" }, where "0" means that the linker script is passed in inBuffers[0] and "1" that the object file is passed in inBuffers[1]. Please, note this implementation has a quite limited functionality. Only object files and linker scripts can be passes via memory buffers. --- lld/Common/DriverDispatcher.cpp | 76 ++++++++++++++++++++ lld/ELF/Config.h | 5 ++ lld/ELF/Driver.cpp | 66 ++++++++++++++++- lld/ELF/InputFiles.cpp | 32 ++++++--- lld/ELF/Writer.cpp | 22 ++++++ lld/include/lld/Common/Driver.h | 40 +++++++++++ llvm/include/llvm/Support/FileOutputBuffer.h | 11 +++ llvm/lib/Support/FileOutputBuffer.cpp | 50 +++++++++++++ 8 files changed, 291 insertions(+), 11 deletions(-) diff --git a/lld/Common/DriverDispatcher.cpp b/lld/Common/DriverDispatcher.cpp index f5c8bcdef4e0..e4b1f423392e 100644 --- a/lld/Common/DriverDispatcher.cpp +++ b/lld/Common/DriverDispatcher.cpp @@ -150,6 +150,22 @@ static Driver whichDriver(llvm::SmallVectorImpl &argsV, return it->d; } +// EVM local begin +static DriverMemBuf whichDriver(llvm::SmallVectorImpl &argsV, + llvm::ArrayRef drivers) { + Flavor f = parseFlavor(argsV); + const auto *const it = + llvm::find_if(drivers, [=](auto &driverdef) { return driverdef.f == f; }); + if (it == drivers.end()) { + // Driver is invalid or not available in this build. + return [](llvm::ArrayRef, llvm::raw_pwrite_stream *, + llvm::ArrayRef, llvm::raw_ostream &, + llvm::raw_ostream &, bool, bool) { return false; }; + } + return it->d; +} +// EVM local end + namespace lld { bool inTestOutputDisabled = false; @@ -174,6 +190,35 @@ int unsafeLldMain(llvm::ArrayRef args, return r; } + +// EVM local begin +/// Universal linker main(). This linker emulates the gnu, darwin, or +/// windows linker based on the argv[0] or -flavor option. +int unsafeLldMainMemBuf(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef args, + llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, + llvm::ArrayRef drivers, + bool exitEarly) { + SmallVector argsV(make_range(args.begin(), args.end())); + DriverMemBuf d = whichDriver(argsV, drivers); + // Run the driver. If an error occurs, false will be returned. + const int r = !d(inBuffers, outBuffer, argsV, stdoutOS, stderrOS, exitEarly, + inTestOutputDisabled); + // At this point 'r' is either 1 for error, and 0 for no error. + + // Call exit() if we can to avoid calling destructors. + if (exitEarly) + exitLld(r); + + // Delete the global context and clear the global context pointer, so that it + // cannot be accessed anymore. + CommonLinkerContext::destroy(); + + return r; +} +// EVM local end } // namespace lld Result lld::lldMain(llvm::ArrayRef args, @@ -201,3 +246,34 @@ Result lld::lldMain(llvm::ArrayRef args, } return {r, /*canRunAgain=*/true}; } + +// EVM local begin +Result lld::lldMainMemBuf(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef args, + llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, + llvm::ArrayRef drivers) { + int r = 0; + { + // The crash recovery is here only to be able to recover from arbitrary + // control flow when fatal() is called (through setjmp/longjmp or + // __try/__except). + llvm::CrashRecoveryContext crc; + if (!crc.RunSafely([&]() { + r = unsafeLldMainMemBuf(inBuffers, outBuffer, args, stdoutOS, + stderrOS, drivers, /*exitEarly=*/false); + })) + return {crc.RetCode, /*canRunAgain=*/false}; + } + + // Cleanup memory and reset everything back in pristine condition. This path + // is only taken when LLD is in test, or when it is used as a library. + llvm::CrashRecoveryContext crc; + if (!crc.RunSafely([]() { CommonLinkerContext::destroy(); })) { + // The memory is corrupted beyond any possible recovery. + return {r, /*canRunAgain=*/false}; + } + return {r, /*canRunAgain=*/true}; +} +// EVM local end diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 28726d48e428..ba554f4784bc 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -157,6 +157,11 @@ struct Config { llvm::CachePruningPolicy thinLTOCachePolicy; llvm::SetVector dependencyFiles; // for --dependency-file llvm::StringMap sectionStartMap; + // EVM local begin + llvm::ArrayRef inBuffers; + llvm::raw_pwrite_stream *outBuffer; + bool useIOMemoryBuffers = false; + // EVM local end llvm::StringRef bfdname; llvm::StringRef chroot; llvm::StringRef dependencyFile; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index eb6734dfd458..ec0ac914fcb3 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -164,6 +164,53 @@ bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, return errorCount() == 0; } + +// EVM local begin +bool linkMemBuf(ArrayRef inBuffers, + raw_pwrite_stream *outBuffer, ArrayRef args, + llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, + bool exitEarly, bool disableOutput) { + // This driver-specific context will be freed later by unsafeLldMainMemBuf(). + auto *ctx = new CommonLinkerContext; + + ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); + ctx->e.cleanupCallback = []() { + elf::ctx.reset(); + symtab = SymbolTable(); + + outputSections.clear(); + symAux.clear(); + + tar = nullptr; + in.reset(); + + partitions.clear(); + partitions.emplace_back(); + + SharedFile::vernauxNum = 0; + }; + ctx->e.logName = args::getFilenameWithoutExe(args[0]); + ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " + "--error-limit=0 to see all errors)"; + + config = ConfigWrapper(); + script = ScriptWrapper(); + + symAux.emplace_back(); + + partitions.clear(); + partitions.emplace_back(); + + config->progName = args[0]; + config->inBuffers = inBuffers; + config->outBuffer = outBuffer; + config->useIOMemoryBuffers = true; + + elf::ctx.driver.linkerMain(args); + + return errorCount() == 0; +} +// EVM local end } // namespace elf } // namespace lld @@ -1868,13 +1915,19 @@ static void setConfigs(opt::InputArgList &args) { (!config->entry.empty() || (!config->shared && !config->relocatable)); if (config->entry.empty() && !config->relocatable) config->entry = config->emachine == EM_MIPS ? "__start" : "_start"; - if (config->outputFile.empty()) + if (!config->useIOMemoryBuffers && config->outputFile.empty()) // EVM local config->outputFile = "a.out"; + // EVM local begin + if (config->useIOMemoryBuffers && !config->outputFile.empty()) + error("specification of an out file name has no effect, as the memory " + "buffer will be used instead"); + // EVM local end + // Fail early if the output file or map file is not writable. If a user has a // long link, e.g. due to a large LTO link, they do not wish to run it and // find that it failed because there was a mistake in their command-line. - { + if (!config->useIOMemoryBuffers) { // EVM local llvm::TimeTraceScope timeScope("Create output files"); if (auto e = tryCreateFile(config->outputFile)) error("cannot open output file " + config->outputFile + ": " + @@ -1931,7 +1984,14 @@ void LinkerDriver::createFiles(opt::InputArgList &args) { } case OPT_script: case OPT_default_script: - if (std::optional path = searchScript(arg->getValue())) { + // EVM local begin + if (config->useIOMemoryBuffers) { + if (std::optional mb = readFile(arg->getValue())) + readLinkerScript(*mb); + break; + // EVM local end + } else if (std::optional path = + searchScript(arg->getValue())) { if (std::optional mb = readFile(*path)) { if (arg->getOption().matches(OPT_default_script)) { defaultScript = mb; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 48f5a9609ecf..67b6fb3dc549 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -249,15 +249,31 @@ std::optional elf::readFile(StringRef path) { log(path); config->dependencyFiles.insert(llvm::CachedHashString(path)); - auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false, - /*RequiresNullTerminator=*/false); - if (auto ec = mbOrErr.getError()) { - error("cannot open " + path + ": " + ec.message()); - return std::nullopt; + // EVM local begin + MemoryBufferRef mbref; + if (config->useIOMemoryBuffers) { + unsigned idx = 0; + if (path.getAsInteger(10, idx)) { + error(path + ": path should be an index"); + return std::nullopt; + } + if (idx >= config->inBuffers.size()) { + error("memory buffer index is out of range"); + return std::nullopt; + } + mbref = config->inBuffers[idx]; + // Don't take MB ownership, because it's owned externally. + } else { + auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (auto ec = mbOrErr.getError()) { + error("cannot open " + path + ": " + ec.message()); + return std::nullopt; + } + mbref = (*mbOrErr)->getMemBufferRef(); + ctx.memoryBuffers.push_back(std::move(*mbOrErr)); // take MB ownership } - - MemoryBufferRef mbref = (*mbOrErr)->getMemBufferRef(); - ctx.memoryBuffers.push_back(std::move(*mbOrErr)); // take MB ownership + // EVM local end if (tar) tar->append(relativeToRoot(path), mbref.getBuffer()); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 8e3a746a08eb..7ab0aa19087a 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2756,6 +2756,28 @@ template void Writer::openFile() { return; } + // EVM local begin + if (config->useIOMemoryBuffers) { + // Create a special buffer that outputs its content to the + // raw memory stream (config->outBuffer) instead of a file. + // The ugly point in this design is that the buffer type is derived + // from FileOutputBuffer, but the file management functionality + // is not used. Probably this is fine, as otherwise we will need + // much more changes in the lld code. + Expected> bufferOrErr = + FileOutputBuffer::create(fileSize, *config->outBuffer); + + if (!bufferOrErr) { + error("failed to create memory buffer for the result: " + + llvm::toString(bufferOrErr.takeError())); + return; + } + + buffer = std::move(*bufferOrErr); + Out::bufferStart = buffer->getBufferStart(); + return; + } + // EVM local end unlinkAsync(config->outputFile); unsigned flags = 0; if (!config->relocatable) diff --git a/lld/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h index 8520e6e7e257..35792ad4cbe7 100644 --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -10,6 +10,7 @@ #define LLD_COMMON_DRIVER_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/raw_ostream.h" namespace lld { @@ -25,11 +26,24 @@ enum Flavor { using Driver = bool (*)(llvm::ArrayRef, llvm::raw_ostream &, llvm::raw_ostream &, bool, bool); +// EVM local begin +using DriverMemBuf = bool (*)(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef, llvm::raw_ostream &, + llvm::raw_ostream &, bool, bool); +// EVM local end + struct DriverDef { Flavor f; Driver d; }; +// EVM local begin +struct DriverDefMemBuf { + Flavor f; + DriverMemBuf d; +}; +// EVM local begin struct Result { int retCode; bool canRunAgain; @@ -43,6 +57,13 @@ struct Result { // by cleanup. Result lldMain(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, llvm::ArrayRef drivers); +// EVM local begin +Result lldMainMemBuf(llvm::ArrayRef inBuffers, + llvm::raw_pwrite_stream *outBuffer, + llvm::ArrayRef args, + llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, + llvm::ArrayRef drivers); +// EVM local end } // namespace lld // With this macro, library users must specify which drivers they use, provide @@ -56,6 +77,19 @@ Result lldMain(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, } \ } +// EVM local begin +#define LLD_HAS_DRIVER_MEM_BUF(name) \ + namespace lld { \ + namespace name { \ + bool linkMemBuf(llvm::ArrayRef inBuffers, \ + llvm::raw_pwrite_stream *outBuffer, \ + llvm::ArrayRef args, \ + llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, \ + bool exitEarly, bool disableOutput); \ + } \ + } +// EVM local end + // An array which declares that all LLD drivers are linked in your executable. // Must be used along with LLD_HAS_DRIVERS. See examples in LLD unittests. #define LLD_ALL_DRIVERS \ @@ -66,4 +100,10 @@ Result lldMain(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, } \ } +// EVM local begin +#define LLD_ALL_DRIVERS_MEM_BUF \ + { \ + { lld::Gnu, &lld::elf::linkMemBuf } \ + } +// EVM local end #endif diff --git a/llvm/include/llvm/Support/FileOutputBuffer.h b/llvm/include/llvm/Support/FileOutputBuffer.h index d4b73522115d..02d2919ccde2 100644 --- a/llvm/include/llvm/Support/FileOutputBuffer.h +++ b/llvm/include/llvm/Support/FileOutputBuffer.h @@ -48,6 +48,17 @@ class FileOutputBuffer { static Expected> create(StringRef FilePath, size_t Size, unsigned Flags = 0); + // EVM local begin + /// Factory method to create an OutputBuffer object which manages a read/write + /// buffer of the specified size. When committed, the buffer will be written + /// to the stream p\ Out. + /// Please note, even though the returned buffer type is derived from + /// FileOutputBuffer, the functionality related to file management + /// is not used and completely ignored. + static Expected> + create(size_t Size, raw_pwrite_stream &Out); + // EVM local end + /// Returns a pointer to the start of the buffer. virtual uint8_t *getBufferStart() const = 0; diff --git a/llvm/lib/Support/FileOutputBuffer.cpp b/llvm/lib/Support/FileOutputBuffer.cpp index 58a06a34e8cf..6611af22ee56 100644 --- a/llvm/lib/Support/FileOutputBuffer.cpp +++ b/llvm/lib/Support/FileOutputBuffer.cpp @@ -112,6 +112,43 @@ class InMemoryBuffer : public FileOutputBuffer { size_t BufferSize; unsigned Mode; }; + +// EVM local begin +// A FileOutputBuffer which keeps data in memory and writes to the final +// output stream on commit(). +// Note, the file management functionality, derived from FileOutputBuffer, +// is not supposed to be used. +class InMemoryStreamBuffer : public FileOutputBuffer { +public: + InMemoryStreamBuffer(MemoryBlock Buf, std::size_t BufSize, + raw_pwrite_stream &Out) + : FileOutputBuffer("<<"), Buffer(Buf), BufferSize(BufSize), + OutStream(Out) {} + + uint8_t *getBufferStart() const override { + return static_cast(Buffer.base()); + } + + uint8_t *getBufferEnd() const override { + return static_cast(Buffer.base()) + BufferSize; + } + + size_t getBufferSize() const override { return BufferSize; } + + Error commit() override { + OutStream << StringRef(static_cast(Buffer.base()), + BufferSize); + OutStream.flush(); + return Error::success(); + } + +private: + // Buffer may actually contain a larger memory block than BufferSize + OwningMemoryBlock Buffer; + size_t BufferSize; + raw_pwrite_stream &OutStream; +}; +// EVM local end } // namespace static Expected> @@ -154,6 +191,19 @@ createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) { std::move(MappedFile)); } +// EVM local begin +// Create an instance of InMemoryStreamBuffer. +Expected> +FileOutputBuffer::create(size_t Size, raw_pwrite_stream &Out) { + std::error_code EC; + MemoryBlock MB = Memory::allocateMappedMemory( + Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); + if (EC) + return errorCodeToError(EC); + return std::make_unique(MB, Size, Out); +} +// EVM local end + // Create an instance of FileOutputBuffer. Expected> FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) { From 189d8b0071499682996079b4a780734012db346d Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 8 Feb 2024 14:05:06 +0100 Subject: [PATCH 066/203] [LLD] Add EVM architecture to LLD --- lld/ELF/Arch/EVM.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++ lld/ELF/CMakeLists.txt | 1 + lld/ELF/Driver.cpp | 3 ++ lld/ELF/InputFiles.cpp | 4 ++ lld/ELF/Target.cpp | 4 ++ lld/ELF/Target.h | 3 ++ 6 files changed, 109 insertions(+) create mode 100644 lld/ELF/Arch/EVM.cpp 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 83d816ddb060..764cac75a78a 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/Hexagon.cpp Arch/LoongArch.cpp Arch/Mips.cpp diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index ec0ac914fcb3..472ee8c938b3 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -53,6 +53,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/Object/IRObjectFile.h" #include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 67b6fb3dc549..14d2ddc7344f 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1694,6 +1694,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 3e221646ce24..66ef4e0f97c1 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -91,6 +91,10 @@ TargetInfo *elf::getTarget() { return getSystemZTargetInfo(); case EM_X86_64: return getX86_64TargetInfo(); + // EVM local begin + case EM_EVM: + return getEVMTargetInfo(); + // EVM local end default: fatal("unsupported e_machine value: " + Twine(config->emachine)); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 0cefa3181356..ac5bbc0d67f5 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -191,6 +191,9 @@ TargetInfo *getSPARCV9TargetInfo(); TargetInfo *getSystemZTargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); +// EVM local begin +TargetInfo *getEVMTargetInfo(); +// EVM local end template TargetInfo *getMipsTargetInfo(); struct ErrorPlace { From 603ff7e093def487773fe14cf465b6c78f4e76d2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Mon, 10 Mar 2025 22:12:16 +0200 Subject: [PATCH 067/203] [EVM][LLD][Test] Exclude COFF, MachO, wasm and MinGW tests from testing --- lld/test/lit.cfg.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py index d309c2ad4ee2..177ceb6f58c4 100644 --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -171,3 +171,11 @@ # ELF tests expect the default target for ld.lld to be ELF. if config.ld_lld_default_mingw: config.excludes.append("ELF") + +# EVM local begin +if config.target_triple.startswith("evm"): + config.excludes.append("wasm") + config.excludes.append("MachO") + config.excludes.append("COFF") + config.excludes.append("MinGW") +# EVM local end From 9a295a3d30ed49f2bbe57b2d8891e31f0abd9500 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 25 Sep 2024 16:19:56 +0200 Subject: [PATCH 068/203] [EVM] Add LLVMLinkEVM C-API Please, note this is a temporary patch. It adds initial support of dependencies, but it doesn't work in a general case. A full solution will be more sophisticated and will likely be implemented on the FE driver side without a need of the LLD usage. --- lld/CMakeLists.txt | 1 + lld/include/lld-c/LLDAsLibraryC.h | 54 +++++ lld/lld-c/CMakeLists.txt | 16 ++ lld/lld-c/LLDAsLibraryC.cpp | 225 ++++++++++++++++++ llvm/include/llvm/BinaryFormat/ELF.h | 2 +- llvm/lib/Object/ELF.cpp | 4 - .../EVM/MCTargetDesc/EVMTargetStreamer.cpp | 10 +- .../EVM/MCTargetDesc/EVMTargetStreamer.h | 3 +- 8 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 lld/include/lld-c/LLDAsLibraryC.h create mode 100644 lld/lld-c/CMakeLists.txt create mode 100644 lld/lld-c/LLDAsLibraryC.cpp diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt index 64c9f2380550..97add9ffecdd 100644 --- a/lld/CMakeLists.txt +++ b/lld/CMakeLists.txt @@ -188,6 +188,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ) endif() +add_subdirectory(lld-c) add_subdirectory(Common) add_subdirectory(tools/lld) diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h new file mode 100644 index 000000000000..106f59eef8b9 --- /dev/null +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -0,0 +1,54 @@ +//==--------- LLDAsLibraryC.h - LLD Public C Interface --------*- C++ -*----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header declares the following EVM C interfaces: +// - 'LLVMLinkEVM' +// The interface to the LLD linker functionality (via lld-as-a-library) +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_C_LLDASLIBRARYC_H +#define LLD_C_LLDASLIBRARYC_H + +#include "llvm-c/ExternC.h" +#include "llvm-c/Types.h" + +LLVM_C_EXTERN_C_BEGIN + +// Currently, the size of a linker symbol is limited to 20 bytes, as its the +// only usage is to represent Ethereum addresses which are of 160 bit width. +#define LINKER_SYMBOL_SIZE 20 + +/** 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/CMakeLists.txt b/lld/lld-c/CMakeLists.txt new file mode 100644 index 000000000000..4d66f3c989c1 --- /dev/null +++ b/lld/lld-c/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lld_library(lldC + LLDAsLibraryC.cpp + + ${LLD_INCLUDE_DIR}/lld/Common + + LINK_COMPONENTS + Core + Support + + LINK_LIBS + lldCommon + lldELF + + DEPENDS + intrinsics_gen + ) diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp new file mode 100644 index 000000000000..484b43b88659 --- /dev/null +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -0,0 +1,225 @@ +#include "lld-c/LLDAsLibraryC.h" +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm-c/Core.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace object; + +LLD_HAS_DRIVER_MEM_BUF(elf) + +static std::mutex lldMutex; + +/// 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 1769a4427462..6d8073f7860f 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -432,7 +432,7 @@ enum { // EVM local begin // ELF Relocation types for EVM -enum { +enum : uint8_t { #include "ELFRelocs/EVM.def" }; // EVM local end diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index c5c16850bd55..226e45f1160d 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -242,10 +242,6 @@ uint32_t llvm::object::getELFRelativeRelocationType(uint32_t Machine) { break; case ELF::EM_LOONGARCH: return ELF::R_LARCH_RELATIVE; - // EVM local begin - case ELF::EM_EVM: - break; - // EVM local end default: break; } 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 From 981ff32b6aa00fb42638efefa476139cdc19d899 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Mon, 30 Sep 2024 16:47:00 +0200 Subject: [PATCH 069/203] [EVM] Do stackification of instructions in the EVMStackify pass to have valid MIR This change moves the place where normal instructions are replaced with their 'stack' counterparts from the EVMMCInstLower to the EVMStackify pass. This ensures we have a valid MIR after each pass. This change also contains some updates in how labels are represented and resolved in the BE. --- llvm/lib/Target/EVM/CMakeLists.txt | 1 - llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 31 +++++++- llvm/lib/Target/EVM/EVMISelLowering.cpp | 2 +- llvm/lib/Target/EVM/EVMInstrInfo.cpp | 7 ++ llvm/lib/Target/EVM/EVMInstrInfo.td | 10 +-- llvm/lib/Target/EVM/EVMMCInstLower.cpp | 38 ++++------ .../lib/Target/EVM/EVMMachineFunctionInfo.cpp | 27 +++++-- llvm/lib/Target/EVM/EVMMachineFunctionInfo.h | 40 ++++++++-- llvm/lib/Target/EVM/EVMRegColoring.cpp | 2 +- .../lib/Target/EVM/EVMSingleUseExpression.cpp | 11 +-- llvm/lib/Target/EVM/EVMStackify.cpp | 75 ++++++++++++++++--- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 23 +++++- llvm/lib/Target/EVM/EVMTargetMachine.h | 8 ++ llvm/lib/Target/EVM/EVMTargetObjectFile.cpp | 20 ----- llvm/lib/Target/EVM/EVMTargetObjectFile.h | 4 +- .../Target/EVM/MCTargetDesc/EVMAsmBackend.cpp | 34 ++------- .../Target/EVM/MCTargetDesc/EVMFixupKinds.h | 6 +- .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 8 -- llvm/test/CodeGen/EVM/fallthrough.mir | 45 +++++++++++ llvm/test/CodeGen/Generic/undef-phi.ll | 1 - 20 files changed, 271 insertions(+), 122 deletions(-) delete mode 100644 llvm/lib/Target/EVM/EVMTargetObjectFile.cpp create mode 100644 llvm/test/CodeGen/EVM/fallthrough.mir diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index df537c407c3c..0d0158d8793e 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -55,7 +55,6 @@ add_llvm_target(EVMCodeGen EVMStackify.cpp EVMSubtarget.cpp EVMTargetMachine.cpp - EVMTargetObjectFile.cpp EVMTargetTransformInfo.cpp LINK_COMPONENTS 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 f4f65d50ed28..6c8738d2d52f 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -387,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/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 0ece41690a4b..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; } @@ -1122,7 +1122,7 @@ def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32 $imm", } // Pseudo instructions for linkage -let isCodeGenOnly = 1 in -defm DATA - : I<(outs GPR:$dst), (ins jmptarget:$reloc), [], "", - "", 0, 0>; +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 ceea05b7f723..297385478bb7 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -27,33 +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); - } - } - - if (RegOpcode == EVM::DATA) + // 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 * @@ -136,7 +126,7 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { case MachineOperand::MO_MCSymbol: { MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_None; unsigned Opc = MI->getOpcode(); - if (Opc == EVM::DATA) + if (Opc == EVM::DATA_S) Kind = MCSymbolRefExpr::VariantKind::VK_EVM_DATA; MCOp = MCOperand::createExpr( @@ -156,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 752bb18b609b..59bfcf0c9213 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().getLIS(); const MachineBlockFrequencyInfo *MBFI = &getAnalysis().getMBFI(); - 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 32584fd4caee..44d74fc01f48 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -269,7 +269,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(); @@ -391,7 +392,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()); @@ -431,8 +432,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()); @@ -594,7 +595,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().getDomTree(); diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp index cece363bdcb2..9d4b0a16c504 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 f38e511c064d..60e62e0b06d3 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -17,6 +17,8 @@ #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/TargetPassConfig.h" #include "llvm/InitializePasses.h" @@ -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 9b962f59742f..8f01067f8ef3 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.cpp b/llvm/lib/Target/EVM/EVMTargetObjectFile.cpp deleted file mode 100644 index 1da524fbe949..000000000000 --- a/llvm/lib/Target/EVM/EVMTargetObjectFile.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===------------- EVMTargetObjectFile.cpp - EVM Object Info --------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the functions of the EVM-specific subclass -// of TargetLoweringObjectFile. -// -//===----------------------------------------------------------------------===// - -#include "EVMTargetObjectFile.h" - -using namespace llvm; - -// Code sections need to be aligned on 1, otherwise linker will add padding -// between .text sections of the object files being linked. -unsigned EVMELFTargetObjectFile::getTextSectionAlignment() const { return 1; } diff --git a/llvm/lib/Target/EVM/EVMTargetObjectFile.h b/llvm/lib/Target/EVM/EVMTargetObjectFile.h index 7de553b2480e..1afa2bb7fb41 100644 --- a/llvm/lib/Target/EVM/EVMTargetObjectFile.h +++ b/llvm/lib/Target/EVM/EVMTargetObjectFile.h @@ -22,7 +22,9 @@ class EVMELFTargetObjectFile final : public TargetLoweringObjectFileELF { public: EVMELFTargetObjectFile() = default; - unsigned getTextSectionAlignment() const override; + // 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 diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp index 433e446ff1d1..adb641928711 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; @@ -99,10 +102,6 @@ const MCFixupKindInfo &EVMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { // in EVMFixupKinds.h. // // Name Offset (bits) Size (bits) Flags - {"fixup_SecRel_i64", 0, 8 * 8, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i56", 0, 8 * 7, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i48", 0, 8 * 6, MCFixupKindInfo::FKF_IsTarget}, - {"fixup_SecRel_i40", 0, 8 * 5, 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}, @@ -135,6 +134,9 @@ bool EVMAsmBackend::evaluateTargetFixup(const MCAssembler &Asm, 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()); @@ -179,18 +181,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; @@ -228,14 +218,6 @@ bool EVMAsmBackend::fixupNeedsRelaxationAdvanced(const MCAssembler &Asm, 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: @@ -250,10 +232,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/EVMFixupKinds.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h index 8d63558bd5ef..ed5ea473e603 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMFixupKinds.h @@ -14,11 +14,7 @@ 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 diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp index 76ddedd8539e..6d0d503d3d8a 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -54,14 +54,6 @@ 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: switch (Kind) { default: 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 dcdb2abebe81..480b01fbf544 100644 --- a/llvm/test/CodeGen/Generic/undef-phi.ll +++ b/llvm/test/CodeGen/Generic/undef-phi.ll @@ -1,7 +1,6 @@ ; XFAIL: target=evm{{.*}} ; TODO: CPR-922 Fix ; 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 From 6334b50e3fe6febfbfbcc4af127adebfd68bd78a Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Fri, 13 Sep 2024 10:04:33 -0700 Subject: [PATCH 070/203] [CodeGen] Use DenseMap::operator[] (NFC) (#108489) Once we modernize CopyInfo with default member initializations, Copies.insert({Unit, ...}) becomes equivalent to: Copies.try_emplace(Unit) which we can simplify further down to Copies[Unit]. --- llvm/lib/CodeGen/MachineCopyPropagation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/lib/CodeGen/MachineCopyPropagation.cpp b/llvm/lib/CodeGen/MachineCopyPropagation.cpp index b34e0939d1c7..0c38d127aa5e 100644 --- a/llvm/lib/CodeGen/MachineCopyPropagation.cpp +++ b/llvm/lib/CodeGen/MachineCopyPropagation.cpp @@ -108,9 +108,10 @@ static std::optional isCopyInstr(const MachineInstr &MI, class CopyTracker { struct CopyInfo { - MachineInstr *MI, *LastSeenUseInCopy; + MachineInstr *MI = nullptr; + MachineInstr *LastSeenUseInCopy = nullptr; SmallVector DefRegs; - bool Avail; + bool Avail = false; }; DenseMap Copies; @@ -240,8 +241,7 @@ class CopyTracker { // Remember source that's copied to Def. Once it's clobbered, then // it's no longer available for copy propagation. for (MCRegUnit Unit : TRI.regunits(Src)) { - auto I = Copies.insert({Unit, {nullptr, nullptr, {}, false}}); - auto &Copy = I.first->second; + auto &Copy = Copies[Unit]; if (!is_contained(Copy.DefRegs, Def)) Copy.DefRegs.push_back(Def); Copy.LastSeenUseInCopy = MI; From 6966a77eaa285a69443b095370fdf7bc41740d2b Mon Sep 17 00:00:00 2001 From: Piyou Chen Date: Wed, 28 Aug 2024 08:32:54 +0800 Subject: [PATCH 071/203] [RISCV][MCP] Remove redundant move from tail duplication (#89865) Tail duplication will generate the redundant move before return. It is because the MachineCopyPropogation can't recognize COPY after post-RA pseudoExpand. This patch make MachineCopyPropogation recognize `%0 = ADDI %1, 0` as COPY --- llvm/lib/CodeGen/MachineCopyPropagation.cpp | 2 +- .../redundant-copy-from-tail-duplicate.ll | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll diff --git a/llvm/lib/CodeGen/MachineCopyPropagation.cpp b/llvm/lib/CodeGen/MachineCopyPropagation.cpp index 0c38d127aa5e..8bcc437cbfb8 100644 --- a/llvm/lib/CodeGen/MachineCopyPropagation.cpp +++ b/llvm/lib/CodeGen/MachineCopyPropagation.cpp @@ -1053,7 +1053,7 @@ void MachineCopyPropagation::BackwardCopyPropagateBlock( // Ignore non-trivial COPYs. std::optional CopyOperands = isCopyInstr(MI, *TII, UseCopyInstr); - if (CopyOperands && MI.getNumOperands() == 2) { + if (CopyOperands) { Register DefReg = CopyOperands->Destination->getReg(); Register SrcReg = CopyOperands->Source->getReg(); diff --git a/llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll b/llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll new file mode 100644 index 000000000000..3d367ddc59bc --- /dev/null +++ b/llvm/test/CodeGen/RISCV/redundant-copy-from-tail-duplicate.ll @@ -0,0 +1,50 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 +; RUN: llc < %s -mtriple=riscv64 -mattr=+v | FileCheck %s + + +define signext i32 @sum(ptr %a, i32 signext %n, i1 %prof.min.iters.check, %0, %1) { +; CHECK-LABEL: sum: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: andi a2, a2, 1 +; CHECK-NEXT: beqz a2, .LBB0_4 +; CHECK-NEXT: # %bb.1: # %for.body.preheader +; CHECK-NEXT: li a3, 0 +; CHECK-NEXT: .LBB0_2: # %for.body +; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: mv a2, a3 +; CHECK-NEXT: lw a3, 0(a0) +; CHECK-NEXT: addi a0, a0, 4 +; CHECK-NEXT: bnez a1, .LBB0_2 +; CHECK-NEXT: # %bb.3: # %for.end +; CHECK-NEXT: mv a0, a2 +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB0_4: # %vector.ph +; CHECK-NEXT: vsetvli a0, zero, e32, m4, ta, ma +; CHECK-NEXT: vmv.s.x v8, zero +; CHECK-NEXT: vmv.v.i v12, 0 +; CHECK-NEXT: vsetivli zero, 1, e32, m4, ta, ma +; CHECK-NEXT: vredsum.vs v8, v12, v8, v0.t +; CHECK-NEXT: vmv.x.s a0, v8 +; CHECK-NEXT: ret +entry: + br i1 %prof.min.iters.check, label %for.body, label %vector.ph + +vector.ph: ; preds = %entry + %2 = tail call i32 @llvm.vp.reduce.add.nxv8i32(i32 0, zeroinitializer, %0, i32 1) + br label %for.end + +for.body: ; preds = %for.body, %entry + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %red.05 = phi i32 [ %3, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr i32, ptr %a, i64 %indvars.iv + %3 = load i32, ptr %arrayidx, align 4 + %indvars.iv.next = add i64 %indvars.iv, 1 + %exitcond.not = icmp eq i32 %n, 0 + br i1 %exitcond.not, label %for.end, label %for.body + +for.end: ; preds = %for.body, %vector.ph + %red.0.lcssa = phi i32 [ %2, %vector.ph ], [ %red.05, %for.body ] + ret i32 %red.0.lcssa +} + +declare i32 @llvm.vp.reduce.add.nxv8i32(i32, , , i32) From 3ee7ab5d25773dee10627c6a0c0a94876c934e7b Mon Sep 17 00:00:00 2001 From: Piyou Chen Date: Tue, 27 Aug 2024 10:08:43 +0800 Subject: [PATCH 072/203] [TII][RISCV] Add renamable bit to copyPhysReg (#91179) The renamable flag is useful during MachineCopyPropagation but renamable flag will be dropped after lowerCopy in some case. This patch introduces extra arguments to pass the renamable flag to copyPhysReg. --- llvm/include/llvm/CodeGen/TargetInstrInfo.h | 10 ++++-- llvm/lib/CodeGen/TargetInstrInfo.cpp | 4 ++- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 4 ++- llvm/lib/Target/AArch64/AArch64InstrInfo.h | 3 +- llvm/lib/Target/AMDGPU/R600InstrInfo.cpp | 3 +- llvm/lib/Target/AMDGPU/R600InstrInfo.h | 3 +- llvm/lib/Target/AMDGPU/SIInstrInfo.cpp | 3 +- llvm/lib/Target/AMDGPU/SIInstrInfo.h | 3 +- llvm/lib/Target/ARC/ARCInstrInfo.cpp | 3 +- llvm/lib/Target/ARC/ARCInstrInfo.h | 3 +- llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp | 4 ++- llvm/lib/Target/ARM/ARMBaseInstrInfo.h | 3 +- llvm/lib/Target/ARM/Thumb1InstrInfo.cpp | 3 +- llvm/lib/Target/ARM/Thumb1InstrInfo.h | 3 +- llvm/lib/Target/ARM/Thumb2InstrInfo.cpp | 3 +- llvm/lib/Target/ARM/Thumb2InstrInfo.h | 3 +- llvm/lib/Target/AVR/AVRInstrInfo.cpp | 3 +- llvm/lib/Target/AVR/AVRInstrInfo.h | 3 +- llvm/lib/Target/BPF/BPFInstrInfo.cpp | 3 +- llvm/lib/Target/BPF/BPFInstrInfo.h | 3 +- llvm/lib/Target/CSKY/CSKYInstrInfo.cpp | 3 +- llvm/lib/Target/CSKY/CSKYInstrInfo.h | 3 +- llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp | 4 ++- llvm/lib/Target/Hexagon/HexagonInstrInfo.h | 3 +- llvm/lib/Target/Lanai/LanaiInstrInfo.cpp | 4 +-- llvm/lib/Target/Lanai/LanaiInstrInfo.h | 4 ++- .../Target/LoongArch/LoongArchInstrInfo.cpp | 4 ++- .../lib/Target/LoongArch/LoongArchInstrInfo.h | 3 +- llvm/lib/Target/M68k/M68kInstrInfo.cpp | 3 +- llvm/lib/Target/M68k/M68kInstrInfo.h | 3 +- llvm/lib/Target/MSP430/MSP430InstrInfo.cpp | 3 +- llvm/lib/Target/MSP430/MSP430InstrInfo.h | 3 +- llvm/lib/Target/Mips/Mips16InstrInfo.cpp | 3 +- llvm/lib/Target/Mips/Mips16InstrInfo.h | 3 +- llvm/lib/Target/Mips/MipsSEInstrInfo.cpp | 3 +- llvm/lib/Target/Mips/MipsSEInstrInfo.h | 3 +- llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp | 3 +- llvm/lib/Target/NVPTX/NVPTXInstrInfo.h | 3 +- llvm/lib/Target/PowerPC/PPCInstrInfo.cpp | 3 +- llvm/lib/Target/PowerPC/PPCInstrInfo.h | 3 +- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 6 ++-- llvm/lib/Target/RISCV/RISCVInstrInfo.h | 3 +- llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp | 3 +- llvm/lib/Target/SPIRV/SPIRVInstrInfo.h | 3 +- llvm/lib/Target/Sparc/SparcInstrInfo.cpp | 3 +- llvm/lib/Target/Sparc/SparcInstrInfo.h | 3 +- llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp | 4 ++- llvm/lib/Target/SystemZ/SystemZInstrInfo.h | 3 +- llvm/lib/Target/VE/VEInstrInfo.cpp | 3 +- llvm/lib/Target/VE/VEInstrInfo.h | 3 +- .../WebAssembly/WebAssemblyInstrInfo.cpp | 4 ++- .../Target/WebAssembly/WebAssemblyInstrInfo.h | 3 +- llvm/lib/Target/X86/X86InstrInfo.cpp | 3 +- llvm/lib/Target/X86/X86InstrInfo.h | 3 +- llvm/lib/Target/XCore/XCoreInstrInfo.cpp | 3 +- llvm/lib/Target/XCore/XCoreInstrInfo.h | 3 +- llvm/test/CodeGen/RISCV/renamable-copy.mir | 31 +++++++++++++++++++ 57 files changed, 159 insertions(+), 59 deletions(-) create mode 100644 llvm/test/CodeGen/RISCV/renamable-copy.mir diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index f1d0aefc31cb..c568f1f9ca06 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -1026,10 +1026,16 @@ class TargetInstrInfo : public MCInstrInfo { /// The source and destination registers may overlap, which may require a /// careful implementation when multiple copy instructions are required for /// large registers. See for example the ARM target. + /// + /// If RenamableDest is true, the copy instruction's destination operand is + /// marked renamable. + /// If RenamableSrc is true, the copy instruction's source operand is + /// marked renamable. virtual void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, - MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const { + MCRegister DestReg, MCRegister SrcReg, bool KillSrc, + bool RenamableDest = false, + bool RenamableSrc = false) const { llvm_unreachable("Target didn't implement TargetInstrInfo::copyPhysReg!"); } diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp index 3cd1bb296d28..54cd760f951d 100644 --- a/llvm/lib/CodeGen/TargetInstrInfo.cpp +++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp @@ -823,7 +823,9 @@ void TargetInstrInfo::lowerCopy(MachineInstr *MI, } copyPhysReg(*MI->getParent(), MI, MI->getDebugLoc(), DstMO.getReg(), - SrcMO.getReg(), SrcMO.isKill()); + SrcMO.getReg(), SrcMO.isKill(), + DstMO.getReg().isPhysical() ? DstMO.isRenamable() : false, + SrcMO.getReg().isPhysical() ? SrcMO.isRenamable() : false); if (MI->getNumOperands() > 2) transferImplicitOperands(MI, TRI); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 805684ef69a5..243fcffb43eb 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -4451,7 +4451,9 @@ void AArch64InstrInfo::copyGPRRegTuple(MachineBasicBlock &MBB, void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { if (AArch64::GPR32spRegClass.contains(DestReg) && (AArch64::GPR32spRegClass.contains(SrcReg) || SrcReg == AArch64::WZR)) { const TargetRegisterInfo *TRI = &getRegisterInfo(); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h index 69ee0a70765e..f217f2d32c4a 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -338,7 +338,8 @@ class AArch64InstrInfo final : public AArch64GenInstrInfo { llvm::ArrayRef Indices) const; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp b/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp index a3159944a2ad..f6ca15eefd70 100644 --- a/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp +++ b/llvm/lib/Target/AMDGPU/R600InstrInfo.cpp @@ -40,7 +40,8 @@ bool R600InstrInfo::isVector(const MachineInstr &MI) const { void R600InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned VectorComponents = 0; if ((R600::R600_Reg128RegClass.contains(DestReg) || R600::R600_Reg128VerticalRegClass.contains(DestReg)) && diff --git a/llvm/lib/Target/AMDGPU/R600InstrInfo.h b/llvm/lib/Target/AMDGPU/R600InstrInfo.h index f720e4656348..c767ecb24590 100644 --- a/llvm/lib/Target/AMDGPU/R600InstrInfo.h +++ b/llvm/lib/Target/AMDGPU/R600InstrInfo.h @@ -73,7 +73,8 @@ class R600InstrInfo final : public R600GenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool isLegalToSplitMBBAt(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const override; diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp index d781354bce16..ad2efd7e108d 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.cpp @@ -794,7 +794,8 @@ static void expandSGPRCopy(const SIInstrInfo &TII, MachineBasicBlock &MBB, void SIInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const TargetRegisterClass *RC = RI.getPhysRegBaseClass(DestReg); unsigned Size = RI.getRegSizeInBits(*RC); const TargetRegisterClass *SrcRC = RI.getPhysRegBaseClass(SrcReg); diff --git a/llvm/lib/Target/AMDGPU/SIInstrInfo.h b/llvm/lib/Target/AMDGPU/SIInstrInfo.h index 91855fb14f6f..d8eb38b2ea3a 100644 --- a/llvm/lib/Target/AMDGPU/SIInstrInfo.h +++ b/llvm/lib/Target/AMDGPU/SIInstrInfo.h @@ -255,7 +255,8 @@ class SIInstrInfo final : public AMDGPUGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void materializeImmediate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, diff --git a/llvm/lib/Target/ARC/ARCInstrInfo.cpp b/llvm/lib/Target/ARC/ARCInstrInfo.cpp index 9b5e45cb5fe9..78db68fca305 100644 --- a/llvm/lib/Target/ARC/ARCInstrInfo.cpp +++ b/llvm/lib/Target/ARC/ARCInstrInfo.cpp @@ -281,7 +281,8 @@ unsigned ARCInstrInfo::removeBranch(MachineBasicBlock &MBB, void ARCInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { assert(ARC::GPR32RegClass.contains(SrcReg) && "Only GPR32 src copy supported."); assert(ARC::GPR32RegClass.contains(DestReg) && diff --git a/llvm/lib/Target/ARC/ARCInstrInfo.h b/llvm/lib/Target/ARC/ARCInstrInfo.h index 1875aafbde82..e25f99025226 100644 --- a/llvm/lib/Target/ARC/ARCInstrInfo.h +++ b/llvm/lib/Target/ARC/ARCInstrInfo.h @@ -65,7 +65,8 @@ class ARCInstrInfo : public ARCGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp index bf6bb8c295ef..0eaf2f167d51 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -892,7 +892,9 @@ void llvm::addPredicatedMveVpredROp(MachineInstrBuilder &MIB, void ARMBaseInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { bool GPRDest = ARM::GPRRegClass.contains(DestReg); bool GPRSrc = ARM::GPRRegClass.contains(SrcReg); diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h index a3c2684ac1fb..2ae00dc18c4f 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h @@ -209,7 +209,8 @@ class ARMBaseInstrInfo : public ARMGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp index 396328e958d1..a38aa3de40d9 100644 --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp @@ -42,7 +42,8 @@ unsigned Thumb1InstrInfo::getUnindexedOpcode(unsigned Opc) const { void Thumb1InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // Need to check the arch. MachineFunction &MF = *MBB.getParent(); const ARMSubtarget &st = MF.getSubtarget(); diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.h b/llvm/lib/Target/ARM/Thumb1InstrInfo.h index 984bec4e6449..84241fb8a9a6 100644 --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.h +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.h @@ -39,7 +39,8 @@ class Thumb1InstrInfo : public ARMBaseInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp index 09bcd3109f2b..d1e07b6703a5 100644 --- a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp @@ -151,7 +151,8 @@ Thumb2InstrInfo::optimizeSelect(MachineInstr &MI, void Thumb2InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // Handle SPR, DPR, and QPR copies. if (!ARM::GPRRegClass.contains(DestReg, SrcReg)) return ARMBaseInstrInfo::copyPhysReg(MBB, I, DL, DestReg, SrcReg, KillSrc); diff --git a/llvm/lib/Target/ARM/Thumb2InstrInfo.h b/llvm/lib/Target/ARM/Thumb2InstrInfo.h index 8915da8c5bf3..70ee3270e64a 100644 --- a/llvm/lib/Target/ARM/Thumb2InstrInfo.h +++ b/llvm/lib/Target/ARM/Thumb2InstrInfo.h @@ -39,7 +39,8 @@ class Thumb2InstrInfo : public ARMBaseInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.cpp b/llvm/lib/Target/AVR/AVRInstrInfo.cpp index 18b7365fc5aa..7b0f8d74e77c 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.cpp +++ b/llvm/lib/Target/AVR/AVRInstrInfo.cpp @@ -42,7 +42,8 @@ AVRInstrInfo::AVRInstrInfo(AVRSubtarget &STI) void AVRInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const AVRRegisterInfo &TRI = *STI.getRegisterInfo(); unsigned Opc; diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.h b/llvm/lib/Target/AVR/AVRInstrInfo.h index 28c0e0319d46..8eb4292f2422 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.h +++ b/llvm/lib/Target/AVR/AVRInstrInfo.h @@ -75,7 +75,8 @@ class AVRInstrInfo : public AVRGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/BPF/BPFInstrInfo.cpp b/llvm/lib/Target/BPF/BPFInstrInfo.cpp index 2209f1f1462b..1b07e7ffc0d3 100644 --- a/llvm/lib/Target/BPF/BPFInstrInfo.cpp +++ b/llvm/lib/Target/BPF/BPFInstrInfo.cpp @@ -31,7 +31,8 @@ BPFInstrInfo::BPFInstrInfo() void BPFInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { if (BPF::GPRRegClass.contains(DestReg, SrcReg)) BuildMI(MBB, I, DL, get(BPF::MOV_rr), DestReg) .addReg(SrcReg, getKillRegState(KillSrc)); diff --git a/llvm/lib/Target/BPF/BPFInstrInfo.h b/llvm/lib/Target/BPF/BPFInstrInfo.h index 354aca1bd2f9..a6b6fd7dc4d9 100644 --- a/llvm/lib/Target/BPF/BPFInstrInfo.h +++ b/llvm/lib/Target/BPF/BPFInstrInfo.h @@ -31,7 +31,8 @@ class BPFInstrInfo : public BPFGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool expandPostRAPseudo(MachineInstr &MI) const override; diff --git a/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp b/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp index 6baca84ab3d0..a2bb87bcaaf9 100644 --- a/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp +++ b/llvm/lib/Target/CSKY/CSKYInstrInfo.cpp @@ -478,7 +478,8 @@ void CSKYInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, void CSKYInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { if (CSKY::GPRRegClass.contains(SrcReg) && CSKY::CARRYRegClass.contains(DestReg)) { if (STI.hasE2()) { diff --git a/llvm/lib/Target/CSKY/CSKYInstrInfo.h b/llvm/lib/Target/CSKY/CSKYInstrInfo.h index 4e3866b1188c..54c1106310d8 100644 --- a/llvm/lib/Target/CSKY/CSKYInstrInfo.h +++ b/llvm/lib/Target/CSKY/CSKYInstrInfo.h @@ -55,7 +55,8 @@ class CSKYInstrInfo : public CSKYGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef Cond, diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp index 3f6de365fe39..7c77bf2b31b8 100644 --- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp +++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.cpp @@ -857,7 +857,9 @@ static void getLiveOutRegsAt(LivePhysRegs &Regs, const MachineInstr &MI) { void HexagonInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { const HexagonRegisterInfo &HRI = *Subtarget.getRegisterInfo(); unsigned KillFlag = getKillRegState(KillSrc); diff --git a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h index 4efc62fd717c..854c3694ceba 100644 --- a/llvm/lib/Target/Hexagon/HexagonInstrInfo.h +++ b/llvm/lib/Target/Hexagon/HexagonInstrInfo.h @@ -174,7 +174,8 @@ class HexagonInstrInfo : public HexagonGenInstrInfo { /// large registers. See for example the ARM target. void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; /// Store the specified register of the given register class to the specified /// stack frame index. The store instruction is to be added to the given diff --git a/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp b/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp index b8a37435f5a6..f7ff63bb5784 100644 --- a/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp +++ b/llvm/lib/Target/Lanai/LanaiInstrInfo.cpp @@ -35,8 +35,8 @@ void LanaiInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, const DebugLoc &DL, MCRegister DestinationRegister, - MCRegister SourceRegister, - bool KillSource) const { + MCRegister SourceRegister, bool KillSource, + bool RenamableDest, bool RenamableSrc) const { if (!Lanai::GPRRegClass.contains(DestinationRegister, SourceRegister)) { llvm_unreachable("Impossible reg-to-reg copy"); } diff --git a/llvm/lib/Target/Lanai/LanaiInstrInfo.h b/llvm/lib/Target/Lanai/LanaiInstrInfo.h index 8ad2b9237c92..2630464f0a76 100644 --- a/llvm/lib/Target/Lanai/LanaiInstrInfo.h +++ b/llvm/lib/Target/Lanai/LanaiInstrInfo.h @@ -49,7 +49,9 @@ class LanaiInstrInfo : public LanaiGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, const DebugLoc &DL, MCRegister DestinationRegister, - MCRegister SourceRegister, bool KillSource) const override; + MCRegister SourceRegister, bool KillSource, + bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp index a85b054a85d7..9079b72f70e1 100644 --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp @@ -39,7 +39,9 @@ MCInst LoongArchInstrInfo::getNop() const { void LoongArchInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { if (LoongArch::GPRRegClass.contains(DstReg, SrcReg)) { BuildMI(MBB, MBBI, DL, get(LoongArch::OR), DstReg) .addReg(SrcReg, getKillRegState(KillSrc)) diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h index eb19051e380c..1183fdd56d7d 100644 --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.h @@ -31,7 +31,8 @@ class LoongArchInstrInfo : public LoongArchGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/M68k/M68kInstrInfo.cpp b/llvm/lib/Target/M68k/M68kInstrInfo.cpp index 338db45782c9..23c5c76a4747 100644 --- a/llvm/lib/Target/M68k/M68kInstrInfo.cpp +++ b/llvm/lib/Target/M68k/M68kInstrInfo.cpp @@ -663,7 +663,8 @@ bool M68kInstrInfo::isPCRelRegisterOperandLegal( void M68kInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DstReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc = 0; // First deal with the normal symmetric copies. diff --git a/llvm/lib/Target/M68k/M68kInstrInfo.h b/llvm/lib/Target/M68k/M68kInstrInfo.h index d1e1e1cd9998..5d81956d89fd 100644 --- a/llvm/lib/Target/M68k/M68kInstrInfo.h +++ b/llvm/lib/Target/M68k/M68kInstrInfo.h @@ -271,7 +271,8 @@ class M68kInstrInfo : public M68kGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool getStackSlotRange(const TargetRegisterClass *RC, unsigned SubIdx, unsigned &Size, unsigned &Offset, diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp b/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp index 740571651664..ae1228ceaa4e 100644 --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.cpp @@ -90,7 +90,8 @@ void MSP430InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, void MSP430InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc; if (MSP430::GR16RegClass.contains(DestReg, SrcReg)) Opc = MSP430::MOV16rr; diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.h b/llvm/lib/Target/MSP430/MSP430InstrInfo.h index b8d015a21cd1..113a22318bec 100644 --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.h +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.h @@ -37,7 +37,8 @@ class MSP430InstrInfo : public MSP430GenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, diff --git a/llvm/lib/Target/Mips/Mips16InstrInfo.cpp b/llvm/lib/Target/Mips/Mips16InstrInfo.cpp index 30ac96936de2..1bc1ed7ab93e 100644 --- a/llvm/lib/Target/Mips/Mips16InstrInfo.cpp +++ b/llvm/lib/Target/Mips/Mips16InstrInfo.cpp @@ -69,7 +69,8 @@ Register Mips16InstrInfo::isStoreToStackSlot(const MachineInstr &MI, void Mips16InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc = 0; if (Mips::CPU16RegsRegClass.contains(DestReg) && diff --git a/llvm/lib/Target/Mips/Mips16InstrInfo.h b/llvm/lib/Target/Mips/Mips16InstrInfo.h index e8567ee3b9ce..8e73c8079b0f 100644 --- a/llvm/lib/Target/Mips/Mips16InstrInfo.h +++ b/llvm/lib/Target/Mips/Mips16InstrInfo.h @@ -50,7 +50,8 @@ class Mips16InstrInfo : public MipsInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp index b99ddfab2a47..87e9ef1c2642 100644 --- a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp +++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp @@ -83,7 +83,8 @@ Register MipsSEInstrInfo::isStoreToStackSlot(const MachineInstr &MI, void MipsSEInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned Opc = 0, ZeroReg = 0; bool isMicroMips = Subtarget.inMicroMipsMode(); diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.h b/llvm/lib/Target/Mips/MipsSEInstrInfo.h index a8855e26ad10..36bddba10410 100644 --- a/llvm/lib/Target/Mips/MipsSEInstrInfo.h +++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.h @@ -44,7 +44,8 @@ class MipsSEInstrInfo : public MipsInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp index 673858f92e7c..bec40874c894 100644 --- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp +++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.cpp @@ -32,7 +32,8 @@ NVPTXInstrInfo::NVPTXInstrInfo() : RegInfo() {} void NVPTXInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo(); const TargetRegisterClass *DestRC = MRI.getRegClass(DestReg); const TargetRegisterClass *SrcRC = MRI.getRegClass(SrcReg); diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h index d6cbeae6984c..f674a00bc351 100644 --- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h +++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.h @@ -53,7 +53,8 @@ class NVPTXInstrInfo : public NVPTXGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; // Branch analysis. bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp index 81f16eb1a905..48833e8f8806 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp @@ -1678,7 +1678,8 @@ static unsigned getCRBitValue(unsigned CRBit) { void PPCInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // We can end up with self copies and similar things as a result of VSX copy // legalization. Promote them here. const TargetRegisterInfo *TRI = &getRegisterInfo(); diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.h b/llvm/lib/Target/PowerPC/PPCInstrInfo.h index 1e2687f92c61..40996f6fbb75 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.h +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.h @@ -454,7 +454,8 @@ class PPCInstrInfo : public PPCGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 6c0cbeadebf4..5dcda041f00e 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -440,12 +440,14 @@ void RISCVInstrInfo::copyPhysRegVector( void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { const TargetRegisterInfo *TRI = STI.getRegisterInfo(); if (RISCV::GPRRegClass.contains(DstReg, SrcReg)) { BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) - .addReg(SrcReg, getKillRegState(KillSrc)) + .addReg(SrcReg, + getKillRegState(KillSrc) | getRenamableRegState(RenamableSrc)) .addImm(0); return; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h index 025cc36d19eb..59c47063c8dc 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -84,7 +84,8 @@ class RISCVInstrInfo : public RISCVGenInstrInfo { const TargetRegisterClass *RegClass) const; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DstReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp index 12cf7613a45c..d0a134cfbdf0 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp @@ -241,7 +241,8 @@ unsigned SPIRVInstrInfo::insertBranch( void SPIRVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // Actually we don't need this COPY instruction. However if we do nothing with // it, post RA pseudo instrs expansion just removes it and we get the code // with undef registers. Therefore, we need to replace all uses of dst with diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h index 95f387491357..67d2d979cb5a 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h +++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h @@ -51,7 +51,8 @@ class SPIRVInstrInfo : public SPIRVGenInstrInfo { int *BytesAdded = nullptr) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool expandPostRAPseudo(MachineInstr &MI) const override; }; diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp index 2727a9f2efbb..0bb2540a97d7 100644 --- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp @@ -438,7 +438,8 @@ bool SparcInstrInfo::isBranchOffsetInRange(unsigned BranchOpc, void SparcInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { unsigned numSubRegs = 0; unsigned movOpc = 0; const unsigned *subRegIdx = nullptr; diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.h b/llvm/lib/Target/Sparc/SparcInstrInfo.h index a7bb34c6c8e7..fc04542c819d 100644 --- a/llvm/lib/Target/Sparc/SparcInstrInfo.h +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.h @@ -87,7 +87,8 @@ class SparcInstrInfo : public SparcGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp index 16bbfd44ef8a..f0f083464f54 100644 --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp @@ -858,7 +858,9 @@ bool SystemZInstrInfo::PredicateInstruction( void SystemZInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { // Split 128-bit GPR moves into two 64-bit moves. Add implicit uses of the // super register in case one of the subregs is undefined. // This handles ADDR128 too. diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h index 61338b081615..cc8a4ccd234c 100644 --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h @@ -276,7 +276,8 @@ class SystemZInstrInfo : public SystemZGenInstrInfo { ArrayRef Pred) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/VE/VEInstrInfo.cpp b/llvm/lib/Target/VE/VEInstrInfo.cpp index c001dc4d92b9..fccbed3bdec8 100644 --- a/llvm/lib/Target/VE/VEInstrInfo.cpp +++ b/llvm/lib/Target/VE/VEInstrInfo.cpp @@ -359,7 +359,8 @@ static void copyPhysSubRegs(MachineBasicBlock &MBB, void VEInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const { + bool KillSrc, bool RenamableDest, + bool RenamableSrc) const { if (IsAliasOfSX(SrcReg) && IsAliasOfSX(DestReg)) { BuildMI(MBB, I, DL, get(VE::ORri), DestReg) diff --git a/llvm/lib/Target/VE/VEInstrInfo.h b/llvm/lib/Target/VE/VEInstrInfo.h index 4fcc479a13d5..3a9718f2f260 100644 --- a/llvm/lib/Target/VE/VEInstrInfo.h +++ b/llvm/lib/Target/VE/VEInstrInfo.h @@ -81,7 +81,8 @@ class VEInstrInfo : public VEGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; /// Stack Spill & Reload { Register isLoadFromStackSlot(const MachineInstr &MI, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp index 32a4accd040e..75011ab3c872 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -57,7 +57,9 @@ bool WebAssemblyInstrInfo::isReallyTriviallyReMaterializable( void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, + bool RenamableSrc) const { // This method is called by post-RA expansion, which expects only pregs to // exist. However we need to handle both here. auto &MRI = MBB.getParent()->getRegInfo(); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h index c1e1a790c60e..8cb692f9bc0c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h @@ -47,7 +47,8 @@ class WebAssemblyInstrInfo final : public WebAssemblyGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; MachineInstr *commuteInstructionImpl(MachineInstr &MI, bool NewMI, unsigned OpIdx1, unsigned OpIdx2) const override; diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp index fab7c167e385..d7aad744083b 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -4238,7 +4238,8 @@ static unsigned CopyToFromAsymmetricReg(unsigned DestReg, unsigned SrcReg, void X86InstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // First deal with the normal symmetric copies. bool HasAVX = Subtarget.hasAVX(); bool HasVLX = Subtarget.hasVLX(); diff --git a/llvm/lib/Target/X86/X86InstrInfo.h b/llvm/lib/Target/X86/X86InstrInfo.h index eaa3dd089394..0573229d4ccf 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.h +++ b/llvm/lib/Target/X86/X86InstrInfo.h @@ -418,7 +418,8 @@ class X86InstrInfo final : public X86GenInstrInfo { Register FalseReg) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, bool isKill, int FrameIndex, diff --git a/llvm/lib/Target/XCore/XCoreInstrInfo.cpp b/llvm/lib/Target/XCore/XCoreInstrInfo.cpp index ae2e0fec3f89..90a195e928a5 100644 --- a/llvm/lib/Target/XCore/XCoreInstrInfo.cpp +++ b/llvm/lib/Target/XCore/XCoreInstrInfo.cpp @@ -331,7 +331,8 @@ XCoreInstrInfo::removeBranch(MachineBasicBlock &MBB, int *BytesRemoved) const { void XCoreInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { bool GRDest = XCore::GRRegsRegClass.contains(DestReg); bool GRSrc = XCore::GRRegsRegClass.contains(SrcReg); diff --git a/llvm/lib/Target/XCore/XCoreInstrInfo.h b/llvm/lib/Target/XCore/XCoreInstrInfo.h index 1dafb6ea7d21..7f330539dd76 100644 --- a/llvm/lib/Target/XCore/XCoreInstrInfo.h +++ b/llvm/lib/Target/XCore/XCoreInstrInfo.h @@ -64,7 +64,8 @@ class XCoreInstrInfo : public XCoreGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg, diff --git a/llvm/test/CodeGen/RISCV/renamable-copy.mir b/llvm/test/CodeGen/RISCV/renamable-copy.mir new file mode 100644 index 000000000000..06f17f4edbcc --- /dev/null +++ b/llvm/test/CodeGen/RISCV/renamable-copy.mir @@ -0,0 +1,31 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -o - %s -mtriple=riscv32 -simplify-mir \ +# RUN: -run-pass=postrapseudos | FileCheck --check-prefix=RV32 %s +# RUN: llc -o - %s -mtriple=riscv64 -simplify-mir \ +# RUN: -run-pass=postrapseudos | FileCheck --check-prefix=RV64 %s + +--- | + define void @foo() { + entry: + ret void + } +... +--- +name: foo +body: | + bb.0.entry: + liveins: $x11 + ; RV32-LABEL: name: foo + ; RV32: liveins: $x11 + ; RV32-NEXT: {{ $}} + ; RV32-NEXT: $x10 = ADDI renamable $x11, 0 + ; RV32-NEXT: PseudoRET implicit $x10 + ; + ; RV64-LABEL: name: foo + ; RV64: liveins: $x11 + ; RV64-NEXT: {{ $}} + ; RV64-NEXT: $x10 = ADDI renamable $x11, 0 + ; RV64-NEXT: PseudoRET implicit $x10 + renamable $x10 = COPY renamable $x11 + PseudoRET implicit $x10 +... From 68662a95a9a5c5945a426d9484e313dc92a821bc Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 10 Sep 2024 13:33:29 +0200 Subject: [PATCH 073/203] [EVM] Add renamable bit to copyPhysReg Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMInstrInfo.cpp | 3 ++- llvm/lib/Target/EVM/EVMInstrInfo.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index caa9e68cb011..340ecff24868 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -36,7 +36,8 @@ bool EVMInstrInfo::isReallyTriviallyReMaterializable( void EVMInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, - MCRegister SrcReg, bool KillSrc) const { + MCRegister SrcReg, bool KillSrc, + bool RenamableDest, bool RenamableSrc) const { // This method is called by post-RA expansion, which expects only // phys registers to exist. However we expect only virtual here. assert(Register::isVirtualRegister(DestReg) && diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h index 8a015bdf2201..216de0cdebe3 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.h +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -44,7 +44,8 @@ class EVMInstrInfo final : public EVMGenInstrInfo { void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, - bool KillSrc) const override; + bool KillSrc, bool RenamableDest = false, + bool RenamableSrc = false) const override; bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, From 1469f7bad19ecf6ace44700c2ddda57a86e29af1 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 7 Oct 2024 11:31:03 +0200 Subject: [PATCH 074/203] [MCP] Skip invalidating constant regs during forward propagation Before this patch, redundant COPY couldn't be removed for the following case: %reg1 = COPY %const-reg ... // No use of %reg1 but there is a def/use of %const-reg %reg2 = COPY killed %reg1 where this can be optimized to: ... // No use of %reg1 but there is a def/use of %const-reg %reg2 = COPY %const-reg This patch allows for such optimization by not invalidating constant registers. This is safe even where constant registers are defined, as architectures like AArch64 and RISCV replace a dead definition of a GPR with a zero constant register for certain instructions. Upstream PR: https://github.com/llvm/llvm-project/pull/111129 Signed-off-by: Vladimir Radosavljevic --- llvm/lib/CodeGen/MachineCopyPropagation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llvm/lib/CodeGen/MachineCopyPropagation.cpp b/llvm/lib/CodeGen/MachineCopyPropagation.cpp index 8bcc437cbfb8..7ddbeed8e0ad 100644 --- a/llvm/lib/CodeGen/MachineCopyPropagation.cpp +++ b/llvm/lib/CodeGen/MachineCopyPropagation.cpp @@ -885,6 +885,12 @@ void MachineCopyPropagation::ForwardCopyPropagateBlock(MachineBasicBlock &MBB) { assert(!Reg.isVirtual() && "MachineCopyPropagation should be run after register allocation!"); + // EVM local begin + // Skip invalidating constant registers. + if (MRI->isReserved(Reg) && MRI->isConstantPhysReg(Reg)) + continue; + // EVM local end + if (MO.isDef() && !MO.isEarlyClobber()) { Defs.push_back(Reg.asMCReg()); continue; From ada1821cdc806425fff1fb095f2a5db5168bc076 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 7 Oct 2024 12:20:37 +0200 Subject: [PATCH 075/203] [MCP] Optimize copies when src is used during backward propagation Before this patch, redundant COPY couldn't be removed for the following case: $R0 = OP ... ... // Read of %R0 $R1 = COPY killed $R0 This patch adds support for tracking the users of the source register during backward propagation, so that we can remove the redundant COPY in the above case and optimize it to: $R1 = OP ... ... // Replace all uses of %R0 with $R1 Upstream PR: https://github.com/llvm/llvm-project/pull/111130 Signed-off-by: Vladimir Radosavljevic --- llvm/lib/CodeGen/MachineCopyPropagation.cpp | 94 ++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/llvm/lib/CodeGen/MachineCopyPropagation.cpp b/llvm/lib/CodeGen/MachineCopyPropagation.cpp index 7ddbeed8e0ad..8c48de890888 100644 --- a/llvm/lib/CodeGen/MachineCopyPropagation.cpp +++ b/llvm/lib/CodeGen/MachineCopyPropagation.cpp @@ -110,6 +110,9 @@ class CopyTracker { struct CopyInfo { MachineInstr *MI = nullptr; MachineInstr *LastSeenUseInCopy = nullptr; + // EVM local begin + SmallPtrSet SrcUsers; + // EVM local end SmallVector DefRegs; bool Avail = false; }; @@ -224,6 +227,45 @@ class CopyTracker { } } + // EVM local begin + /// Track copy's src users, and return false if that can't be done. + /// We can only track if we have a COPY instruction which source is + /// the same as the Reg. + bool trackSrcUsers(MCRegister Reg, MachineInstr &MI, + const TargetRegisterInfo &TRI, const TargetInstrInfo &TII, + bool UseCopyInstr) { + MCRegUnit RU = *TRI.regunits(Reg).begin(); + MachineInstr *AvailCopy = findCopyDefViaUnit(RU, TRI); + if (!AvailCopy) + return false; + + std::optional CopyOperands = + isCopyInstr(*AvailCopy, TII, UseCopyInstr); + Register Src = CopyOperands->Source->getReg(); + + // Bail out, if the source of the copy is not the same as the Reg. + if (Src != Reg) + return false; + + auto I = Copies.find(RU); + if (I == Copies.end()) + return false; + + I->second.SrcUsers.insert(&MI); + return true; + } + + /// Return the users for a given register. + SmallPtrSet getSrcUsers(MCRegister Reg, + const TargetRegisterInfo &TRI) { + MCRegUnit RU = *TRI.regunits(Reg).begin(); + auto I = Copies.find(RU); + if (I == Copies.end()) + return {}; + return I->second.SrcUsers; + } + // EVM local end + /// Add this copy's registers into the tracker's copy maps. void trackCopy(MachineInstr *MI, const TargetRegisterInfo &TRI, const TargetInstrInfo &TII, bool UseCopyInstr) { @@ -236,7 +278,9 @@ class CopyTracker { // Remember Def is defined by the copy. for (MCRegUnit Unit : TRI.regunits(Def)) - Copies[Unit] = {MI, nullptr, {}, true}; + // EVM local begin + Copies[Unit] = {MI, nullptr, {}, {}, true}; + // EVM local end // Remember source that's copied to Def. Once it's clobbered, then // it's no longer available for copy propagation. @@ -427,6 +471,10 @@ class MachineCopyPropagation : public MachineFunctionPass { bool hasImplicitOverlap(const MachineInstr &MI, const MachineOperand &Use); bool hasOverlappingMultipleDef(const MachineInstr &MI, const MachineOperand &MODef, Register Def); + // EVM local begin + bool canUpdateSrcUsers(const MachineInstr &Copy, + const MachineOperand &CopySrc); + // EVM local end /// Candidates for deletion. SmallSetVector MaybeDeadCopies; @@ -667,6 +715,28 @@ bool MachineCopyPropagation::hasOverlappingMultipleDef( return false; } +// EVM local begin +/// Return true if it is safe to update the users of the source register of the +/// copy. +bool MachineCopyPropagation::canUpdateSrcUsers(const MachineInstr &Copy, + const MachineOperand &CopySrc) { + for (auto *SrcUser : Tracker.getSrcUsers(CopySrc.getReg(), *TRI)) { + if (hasImplicitOverlap(*SrcUser, CopySrc)) + return false; + + for (MachineOperand &MO : SrcUser->uses()) { + if (!MO.isReg() || !MO.isUse() || MO.getReg() != CopySrc.getReg()) + continue; + if (MO.isTied() || !MO.isRenamable() || + !isBackwardPropagatableRegClassCopy(Copy, *SrcUser, + MO.getOperandNo())) + return false; + } + } + return true; +} +// EVM local end + /// Look for available copies whose destination register is used by \p MI and /// replace the use in \p MI with the copy's source register. void MachineCopyPropagation::forwardUses(MachineInstr &MI) { @@ -1036,6 +1106,11 @@ void MachineCopyPropagation::propagateDefs(MachineInstr &MI) { if (hasOverlappingMultipleDef(MI, MODef, Def)) continue; + // EVM local begin + if (!canUpdateSrcUsers(*Copy, *CopyOperands->Source)) + continue; + // EVM local end + LLVM_DEBUG(dbgs() << "MCP: Replacing " << printReg(MODef.getReg(), TRI) << "\n with " << printReg(Def, TRI) << "\n in " << MI << " from " << *Copy); @@ -1043,6 +1118,17 @@ void MachineCopyPropagation::propagateDefs(MachineInstr &MI) { MODef.setReg(Def); MODef.setIsRenamable(CopyOperands->Destination->isRenamable()); + // EVM local begin + for (auto *SrcUser : Tracker.getSrcUsers(Src, *TRI)) { + for (MachineOperand &MO : SrcUser->uses()) { + if (!MO.isReg() || !MO.isUse() || MO.getReg() != Src) + continue; + MO.setReg(Def); + MO.setIsRenamable(CopyOperands->Destination->isRenamable()); + } + } + // EVM local end + LLVM_DEBUG(dbgs() << "MCP: After replacement: " << MI << "\n"); MaybeDeadCopies.insert(Copy); Changed = true; @@ -1108,7 +1194,11 @@ void MachineCopyPropagation::BackwardCopyPropagateBlock( CopyDbgUsers[Copy].insert(&MI); } } - } else { + // EVM local begin + } else if (!Tracker.trackSrcUsers(MO.getReg().asMCReg(), MI, *TRI, *TII, + UseCopyInstr)) { + // If we can't track the source users, invalidate the register. + // EVM local end Tracker.invalidateRegister(MO.getReg().asMCReg(), *TRI, *TII, UseCopyInstr); } From 8761490526583c8d5f7c7c2f026d03b3abb36c2e Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 26 Mar 2024 15:11:57 +0200 Subject: [PATCH 076/203] [Optimizer] Add mergebb pass that merges identical BBs The patch adds a pass that merges two identical basic blocks. The logic is borrowed from https://reviews.llvm.org/D82730, but unlike the original PR, it's extracted to a separate pass which is scheduled after simplify CFG. The patch also contain a fix by Vladimir Radosavljevic that removes unreachable blocks before merging. --- .../llvm/Transforms/Scalar/MergeIdenticalBB.h | 28 ++ llvm/lib/Passes/PassBuilder.cpp | 3 + llvm/lib/Passes/PassRegistry.def | 3 + llvm/lib/Transforms/Scalar/CMakeLists.txt | 3 + .../Transforms/Scalar/MergeIdenticalBB.cpp | 242 +++++++++++++ llvm/test/CodeGen/EVM/merge-blocks.ll | 318 ++++++++++++++++++ llvm/test/CodeGen/EVM/mergebb-crash.ll | 31 ++ 7 files changed, 628 insertions(+) create mode 100644 llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h create mode 100644 llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp create mode 100644 llvm/test/CodeGen/EVM/merge-blocks.ll create mode 100644 llvm/test/CodeGen/EVM/mergebb-crash.ll diff --git a/llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h b/llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h new file mode 100644 index 000000000000..dabdaf1f1558 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/MergeIdenticalBB.h @@ -0,0 +1,28 @@ +//===----- MergeSimilarBB.h - Merge identical basic blocks ------*- C++ -*-===// +/// \file +/// This file provides the interface for the pass responsible for merging +/// indentical basic blocks. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_MERGEIDENTICALBB_H +#define LLVM_TRANSFORMS_SCALAR_MERGEIDENTICALBB_H + +#include "llvm/IR/Function.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +/// A pass to merge similar basic blocks. +/// +/// The pass identifies isomorphic basic blocks by hashing its content, then it +/// attempt to merge BBs with the same hash. +class MergeIdenticalBBPass : public PassInfoMixin { +public: + /// Run the pass over the function. + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // namespace llvm + +#endif diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 5dbb1e2f4987..610c4d0ef861 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -257,6 +257,9 @@ #include "llvm/Transforms/Scalar/MakeGuardsExplicit.h" #include "llvm/Transforms/Scalar/MemCpyOptimizer.h" #include "llvm/Transforms/Scalar/MergeICmps.h" +// EVM local begin +#include "llvm/Transforms/Scalar/MergeIdenticalBB.h" +// EVM local begin #include "llvm/Transforms/Scalar/MergedLoadStoreMotion.h" #include "llvm/Transforms/Scalar/NaryReassociate.h" #include "llvm/Transforms/Scalar/NewGVN.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 3b92823cd283..c66b9e2070fc 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -395,6 +395,9 @@ FUNCTION_PASS("make-guards-explicit", MakeGuardsExplicitPass()) FUNCTION_PASS("mem2reg", PromotePass()) FUNCTION_PASS("memcpyopt", MemCpyOptPass()) FUNCTION_PASS("memprof", MemProfilerPass()) +// EVM local begin +FUNCTION_PASS("mergebb", MergeIdenticalBBPass()) +// EVM local end FUNCTION_PASS("mergeicmps", MergeICmpsPass()) FUNCTION_PASS("mergereturn", UnifyFunctionExitNodesPass()) FUNCTION_PASS("move-auto-init", MoveAutoInitPass()) diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index ba09ebf8b04c..dfa27013f0eb 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -56,6 +56,9 @@ add_llvm_component_library(LLVMScalarOpts MakeGuardsExplicit.cpp MemCpyOptimizer.cpp MergeICmps.cpp + # EVM local begin + MergeIdenticalBB.cpp + # EVM local end MergedLoadStoreMotion.cpp NaryReassociate.cpp NewGVN.cpp diff --git a/llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp b/llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp new file mode 100644 index 000000000000..3bc7c38efbb5 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/MergeIdenticalBB.cpp @@ -0,0 +1,242 @@ +//===------------- MergeIdenticalBB.cpp - Identical BB merging pass +//-----------===// +// +// 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 implements merge of similar basic blocks. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/MergeIdenticalBB.h" + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ValueHandle.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" +#include +using namespace llvm; + +#define DEBUG_TYPE "mergebb" + +/// Maximum number of candidate blocks with same hash to consider for merging. +static const unsigned MaxBlockMergeCandidates = 32; + +STATISTIC(NumBlocksMerged, "Number of blocks merged"); + +/// Check whether replacing BB with ReplacementBB would result in a CallBr +/// instruction with a duplicate destination in one of the predecessors. +// FIXME: See note in CodeGenPrepare.cpp. +bool wouldDuplicateCallBrDest(const BasicBlock &BB, + const BasicBlock &ReplacementBB) { + for (const BasicBlock *Pred : predecessors(&BB)) { + if (auto *CBI = dyn_cast(Pred->getTerminator())) + for (const BasicBlock *Succ : successors(CBI)) + if (&ReplacementBB == Succ) + return true; + } + return false; +} + +class HashAccumulator64 { + uint64_t Hash; + +public: + HashAccumulator64() { Hash = 0x6acaa36bef8325c5ULL; } + void add(uint64_t V) { Hash = hashing::detail::hash_16_bytes(Hash, V); } + uint64_t getHash() { return Hash; } +}; + +static uint64_t hashBlock(const BasicBlock &BB) { + HashAccumulator64 Acc; + for (const Instruction &I : BB) + Acc.add(I.getOpcode()); + for (const BasicBlock *Succ : successors(&BB)) + Acc.add((uintptr_t)Succ); + return Acc.getHash(); +} + +static bool canMergeBlocks(const BasicBlock &BB1, const BasicBlock &BB2) { + // Quickly bail out if successors don't match. + if (!std::equal( + succ_begin(&BB1), succ_end(&BB1), succ_begin(&BB2), + [](const BasicBlock *S1, const BasicBlock *S2) { return S1 == S2; })) + return false; + + // Map from instructions in one block to instructions in the other. + SmallDenseMap Map; + auto ValuesEqual = [&Map](const Value *V1, const Value *V2) { + if (V1 == V2) + return true; + + if (const auto *I1 = dyn_cast(V1)) + if (const auto *I2 = dyn_cast(V2)) + if (const Instruction *Mapped = Map.lookup(I1)) + if (Mapped == I2) + return true; + + return false; + }; + + auto InstructionsEqual = [&](const Instruction &I1, const Instruction &I2) { + if (!I1.isSameOperationAs(&I2)) + return false; + + if (const auto *Call = dyn_cast(&I1)) + if (Call->cannotMerge()) + return false; + + if (!std::equal(I1.op_begin(), I1.op_end(), I2.op_begin(), I2.op_end(), + ValuesEqual)) + return false; + + if (const PHINode *PHI1 = dyn_cast(&I2)) { + const PHINode *PHI2 = cast(&I2); + return std::equal(PHI1->block_begin(), PHI1->block_end(), + PHI2->block_begin(), PHI2->block_end()); + } + + if (!I1.use_empty()) + Map.insert({&I1, &I2}); + return true; + }; + auto It1 = BB1.instructionsWithoutDebug(); + auto It2 = BB2.instructionsWithoutDebug(); + if (!std::equal(It1.begin(), It1.end(), It2.begin(), It2.end(), + InstructionsEqual)) + return false; + + // Make sure phi values in successor blocks match. + for (const BasicBlock *Succ : successors(&BB1)) { + for (const PHINode &Phi : Succ->phis()) { + const Value *Incoming1 = Phi.getIncomingValueForBlock(&BB1); + const Value *Incoming2 = Phi.getIncomingValueForBlock(&BB2); + if (!ValuesEqual(Incoming1, Incoming2)) + return false; + } + } + + if (wouldDuplicateCallBrDest(BB1, BB2)) + return false; + + return true; +} + +static bool tryMergeTwoBlocks(BasicBlock &BB1, BasicBlock &BB2) { + if (!canMergeBlocks(BB1, BB2)) + return false; + + // We will keep BB1 and drop BB2. Merge metadata and attributes. + for (auto Insts : llvm::zip(BB1, BB2)) { + Instruction &I1 = std::get<0>(Insts); + Instruction &I2 = std::get<1>(Insts); + + I1.andIRFlags(&I2); + combineMetadataForCSE(&I1, &I2, true); + if (!isa(&I1)) + I1.applyMergedLocation(I1.getDebugLoc(), I2.getDebugLoc()); + } + + // Store predecessors, because they will be modified in this loop. + SmallVector Preds(predecessors(&BB2)); + for (BasicBlock *Pred : Preds) + Pred->getTerminator()->replaceSuccessorWith(&BB2, &BB1); + + for (BasicBlock *Succ : successors(&BB2)) + Succ->removePredecessor(&BB2); + + BB2.eraseFromParent(); + return true; +} + +static bool mergeIdenticalBlocks(Function &F) { + bool Changed = false; + SmallDenseMap> SameHashBlocks; + + for (BasicBlock &BB : make_early_inc_range(F)) { + // The entry block cannot be merged. + if (&BB == &F.getEntryBlock()) + continue; + + // Identify potential merging candidates based on a basic block hash. + bool Merged = false; + auto &Blocks = SameHashBlocks.try_emplace(hashBlock(BB)).first->second; + for (BasicBlock *Block : Blocks) { + if (tryMergeTwoBlocks(*Block, BB)) { + Merged = true; + ++NumBlocksMerged; + break; + } + } + + Changed |= Merged; + if (!Merged && Blocks.size() < MaxBlockMergeCandidates) + Blocks.push_back(&BB); + } + + // TODO: Merge iteratively. + return Changed; +} + +static bool mergeIdenticalBBImpl(Function &F, const TargetTransformInfo &TTI, + DominatorTree *DT) { + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); + + bool EverChanged = removeUnreachableBlocks(F, DT ? &DTU : nullptr); + EverChanged |= mergeIdenticalBlocks(F); + + return EverChanged; +} + +static bool mergeIdenticalBB(Function &F, const TargetTransformInfo &TTI, + DominatorTree *DT) { + assert((!RequireAndPreserveDomTree || + (DT && DT->verify(DominatorTree::VerificationLevel::Full))) && + "Original domtree is invalid?"); + + bool Changed = mergeIdenticalBBImpl(F, TTI, DT); + + assert((!RequireAndPreserveDomTree || + (DT && DT->verify(DominatorTree::VerificationLevel::Full))) && + "Failed to maintain validity of domtree!"); + + return Changed; +} + +PreservedAnalyses MergeIdenticalBBPass::run(Function &F, + FunctionAnalysisManager &AM) { + auto &TTI = AM.getResult(F); + DominatorTree *DT = nullptr; + if (RequireAndPreserveDomTree) + DT = &AM.getResult(F); + if (!mergeIdenticalBB(F, TTI, DT)) + return PreservedAnalyses::all(); + PreservedAnalyses PA; + if (RequireAndPreserveDomTree) + PA.preserve(); + return PA; +} + diff --git a/llvm/test/CodeGen/EVM/merge-blocks.ll b/llvm/test/CodeGen/EVM/merge-blocks.ll new file mode 100644 index 000000000000..b25f17077407 --- /dev/null +++ b/llvm/test/CodeGen/EVM/merge-blocks.ll @@ -0,0 +1,318 @@ +; RUN: opt -S -mtriple=evm -passes="mergebb,simplifycfg" < %s | FileCheck %s + +declare void @dummy() + +define i32 @basic(i32 %x, i32* %p) { +; CHECK-LABEL: @basic( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + store i32 0, i32* %p + br label %exit + +bb2: + store i32 0, i32* %p + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; nonnull present in block blocks, keep it. +define i32 @metadata_nonnull_keep(i32 %x, i32** %p1, i32** %p2) { +; CHECK-LABEL: @metadata_nonnull_keep( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[V1:%.*]] = load ptr, ptr [[P1:%.*]], align 4, !nonnull !0 +; CHECK-NEXT: store ptr [[V1]], ptr [[P2:%.*]], align 32 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %v1 = load i32*, i32** %p1, align 4, !nonnull !{} + store i32* %v1, i32** %p2 + br label %exit + +bb2: + %v2 = load i32*, i32** %p1, align 4, !nonnull !{} + store i32* %v2, i32** %p2 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; nonnull only present in one of the blocks, drop it. +define i32 @metadata_nonnull_drop(i32 %x, i32** %p1, i32** %p2) { +; CHECK-LABEL: @metadata_nonnull_drop( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[V1:%.*]] = load ptr, ptr [[P1:%.*]], align 4 +; CHECK-NEXT: store ptr [[V1]], ptr [[P2:%.*]], align 32 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %v1 = load i32*, i32** %p1, align 4, !nonnull !{} + store i32* %v1, i32** %p2 + br label %exit + +bb2: + %v2 = load i32*, i32** %p1, align 4 + store i32* %v2, i32** %p2 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; The union of both range metadatas should be taken. +define i32 @metadata_range(i32 %x, i32* %p1, i32* %p2) { +; CHECK-LABEL: @metadata_range( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P1:%.*]], align 4, !range !1 +; CHECK-NEXT: store i32 [[V1]], ptr [[P2:%.*]], align 4 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %v1 = load i32, i32* %p1, align 4, !range !{i32 0, i32 10} + store i32 %v1, i32* %p2 + br label %exit + +bb2: + %v2 = load i32, i32* %p1, align 4, !range !{i32 5, i32 15} + store i32 %v2, i32* %p2 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ 0, %bb1 ], [ 0, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; Only the common nuw flag may be preserved. +define i32 @attributes(i32 %x, i32 %y) { +; CHECK-LABEL: @attributes( +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[A:%.*]] = add nuw i32 [[Y:%.*]], 1 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[A]], [[BB1]] ], [ 1, [[BB3]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %x, label %bb3 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + %a = add nuw nsw i32 %y, 1 + br label %exit + +bb2: + %b = add nuw i32 %y, 1 + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ %a, %bb1 ], [ %b, %bb2 ], [ 1, %bb3] + ret i32 %phi +} + +; Don't try to merge with the entry block. +define void @entry_block() { +; CHECK-LABEL: @entry_block( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br label [[LOOP]] +; +entry: + br label %loop + +loop: + br label %loop +} + +; For phi nodes, we need to check that incoming blocks match. +define i32 @phi_blocks(i32 %x, i32* %p) { +; CHECK-LABEL: @phi_blocks( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[EXIT:%.*]], label [[BB3:%.*]] +; CHECK: bb3: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI3:%.*]] = phi i32 [ 1, [[BB3]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: ret i32 [[PHI3]] +; +entry: + switch i32 %x, label %bb3 [ + i32 0, label %bb1.split + i32 1, label %bb2 + ] + +bb1.split: + br label %bb1 + +bb1: + %phi1 = phi i32 [ 0, %bb1.split ] + br label %exit + +bb2: + %phi2 = phi i32 [ 0, %entry ] + br label %exit + +bb3: + call void @dummy() + br label %exit + +exit: + %phi3 = phi i32 [ %phi1, %bb1 ], [ %phi2, %bb2 ], [ 1, %bb3] + ret i32 %phi3 +} + +; This requires merging bb3,4,5,6 before bb1,2. +define i32 @two_level(i32 %x, i32 %y, i32 %z) { +; CHECK-LABEL: @two_level( +; CHECK-NEXT: switch i32 [[Z:%.*]], label [[BB7:%.*]] [ +; CHECK-NEXT: i32 0, label [[BB1:%.*]] +; CHECK-NEXT: i32 1, label [[BB2:%.*]] +; CHECK-NEXT: ] +; CHECK: bb1: +; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i32 [[X:%.*]], 2 +; CHECK-NEXT: br i1 [[SWITCH]], label [[BB3:%.*]], label [[BB7]] +; CHECK: bb2: +; CHECK-NEXT: [[SWITCH1:%.*]] = icmp ult i32 [[X]], 2 +; CHECK-NEXT: br i1 [[SWITCH1]], label [[BB3]], label [[BB7]] +; CHECK: bb3: +; CHECK-NEXT: [[A:%.*]] = add i32 [[Y:%.*]], 1 +; CHECK-NEXT: br label [[EXIT:%.*]] +; CHECK: bb7: +; CHECK-NEXT: call void @dummy() +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[A]], [[BB3]] ], [ 0, [[BB7]] ] +; CHECK-NEXT: ret i32 [[PHI]] +; + switch i32 %z, label %bb7 [ + i32 0, label %bb1 + i32 1, label %bb2 + ] + +bb1: + switch i32 %x, label %bb7 [ + i32 0, label %bb3 + i32 1, label %bb4 + ] + +bb2: + switch i32 %x, label %bb7 [ + i32 0, label %bb5 + i32 1, label %bb6 + ] + +bb3: + %a = add i32 %y, 1 + br label %exit + +bb4: + %b = add i32 %y, 1 + br label %exit + +bb5: + %c = add i32 %y, 1 + br label %exit + +bb6: + %d = add i32 %y, 1 + br label %exit + +bb7: + call void @dummy() + br label %exit + +exit: + %phi = phi i32 [ %a, %bb3 ], [ %b, %bb4 ], [ %c, %bb5 ], [ %d, %bb6 ], [ 0, %bb7 ] + ret i32 %phi +} + +; CHECK: !1 = !{i32 0, i32 15} + diff --git a/llvm/test/CodeGen/EVM/mergebb-crash.ll b/llvm/test/CodeGen/EVM/mergebb-crash.ll new file mode 100644 index 000000000000..7a81b038279b --- /dev/null +++ b/llvm/test/CodeGen/EVM/mergebb-crash.ll @@ -0,0 +1,31 @@ +; RUN: opt -mtriple=evm -passes=mergebb -S < %s | FileCheck %s + +define void @test() { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[RETURN_LOOPEXIT:%.*]] +; CHECK: return.loopexit: +; CHECK-NEXT: [[VAR_Y7_US:%.*]] = alloca i256, align 32 +; CHECK-NEXT: unreachable +; +entry: + br label %return.loopexit + +return.loopexit: ; preds = %entry + %var_y7.us = alloca i256, align 32 + store i256 1, ptr null, align 4294967296 + br label %return + +return: ; preds = %for_join499, %if_join349, %return.loopexit + %var_y7.lcssa = phi ptr [ null, %if_join349 ], [ null, %return.loopexit ], [ %var_y7, %for_join499 ] + ret void + +if_join349: ; No predecessors! + %var_y7 = alloca i256, align 32 + store i256 1, ptr null, align 4294967296 + br label %return + +for_join499: ; No predecessors! + br label %return +} + From fbed86b91fe341399cf87102f772a2b843b0d611 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 31 Oct 2024 17:21:35 +0200 Subject: [PATCH 077/203] [EVM] Add merge identical bb pass to opt pipeline --- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 60e62e0b06d3..4405511587a4 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -26,6 +26,7 @@ #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Transforms/IPO/GlobalDCE.h" +#include "llvm/Transforms/Scalar/MergeIdenticalBB.h" #include "llvm/Transforms/Utils.h" using namespace llvm; @@ -115,6 +116,11 @@ void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PM.addPass(GlobalDCEPass()); PM.addPass(createModuleToFunctionPassAdaptor(EVMAllocaHoistingPass())); }); + PB.registerScalarOptimizerLateEPCallback( + [](FunctionPassManager &PM, OptimizationLevel Level) { + if (Level.getSizeLevel() || Level.getSpeedupLevel() > 1) + PM.addPass(MergeIdenticalBBPass()); + }); } namespace { From f24507a9e571109bcd796126084a2b7f5d0b68f7 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 6 Nov 2024 10:22:38 +0100 Subject: [PATCH 078/203] [EVM] Use std::string instead of Twine Twine variables are prone to use-after-free bugs, as it is stated in the clang-tidy documentation: https://clang.llvm.org/extra/clang-tidy/checks/llvm/twine-local.html Signed-off-by: Vladimir Radosavljevic --- lld/lld-c/LLDAsLibraryC.cpp | 25 ++++++++++++++----------- llvm/lib/Target/EVM/EVMISelLowering.cpp | 4 ++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp index 484b43b88659..a3d54da59eda 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -83,9 +83,11 @@ static std::string creteEVMLinkerScript(ArrayRef memBufs, // __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"; + std::string topLevel = + (topName + "(.text);\n" + dataOffsetPrefix + deployed + " = .;\n" + + deployed + "(.text);\n" + dataSizePrefix + deployed + " = . - " + + dataOffsetPrefix + deployed + ";\n") + .str(); // Contains symbols whose values are the sizes of the dependent contracts. // For the example above, this contains: @@ -125,9 +127,10 @@ static std::string creteEVMLinkerScript(ArrayRef memBufs, // 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"; + std::string deploySize = + ("LONG(" + dataOffsetPrefix + deployed + ");\n").str(); - Twine script = formatv("{0}\n\ + std::string script = formatv("{0}\n\ ENTRY(0);\n\ SECTIONS {\n\ . = 0;\n\ @@ -139,10 +142,10 @@ SECTIONS {\n\ }\n\ }\n\ ", - symDatasizeDeps, topLevel, symDataOffsetDeps, - symDatasizeTop, deploySize); + symDatasizeDeps, topLevel, symDataOffsetDeps, + symDatasizeTop, deploySize); - return script.str(); + return script; } LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], @@ -172,11 +175,11 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], // 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(); + const std::string remapStr("--remap-inputs="); + std::string remapDeployStr = remapStr + inBuffersIDs[0] + "=0"; lldArgs.push_back(remapDeployStr.c_str()); - std::string remapDeployedStr = (remapStr + inBuffersIDs[1] + "=1").str(); + std::string remapDeployedStr = remapStr + inBuffersIDs[1] + "=1"; lldArgs.push_back(remapDeployedStr.c_str()); lldArgs.push_back("--remap-inputs=script.x=2"); diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index 6c8738d2d52f..3dc92eb5f5b3 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -167,8 +167,8 @@ SDValue EVMTargetLowering::lowerIntrinsicDataSize(unsigned IntrID, SDValue Op, 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; + std::string SymbolReloc = + (Twine(IsDataSize ? "__datasize_" : "__dataoffset_") + ContractID).str(); MCSymbol *Sym = MF.getContext().getOrCreateSymbol(SymbolReloc); return SDValue( DAG.getMachineNode(EVM::DATA, DL, Ty, DAG.getMCSymbol(Sym, MVT::i256)), From 4b1c3fbdf26fc785a8c68f3e580d50cfd102f86c Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 12 Nov 2024 23:17:46 +0300 Subject: [PATCH 079/203] [EVM] NFC: fix unused variables --- llvm/lib/Target/EVM/EVMStackify.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp index 9d4b0a16c504..2fb40b2da3d6 100644 --- a/llvm/lib/Target/EVM/EVMStackify.cpp +++ b/llvm/lib/Target/EVM/EVMStackify.cpp @@ -703,7 +703,7 @@ StackLocation StackModel::allocateStackModelLoc(const Register &Reg) { // on reg's live interval. The register goes to X stack if its live interval // spawns across a BB boundary, otherwise put it to L-stack. const auto *LI = &LIS.getInterval(Reg); - if (const auto *BB = LIS.intervalIsInOneMBB(*LI)) + if (LIS.intervalIsInOneMBB(*LI)) return pushRegToStackModel(StackType::L, Reg); return pushRegToStackModel(StackType::X, Reg); @@ -1236,7 +1236,7 @@ bool EVMStackify::runOnMachineFunction(MachineFunction &MF) { LIS.shrinkToUses(LI); LLVM_DEBUG(LI->dump()); - if (const auto *BB = LIS.intervalIsInOneMBB(*LI)) { + if ([[maybe_unused]] const auto *BB = LIS.intervalIsInOneMBB(*LI)) { LLVM_DEBUG(dbgs() << "\tIs live in: (" << BB->getName() << ")\n"); } } From 02d8e29d7cf0f70c85cf9a187a523410bb900e1b Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 30 Oct 2024 09:38:34 +0200 Subject: [PATCH 080/203] [UpdateTestChecks] Add EVM support in update_llc_test_checks.py --- .../Inputs/evm-basic.ll | 6 +++++ .../Inputs/evm-basic.ll.expected | 22 +++++++++++++++++++ .../update_llc_test_checks/evm-basic.test | 4 ++++ llvm/utils/UpdateTestChecks/asm.py | 17 ++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll create mode 100644 llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected create mode 100644 llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll new file mode 100644 index 000000000000..df99209b5291 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll @@ -0,0 +1,6 @@ +; RUN: llc < %s -mtriple=evm | FileCheck %s + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { + %x1 = sub i256 %a4, %a1 + ret i256 %x1 +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected new file mode 100644 index 000000000000..4b6359646f18 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected @@ -0,0 +1,22 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=evm | FileCheck %s + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_second_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP6 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP5 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: JUMP + %x1 = sub i256 %a4, %a1 + ret i256 %x1 +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test new file mode 100644 index 000000000000..1cae7b26a8fd --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/evm-basic.test @@ -0,0 +1,4 @@ +# REQUIRES: evm-registered-target + +# RUN: cp -f %S/Inputs/evm-basic.ll %t.ll && %update_llc_test_checks %t.ll +# RUN: diff -u %S/Inputs/evm-basic.ll.expected %t.ll diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py index f150098eaaee..595bd316555a 100644 --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -257,6 +257,12 @@ class string: flags=(re.M | re.S), ) +ASM_FUNCTION_EVM_RE = re.compile( + r'^_?(?P[^:]+):[ \t]*;+[ \t]*@"?(?P=func)"?\n[^:]*?' + r'(?P.*?)\n' + r'\s*; -- End function', + flags=(re.M | re.S)) + SCRUB_X86_SHUFFLES_RE = re.compile( r"^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$", flags=re.M ) @@ -526,6 +532,16 @@ def scrub_asm_loongarch(asm, args): return asm +def scrub_asm_evm(asm, args): + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) + return asm + # Returns a tuple of a scrub function and a function regex. Scrub function is # used to alter function body in some way, for example, remove trailing spaces. # Function regex is used to match function name, body, etc. in raw llc output. @@ -579,6 +595,7 @@ def get_run_handler(triple): "nvptx": (scrub_asm_nvptx, ASM_FUNCTION_NVPTX_RE), "loongarch32": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), "loongarch64": (scrub_asm_loongarch, ASM_FUNCTION_LOONGARCH_RE), + "evm" : (scrub_asm_evm, ASM_FUNCTION_EVM_RE), } handler = None best_prefix = "" From 0366b1be90dac18edf23eeb2f780a643c07eebd8 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Tue, 19 Nov 2024 13:22:42 +0100 Subject: [PATCH 081/203] [EVM] Use Keccak256 hash-ed linker symbols in LLVM MC/LLD --- lld/lld-c/CMakeLists.txt | 2 + lld/lld-c/LLDAsLibraryC.cpp | 95 ++++++++++++------- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 37 ++++++++ llvm/lib/Target/EVM/EVMISelLowering.cpp | 31 +++--- llvm/lib/Target/EVM/EVMISelLowering.h | 3 - llvm/lib/Target/EVM/EVMInstrInfo.td | 11 ++- llvm/lib/Target/EVM/EVMMCInstLower.cpp | 8 +- .../EVM/MCTargetDesc/EVMMCTargetDesc.cpp | 19 ++++ .../Target/EVM/MCTargetDesc/EVMMCTargetDesc.h | 6 ++ llvm/test/MC/EVM/data-linker-symbol-relocs.ll | 34 +++++++ llvm/test/MC/EVM/datasize_relocation.ll | 51 ---------- 11 files changed, 182 insertions(+), 115 deletions(-) create mode 100644 llvm/test/MC/EVM/data-linker-symbol-relocs.ll delete mode 100644 llvm/test/MC/EVM/datasize_relocation.ll diff --git a/lld/lld-c/CMakeLists.txt b/lld/lld-c/CMakeLists.txt index 4d66f3c989c1..3e4e20806d6f 100644 --- a/lld/lld-c/CMakeLists.txt +++ b/lld/lld-c/CMakeLists.txt @@ -5,6 +5,8 @@ add_lld_library(lldC LINK_COMPONENTS Core + EVMDesc + Object Support LINK_LIBS diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp index a3d54da59eda..e5c8aa73f28b 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -28,6 +28,14 @@ using namespace object; LLD_HAS_DRIVER_MEM_BUF(elf) +namespace EVM { +std::string getLinkerSymbolHash(StringRef SymName); +std::string getLinkerSymbolSectionName(StringRef Name); +std::string getDataSizeSymbol(StringRef SymbolName); +std::string getDataOffsetSymbol(StringRef SymbolName); +} // namespace EVM +} // namespace llvm + static std::mutex lldMutex; /// This function generates a linker script for EVM architecture. @@ -70,12 +78,17 @@ static std::string creteEVMLinkerScript(ArrayRef memBufs, ArrayRef bufIDs) { assert(memBufs.size() == bufIDs.size()); size_t numObjectsToLink = memBufs.size(); - StringRef dataSizePrefix("__datasize_"); - StringRef dataOffsetPrefix("__dataoffset_"); + + auto getDataOffsetName = [](StringRef name) { + return EVM::getDataOffsetSymbol(EVM::getLinkerSymbolHash(name)); + }; + auto getDataSizeName = [](StringRef name) { + return EVM::getDataSizeSymbol(EVM::getLinkerSymbolHash(name)); + }; // Define the script part related to the top-level contract. - StringRef topName(bufIDs[0]); - StringRef deployed(bufIDs[1]); + std::string topName = EVM::getLinkerSymbolHash(bufIDs[0]); + std::string deployed = EVM::getLinkerSymbolHash(bufIDs[1]); // Contains the linker script part corresponding to the top-level contract. // For the example above, this contains: @@ -83,54 +96,58 @@ static std::string creteEVMLinkerScript(ArrayRef memBufs, // __dataoffset_D_105_deployed = .; // D_105_deployed(.text); // __datasize_D_105_deployed = . - __dataoffset_D_105_deployed; - std::string topLevel = - (topName + "(.text);\n" + dataOffsetPrefix + deployed + " = .;\n" + - deployed + "(.text);\n" + dataSizePrefix + deployed + " = . - " + - dataOffsetPrefix + deployed + ";\n") - .str(); + std::string topLevelBuf; + raw_string_ostream topLevel(topLevelBuf); + topLevel << topName << "(.text);\n" + << EVM::getDataOffsetSymbol(deployed) << " = .;\n" + << deployed << "(.text);\n" + << EVM::getDataSizeSymbol(deployed) << " = . - " + << EVM::getDataOffsetSymbol(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; + std::string dataSizeBuf; + raw_string_ostream symDatasizeDeps(dataSizeBuf); // Contains symbols whose values are the offsets of the dependent contracts. // For the example above, this contains: // __dataoffset_B_40 = .; - std::string symDataOffsetDeps; + std::string dataOffsetBuf; + raw_string_ostream symDataOffsetDeps(dataOffsetBuf); 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(); + symDatasizeDeps << getDataSizeName(bufIDs[idx]) << " = " + << LLVMGetBufferSize(memBufs[idx]) << ";\n"; - symDataOffsetDeps = (dataOffsetPrefix + bufIDs[2] + " = .;\n").str(); + symDataOffsetDeps << getDataOffsetName(bufIDs[2]) << " = .;\n"; for (unsigned idx = 3; idx < numObjectsToLink; ++idx) - symDataOffsetDeps += - (dataOffsetPrefix + bufIDs[idx] + " = " + dataOffsetPrefix + - bufIDs[idx - 1] + " + " + dataSizePrefix + bufIDs[idx - 1] + ";\n") - .str(); + symDataOffsetDeps << getDataOffsetName(bufIDs[idx]) << " = " + << getDataOffsetName(bufIDs[idx - 1]) << " + " + << getDataSizeName(bufIDs[idx - 1]) << ";\n"; } // Contains a symbol whose value is the total size of the top-level contract // with all the dependencies. - std::string symDatasizeTop = (dataSizePrefix + topName + " = ").str(); + std::string dataSizeTopBuf; + raw_string_ostream symDatasizeTop(dataSizeTopBuf); + symDatasizeTop << EVM::getDataSizeSymbol(topName) << " = "; if (numObjectsToLink > 2) - symDatasizeTop += (dataOffsetPrefix + bufIDs.back() + " + " + - dataSizePrefix + bufIDs.back() + ";\n") - .str(); + symDatasizeTop << getDataOffsetName(bufIDs.back()) << " + " + << getDataSizeName(bufIDs.back()) << ";\n"; else - symDatasizeTop += ".;\n"; + 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. std::string deploySize = - ("LONG(" + dataOffsetPrefix + deployed + ");\n").str(); + "LONG(" + EVM::getDataOffsetSymbol(deployed) + ");\n"; - std::string script = formatv("{0}\n\ + std::string script = + formatv("{0}\n\ ENTRY(0);\n\ SECTIONS {\n\ . = 0;\n\ @@ -142,8 +159,8 @@ SECTIONS {\n\ }\n\ }\n\ ", - symDatasizeDeps, topLevel, symDataOffsetDeps, - symDatasizeTop, deploySize); + symDatasizeDeps.str(), topLevel.str(), symDataOffsetDeps.str(), + symDatasizeTop.str(), deploySize); return script; } @@ -154,16 +171,21 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], assert(numInBuffers > 1); SmallVector localInMemBufRefs(3); SmallVector> localInMemBufs(3); + + // TODO: #740. Verify that the object files contain sections with original + // inBuffersIDs, i.e. before taking hash. for (unsigned idx = 0; idx < 2; ++idx) { MemoryBufferRef ref = *unwrap(inBuffers[idx]); - localInMemBufs[idx] = - MemoryBuffer::getMemBuffer(ref.getBuffer(), inBuffersIDs[idx], - /*RequiresNullTerminator*/ false); + // We need to copy buffers to be able to change their names, as this matters + // for the linker. + localInMemBufs[idx] = MemoryBuffer::getMemBufferCopy( + ref.getBuffer(), EVM::getLinkerSymbolHash(inBuffersIDs[idx])); 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(); @@ -176,19 +198,20 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], // Use remapping of file names (a linker feature) to replace file names with // indexes in the array of memory buffers. const std::string remapStr("--remap-inputs="); - std::string remapDeployStr = remapStr + inBuffersIDs[0] + "=0"; + std::string topHash = EVM::getLinkerSymbolHash(inBuffersIDs[0]); + std::string deployedHash = EVM::getLinkerSymbolHash(inBuffersIDs[1]); + std::string remapDeployStr = remapStr + topHash + "=0"; lldArgs.push_back(remapDeployStr.c_str()); - std::string remapDeployedStr = remapStr + inBuffersIDs[1] + "=1"; + std::string remapDeployedStr = remapStr + deployedHash + "=1"; lldArgs.push_back(remapDeployedStr.c_str()); lldArgs.push_back("--remap-inputs=script.x=2"); // Deploy code - lldArgs.push_back(inBuffersIDs[0]); + lldArgs.push_back(topHash.c_str()); // Deployed code - lldArgs.push_back(inBuffersIDs[1]); - + lldArgs.push_back(deployedHash.c_str()); lldArgs.push_back("--oformat=binary"); SmallString<0> codeString; diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index 22b3d476d072..999ac042d2bd 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -13,13 +13,20 @@ #include "EVMMCInstLower.h" #include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "MCTargetDesc/EVMTargetStreamer.h" #include "TargetInfo/EVMTargetInfo.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" #include "llvm/MC/TargetRegistry.h" + using namespace llvm; #define DEBUG_TYPE "asm-printer" @@ -50,6 +57,9 @@ class EVMAsmPrinter : public AsmPrinter { /// fall-through. bool isBlockOnlyReachableByFallthrough( const MachineBasicBlock *MBB) const override; + +private: + void emitLinkerSymbol(const MachineInstr *MI); }; } // end of anonymous namespace @@ -94,6 +104,13 @@ void EVMAsmPrinter::emitFunctionEntryLabel() { void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping, MF->getRegInfo()); + + unsigned Opc = MI->getOpcode(); + if (Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S) { + emitLinkerSymbol(MI); + return; + } + MCInst TmpInst; MCInstLowering.Lower(MI, TmpInst); EmitToStreamer(*OutStreamer, TmpInst); @@ -105,6 +122,26 @@ bool EVMAsmPrinter::isBlockOnlyReachableByFallthrough( return false; } +void EVMAsmPrinter::emitLinkerSymbol(const MachineInstr *MI) { + MCSymbol *LinkerSymbol = MI->getOperand(0).getMCSymbol(); + StringRef LinkerSymbolName = LinkerSymbol->getName(); + unsigned Opc = MI->getOpcode(); + assert(Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S); + + std::string SymbolNameHash = EVM::getLinkerSymbolHash(LinkerSymbolName); + std::string DataSymbolNameHash = + (Opc == EVM::DATASIZE_S) ? EVM::getDataSizeSymbol(SymbolNameHash) + : EVM::getDataOffsetSymbol(SymbolNameHash); + + MCInst MCI; + MCI.setOpcode(EVM::PUSH4_S); + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_EVM_DATA; + MCOperand MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(DataSymbolNameHash, Kind, OutContext)); + MCI.addOperand(MCOp); + EmitToStreamer(*OutStreamer, MCI); +} + 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 3dc92eb5f5b3..7f83fb39adef 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -153,28 +153,21 @@ SDValue EVMTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, default: return SDValue(); case Intrinsic::evm_datasize: - case Intrinsic::evm_dataoffset: - return lowerIntrinsicDataSize(IntrID, Op, DAG); + case Intrinsic::evm_dataoffset: { + 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(); + MCSymbol *Sym = MF.getContext().getOrCreateSymbol(ContractID); + unsigned Opc = + (IntrID == Intrinsic::evm_datasize) ? EVM::DATASIZE : EVM::DATAOFFSET; + return SDValue( + DAG.getMachineNode(Opc, DL, Ty, DAG.getMCSymbol(Sym, MVT::i256)), 0); + } break; } } -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; - std::string SymbolReloc = - (Twine(IsDataSize ? "__datasize_" : "__dataoffset_") + ContractID).str(); - 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); diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 3c20d090c4d8..c47b593917d2 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -133,9 +133,6 @@ class EVMTargetLowering final : public TargetLowering { 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.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 8700834e45d8..fe34e814aff9 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -1122,7 +1122,12 @@ def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32 $imm", } // 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>; +let isCodeGenOnly = 1, BaseName = "DATASIZE" in { + def DATASIZE : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; +} + +let isCodeGenOnly = 1, BaseName = "DATAOFFSET" in { + def DATAOFFSET : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATAOFFSET_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 297385478bb7..3278761f0d95 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -39,7 +39,7 @@ static void stackifyInstruction(const MachineInstr *MI, MCInst &OutMI) { // Set up final opcodes for the following codegen-only instructions. unsigned Opcode = OutMI.getOpcode(); - if (Opcode == EVM::PUSH_LABEL || Opcode == EVM::DATA_S) + if (Opcode == EVM::PUSH_LABEL) OutMI.setOpcode(EVM::PUSH4_S); // Check that all the instructions are in the 'stack' form. @@ -125,9 +125,11 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { } break; case MachineOperand::MO_MCSymbol: { MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_None; +#ifndef NDEBUG unsigned Opc = MI->getOpcode(); - if (Opc == EVM::DATA_S) - Kind = MCSymbolRefExpr::VariantKind::VK_EVM_DATA; + // We handle the linkage-related instructions in the EVMAsmPrinter. + assert(Opc != EVM::DATASIZE_S && Opc != EVM::DATAOFFSET_S); +#endif // NDEBUG MCOp = MCOperand::createExpr( MCSymbolRefExpr::create(MO.getMCSymbol(), Kind, Ctx)); diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp index c9e24e746f81..b4b701f6b66c 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -15,10 +15,12 @@ #include "EVMMCAsmInfo.h" #include "EVMTargetStreamer.h" #include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/KECCAK.h" using namespace llvm; @@ -113,3 +115,20 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTargetMC() { // Register the null target streamer. TargetRegistry::RegisterNullTargetStreamer(T, createEVMNullTargetStreamer); } + +// Returs a string of the following format: +// '__$KECCAK256(SymName)$__' +std::string EVM::getLinkerSymbolHash(StringRef SymName) { + std::array Hash = KECCAK::KECCAK_256(SymName); + SmallString<72> HexHash; + toHex(Hash, /*LowerCase*/ true, HexHash); + return (Twine("__$") + HexHash + "$__").str(); +} + +std::string EVM::getDataSizeSymbol(StringRef SymbolName) { + return (Twine("__datasize") + SymbolName).str(); +} + +std::string EVM::getDataOffsetSymbol(StringRef SymbolName) { + return (Twine("__dataoffset") + SymbolName).str(); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h index 639ae49357b1..55463908ee02 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h @@ -13,6 +13,7 @@ #ifndef LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H #define LLVM_LIB_TARGET_EVM_MCTARGETDESC_EVMMCTARGETDESC_H +#include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" #include @@ -36,6 +37,11 @@ MCAsmBackend *createEVMMCAsmBackend(const Target &T, const MCSubtargetInfo &STI, std::unique_ptr createEVMELFObjectWriter(uint8_t OSABI); +namespace EVM { +std::string getLinkerSymbolHash(StringRef SymName); +std::string getDataSizeSymbol(StringRef SymbolName); +std::string getDataOffsetSymbol(StringRef SymbolName); +} // namespace EVM } // namespace llvm // Defines symbolic names for EVM registers. diff --git a/llvm/test/MC/EVM/data-linker-symbol-relocs.ll b/llvm/test/MC/EVM/data-linker-symbol-relocs.ll new file mode 100644 index 000000000000..8c0fb82b1e84 --- /dev/null +++ b/llvm/test/MC/EVM/data-linker-symbol-relocs.ll @@ -0,0 +1,34 @@ +; 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 __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{\d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{\d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{\d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ + +; 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.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) + +define i256 @foo() { +entry: + %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) + %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %deployed_size, %deployed_off + ret i256 %res +} + +define i256 @bar() { +entry: + %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) + %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = sub i256 %deployed_size, %deployed_off + ret i256 %res +} + +!1 = !{!"D_105_deployed"} diff --git a/llvm/test/MC/EVM/datasize_relocation.ll b/llvm/test/MC/EVM/datasize_relocation.ll deleted file mode 100644 index 906aee7bc15a..000000000000 --- a/llvm/test/MC/EVM/datasize_relocation.ll +++ /dev/null @@ -1,51 +0,0 @@ -; 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"} From aec865df48ffab5bf67c8398a6d7f8506fb6c686 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 15 Nov 2024 13:21:22 +0100 Subject: [PATCH 082/203] [EVM] Disable memcopy expansion --- .../lib/Transforms/InstCombine/CMakeLists.txt | 1 + .../InstCombine/InstCombineCalls.cpp | 11 +++++++ .../CodeGen/EVM/memintrinsics-no-expansion.ll | 32 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll diff --git a/llvm/lib/Transforms/InstCombine/CMakeLists.txt b/llvm/lib/Transforms/InstCombine/CMakeLists.txt index 6ab5e6da21cf..ae0ae4131b18 100644 --- a/llvm/lib/Transforms/InstCombine/CMakeLists.txt +++ b/llvm/lib/Transforms/InstCombine/CMakeLists.txt @@ -26,5 +26,6 @@ add_llvm_component_library(LLVMInstCombine Analysis Core Support + TargetParser # EVM local TransformUtils ) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index ed981989fbc2..4a297c689877 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -64,6 +64,9 @@ #include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/InstCombine/InstCombiner.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/Local.h" @@ -156,6 +159,14 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) { uint64_t Size = MemOpLength->getLimitedValue(); assert(Size && "0-sized memory transferring should be removed already."); + // EVM local begin + // For EVM we do not need to expand memcpy at all, as the possible memcpy + // operations are 1 to 1 mapped to corresponding instructions. + Triple TT(MI->getFunction()->getParent()->getTargetTriple()); + if (TT.isEVM()) + return nullptr; + // EVM local end + if (Size > 8 || (Size&(Size-1))) return nullptr; // If not 1/2/4/8 bytes, exit. diff --git a/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll b/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll new file mode 100644 index 000000000000..3391914454fa --- /dev/null +++ b/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll @@ -0,0 +1,32 @@ +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define fastcc void @calldata_to_heap() { +; CHECK-LABEL: calldata_to_heap +; CHECK: llvm.memcpy.p1.p2.i256 + + call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) null, ptr addrspace(2) null, i256 4, i1 false) + ret void +} + +define fastcc void @returndata_to_heap() { +; CHECK-LABEL: returndata_to_heap +; CHECK: llvm.memcpy.p1.p3.i256 + + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) null, ptr addrspace(3) null, i256 4, i1 false) + ret void +} + +define fastcc void @code_to_heap() { +; CHECK-LABEL: code_to_heap +; CHECK: llvm.memcpy.p1.p4.i256 + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) null, i256 4, i1 false) + ret void +} From 46514b81d6387f3a8be74f6df8be1dc192864dac Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 14 Nov 2024 12:31:30 +0100 Subject: [PATCH 083/203] [EVM] Add libraries support --- lld/include/lld-c/LLDAsLibraryC.h | 69 +++- lld/lld-c/LLDAsLibraryC.cpp | 317 +++++++++++++++++- lld/unittests/CMakeLists.txt | 3 + lld/unittests/EVM/CMakeLists.txt | 16 + lld/unittests/EVM/LLDTest.cpp | 236 +++++++++++++ llvm/include/llvm/BinaryFormat/ELF.h | 7 + llvm/include/llvm/IR/IntrinsicsEVM.td | 10 + llvm/include/llvm/MC/MCExpr.h | 2 +- llvm/include/llvm/Object/ELFObjectFile.h | 5 + .../EVM/Disassembler/EVMDisassembler.cpp | 23 +- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 87 ++++- llvm/lib/Target/EVM/EVMISelLowering.cpp | 14 +- llvm/lib/Target/EVM/EVMInstrInfo.td | 21 +- llvm/lib/Target/EVM/EVMMCInstLower.cpp | 1 - .../Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp | 4 +- .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 13 +- .../EVM/MCTargetDesc/EVMMCTargetDesc.cpp | 28 ++ .../Target/EVM/MCTargetDesc/EVMMCTargetDesc.h | 5 + .../EVM/MCTargetDesc/EVMTargetStreamer.cpp | 61 +++- .../EVM/MCTargetDesc/EVMTargetStreamer.h | 12 +- llvm/test/MC/EVM/data-linker-symbol-relocs.ll | 32 +- llvm/tools/llvm-readobj/ELFDumper.cpp | 16 + 22 files changed, 917 insertions(+), 65 deletions(-) create mode 100644 lld/unittests/EVM/CMakeLists.txt create mode 100644 lld/unittests/EVM/LLDTest.cpp diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h index 106f59eef8b9..4327e4f052c3 100644 --- a/lld/include/lld-c/LLDAsLibraryC.h +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -10,6 +10,55 @@ // - 'LLVMLinkEVM' // The interface to the LLD linker functionality (via lld-as-a-library) // +// - 'LLVMIsELF' +// Checks if the given memory buffer contains an ELF EVM object file +// +// - 'LLVMGetUndefinedSymbolsEVM' +// Returns an array of undefined linker symbols +// +// - 'LLVMDisposeUndefinedSymbolsEVM' +// Disposes an array returned by the 'LLVMGetUndefinedSymbolsEVM' +// +// These functions use a notion of the 'Linker Symbol' which is generalization +// of a usual ELF global symbol. The main difference is that 'Linker Symbol' +// has a 20-byte value, whereas the maximum value width of a usual ELF symbol +// is just 8 bytes. In order to represent a 20-byte symbol value with its +// relocation, initial 'Linker Symbol' is split into five sub-symbols +// which are usual 32-bit ELF symbols. This split is performed by the LLVM MC +// layer. For example, if the codegen needs to represent a 20-byte relocatable +// value associated with the symbol 'symbol_id', the MC layer sequentially +// (in the binary layout) emits the following undefined symbols: +// +// '__linker_symbol_id_0' +// '__linker_symbol_id_1' +// '__linker_symbol_id_2' +// '__linker_symbol_id_3' +// '__linker_symbol_id_4' +// +// with associated 32-bit relocations. Each sub-symbol name is formed by +// prepending '__linker' and appending '_[0-4]'. MC layer also sets the +// ELF::STO_EVM_LINKER_SYMBOL flag in the 'st_other' field in the symbol +// table entry to distinguish such symbols from all others. +// In EVM, only these symbols are allowed to be undefined in an object +// code. All other cases must be treated as unreachable and denote a bug +// in the FE/LLVM codegen/Linker implementation. +// 'Linker Symbols' are resolved, i.e they receive their final 20-byte +// values, at the linkage stage when calling LLVMLinkEVM. +// For this, the 'LLVMLinkEVM' has parameters: +// - \p linkerSymbolNames, array of null-terminated linker symbol names +// - \p linkerSymbolValues, array of symbol values +// +// For example, if linkerSymbolNames[0] points to a string 'symbol_id', +// it takes the linkerSymbolValues[0] value which is 20-byte array +// 0xAAAAAAAABB.....EEEEEEEE) and creates five symbol definitions in +// a linker script: +// +// "__linker_symbol_id_0" = 0xAAAAAAAA +// "__linker_symbol_id_1" = 0xBBBBBBBB +// "__linker_symbol_id_2" = 0xCCCCCCCC +// "__linker_symbol_id_3" = 0xDDDDDDDD +// "__linker_symbol_id_4" = 0xEEEEEEEE +// //===----------------------------------------------------------------------===// #ifndef LLD_C_LLDASLIBRARYC_H @@ -24,6 +73,17 @@ LLVM_C_EXTERN_C_BEGIN // only usage is to represent Ethereum addresses which are of 160 bit width. #define LINKER_SYMBOL_SIZE 20 +/** Returns true if the \p inBuffer contains an ELF object file. */ +LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer); + +void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, + char ***linkerSymbols, + uint64_t *numLinkerSymbols); + +/** Disposes an array with symbols returned by the + * LLVMGetUndefinedReferences* functions. */ +void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols); + /** Links the deploy and runtime ELF object files using the information about * dependencies. * \p inBuffers - array of input memory buffers with following structure: @@ -42,13 +102,18 @@ LLVM_C_EXTERN_C_BEGIN * to the object names in the YUL layout. * On success, outBuffers[0] will contain the deploy bytecode and outBuffers[1] * the runtime bytecode. + * \p linkerSymbolNames has the same meaning as for LLVMLinkEVM. + * If at least one resulting binary contains unresolved linker symbols, + * output binaries will be returned as ELF object files. See LLVMLinkEVM + * description. * 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); - + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols, 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 e5c8aa73f28b..64dbf87cbe6b 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -14,13 +14,13 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" +#include #include #include #include -#include +#include #include #include -#include #include using namespace llvm; @@ -28,16 +28,189 @@ using namespace object; LLD_HAS_DRIVER_MEM_BUF(elf) +namespace llvm { namespace EVM { std::string getLinkerSymbolHash(StringRef SymName); -std::string getLinkerSymbolSectionName(StringRef Name); +std::string getLinkerSymbolName(StringRef SymName); +std::string getSymbolSectionName(StringRef Name); +std::string getSymbolIndexedName(StringRef Name, unsigned SubIdx); std::string getDataSizeSymbol(StringRef SymbolName); std::string getDataOffsetSymbol(StringRef SymbolName); +std::string getNonIndexedSymbolName(StringRef Name); +bool isLinkerSymbolName(StringRef Name); } // namespace EVM } // namespace llvm +enum class ReferenceSymbolType { Linker }; + +constexpr static unsigned subSymbolRelocSize = sizeof(uint32_t); + static std::mutex lldMutex; +/// Returns reference to the section content. The section is expected +/// the be present in the file. +static StringRef getSectionContent(const ObjectFile &file, + StringRef sectionName) { + section_iterator si = std::find_if(file.section_begin(), file.section_end(), + [§ionName](const SectionRef &sec) { + StringRef curSecName = + cantFail(sec.getName()); + return curSecName == sectionName; + }); + if (si == file.section_end()) + report_fatal_error(Twine("lld: expected ") + sectionName + + " in object file"); + + return cantFail(si->getContents()); +} + +static std::string getLinkerSubSymbolName(StringRef name, unsigned idx) { + return EVM::getSymbolIndexedName( + EVM::getLinkerSymbolName(EVM::getLinkerSymbolHash(name)), idx); +} + +/// Returns true if the object file \p file contains any other undefined +/// linker dependency symbols besides those passed in \p linkerSymbolNames. +static bool hasUndefinedReferenceSymbols(ObjectFile &file, + const char *const *linkerSymbolNames, + uint64_t numLinkerSymbols) { + StringSet<> symbolsToBeDefined; + // Create a set of possible symbols from the 'linkerSymbolNames' array. + for (unsigned symIdx = 0; symIdx < numLinkerSymbols; ++symIdx) { + for (unsigned subSymIdx = 0; + subSymIdx < LINKER_SYMBOL_SIZE / subSymbolRelocSize; ++subSymIdx) { + std::string subSymName = + getLinkerSubSymbolName(linkerSymbolNames[symIdx], subSymIdx); + if (!symbolsToBeDefined.insert(subSymName).second) + report_fatal_error(Twine("lld: duplicating reference symbol ") + + subSymName + "in object file"); + } + } + + for (const SymbolRef &sym : file.symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other == ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) { + StringRef symName = cantFail(sym.getName()); + if (!symbolsToBeDefined.contains(symName)) + return true; + } + } + return false; +} + +/// Returns a string with the symbol definitions passed in +/// \p linkerSymbolValues. For each name from the \p linkerSymbolNames array it +/// creates five symbol definitions. For example, if the linkerSymbolNames[0] +/// points to a string 'symbol_id', it takes the linkerSymbolValues[0] value +/// (which is 20 byte array: 0xAAAAAAAABB.....EEEEEEEE) and creates five symbol +/// definitions: +/// +/// __linker_symbol_id_0 = 0xAAAAAAAA +/// __linker_symbol_id_1 = 0xBBBBBBBB +/// __linker_symbol_id_2 = 0xCCCCCCCC +/// __linker_symbol_id_3 = 0xDDDDDDDD +/// __linker_symbol_id_4 = 0xEEEEEEEE +/// +static std::string +createSymbolDefinitions(const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols) { + auto getSymbolDef = + [](StringRef symName, const char *symVal, size_t symSize, + std::function subSymNameFunc) + -> std::string { + std::string symbolDefBuf; + raw_string_ostream symbolDef(symbolDefBuf); + SmallString<128> hexStrSymbolVal; + toHex(ArrayRef(reinterpret_cast(symVal), symSize), + /*LowerCase*/ false, hexStrSymbolVal); + for (unsigned idx = 0; idx < symSize / subSymbolRelocSize; ++idx) { + symbolDef << subSymNameFunc(symName, idx); + symbolDef << " = 0x"; + symbolDef << hexStrSymbolVal + .substr(2 * subSymbolRelocSize * idx, + 2 * subSymbolRelocSize) + .str(); + symbolDef << ";\n"; + } + return symbolDef.str(); + }; + + std::string symbolsDefBuf; + raw_string_ostream symbolsDef(symbolsDefBuf); + for (uint64_t symNum = 0; symNum < numLinkerSymbols; ++symNum) + symbolsDef << getSymbolDef(linkerSymbolNames[symNum], + linkerSymbolValues[symNum], LINKER_SYMBOL_SIZE, + &getLinkerSubSymbolName); + + return symbolsDef.str(); +} + +/// Returns an array of names of undefined reference symbols in the +/// ELF object. +static char **LLVMGetUndefinedSymbols(const ObjectFile *oFile, + uint64_t *numSymbols, + ReferenceSymbolType symbolType) { + StringSet<> undefSymbols; + StringSet<> undefSubSymbols; + for (const SymbolRef &sym : oFile->symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other == ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) { + StringRef subName = cantFail(sym.getName()); + if (symbolType == ReferenceSymbolType::Linker && + EVM::isLinkerSymbolName(subName)) { + undefSubSymbols.insert(subName); + std::string symName = EVM::getNonIndexedSymbolName(subName); + undefSymbols.insert(symName); + } + } + } + + *numSymbols = undefSymbols.size(); + if (!undefSymbols.size()) + return nullptr; + + char **undefSymbolNames = reinterpret_cast( + std::malloc(undefSymbols.size() * sizeof(char *))); + unsigned undefSymIdx = 0; + for (const StringSet<>::value_type &entry : undefSymbols) { + StringRef symName = entry.first(); + // Check that 'undefSubSymbols' forms a set of groups each consisting of + // five or eight sub-symbols depending on the symbol type. + for (unsigned idx = 0; idx < LINKER_SYMBOL_SIZE / subSymbolRelocSize; idx++) { + std::string subSymName = EVM::getSymbolIndexedName(symName, idx); + if (!undefSubSymbols.contains(subSymName)) { + report_fatal_error(Twine("lld: missing reference symbol ") + + subSymName); + } + } + std::string secName = EVM::getSymbolSectionName(symName); + undefSymbolNames[undefSymIdx++] = + strdup(getSectionContent(*oFile, secName).str().c_str()); + } + + // Sort the returned names in lexicographical order. + std::sort( + undefSymbolNames, undefSymbolNames + *numSymbols, + [](const char *s1, const char *s2) { return std::strcmp(s1, s2) < 0; }); + + return undefSymbolNames; +} + +/// Disposes an array with symbols returned by the +/// LLVMGetUndefinedReferences* functions. +void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols) { + for (unsigned idx = 0; idx < numSymbols; ++idx) + std::free(symbolNames[idx]); + std::free(symbolNames); +} + +//----------------------------------------------------------------------------// + /// This function generates a linker script for EVM architecture. /// \p memBufs - array of input memory buffers with following structure: /// @@ -165,9 +338,111 @@ SECTIONS {\n\ return script; } +/// Create linker script as described in the createEVMRelLinkerScript. +static std::string createEVMLinkerSymbolsDefinitions( + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols) { + return createSymbolDefinitions(linkerSymbolNames, linkerSymbolValues, + numLinkerSymbols); +} + +/// Resolve undefined linker symbols in the ELF object file \inBuffer +/// and return the obtained ELF object file. +static bool +resolveEVMLinkerSymbols(MemoryBufferRef inBuffer, + LLVMMemoryBufferRef *outBuffer, + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols, char **errorMessage) { + SmallVector localInMemBufRefs(2); + localInMemBufRefs[0] = inBuffer; + std::string linkerScript = createEVMLinkerSymbolsDefinitions( + linkerSymbolNames, linkerSymbolValues, numLinkerSymbols); + + std::unique_ptr linkerScriptBuf = + MemoryBuffer::getMemBuffer(linkerScript, "1"); + + localInMemBufRefs[1] = linkerScriptBuf->getMemBufferRef(); + + SmallVector lldArgs; + lldArgs.push_back("ld.lld"); + lldArgs.push_back("-T"); + lldArgs.push_back("1"); + + lldArgs.push_back("--relocatable"); + + // Push the name of the input object file - '0'. + lldArgs.push_back("0"); + + 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; + // For unification with other LLVM C-API functions, return 'true' in case of + // an error. + if (!Ret) { + *errorMessage = strdup(errorString.c_str()); + return true; + } + + StringRef data = ostream.str(); + *outBuffer = LLVMCreateMemoryBufferWithMemoryRangeCopy(data.data(), + data.size(), "result"); + return false; +} + +/// Returns true if the \p inBuffer contains an ELF object file. +LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer) { + Expected> inBinaryOrErr = + ELFObjectFile::create(unwrap(inBuffer)->getMemBufferRef()); + if (!inBinaryOrErr) { + handleAllErrors(inBinaryOrErr.takeError(), [](const ErrorInfoBase &EI) {}); + return false; + } + return inBinaryOrErr.get().getArch() == Triple::evm; +} + +/// Returns an array of undefined linker symbol names +/// of the ELF object passed in \p inBuffer. +void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, + char ***linkerSymbols, + uint64_t *numLinkerSymbols) { + if (linkerSymbols) { + *numLinkerSymbols = 0; + *linkerSymbols = nullptr; + } + + if (!LLVMIsELFEVM(inBuffer)) + return; + + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + const auto *oFile = static_cast(inBinary.get()); + + if (linkerSymbols) + *linkerSymbols = LLVMGetUndefinedSymbols(oFile, numLinkerSymbols, + ReferenceSymbolType::Linker); +} + +/// Links the deploy and runtime ELF object files using the information about +// dependencies. LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], const char *inBuffersIDs[], uint64_t numInBuffers, - LLVMMemoryBufferRef outBuffers[2], char **errorMessage) { + LLVMMemoryBufferRef outBuffers[2], + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols, char **errorMessage) { assert(numInBuffers > 1); SmallVector localInMemBufRefs(3); SmallVector> localInMemBufs(3); @@ -183,8 +458,38 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], localInMemBufRefs[idx] = localInMemBufs[idx]->getMemBufferRef(); } - std::string linkerScript = creteEVMLinkerScript( - ArrayRef(inBuffers, numInBuffers), ArrayRef(inBuffersIDs, numInBuffers)); + // We need to check 'init' and 'deploy' ELF files for the undefined reference + // symbols. They are located at the beginning of the localInMemBufRefs. + bool shouldEmitRelocatable = std::any_of( + localInMemBufRefs.begin(), std::next(localInMemBufRefs.begin(), 2), + [linkerSymbolNames, numLinkerSymbols](MemoryBufferRef bufRef) { + std::unique_ptr InBinary = cantFail(createBinary(bufRef)); + assert(InBinary->isObject()); + return hasUndefinedReferenceSymbols( + *static_cast(InBinary.get()), linkerSymbolNames, + numLinkerSymbols); + }); + + if (shouldEmitRelocatable) { + for (unsigned idx = 0; idx < 2; ++idx) { + LLVMMemoryBufferRef outBuf = nullptr; + char *errMsg = nullptr; + if (resolveEVMLinkerSymbols(localInMemBufRefs[idx], &outBuf, + linkerSymbolNames, linkerSymbolValues, + numLinkerSymbols, &errMsg)) { + *errorMessage = errMsg; + return true; + } + outBuffers[idx] = outBuf; + } + return false; + } + + std::string linkerScript = createEVMLinkerSymbolsDefinitions( + linkerSymbolNames, linkerSymbolValues, numLinkerSymbols); + + linkerScript += creteEVMLinkerScript(ArrayRef(inBuffers, numInBuffers), + ArrayRef(inBuffersIDs, numInBuffers)); std::unique_ptr scriptBuf = MemoryBuffer::getMemBuffer(linkerScript, "script.x"); diff --git a/lld/unittests/CMakeLists.txt b/lld/unittests/CMakeLists.txt index ffaea3f20783..25cdf788504c 100644 --- a/lld/unittests/CMakeLists.txt +++ b/lld/unittests/CMakeLists.txt @@ -4,3 +4,6 @@ endfunction() add_subdirectory(AsLibAll) add_subdirectory(AsLibELF) +# EVM local begin +add_subdirectory(EVM) +# EVM local End diff --git a/lld/unittests/EVM/CMakeLists.txt b/lld/unittests/EVM/CMakeLists.txt new file mode 100644 index 000000000000..a0bbb663b407 --- /dev/null +++ b/lld/unittests/EVM/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lld_unittests(EVMLLDTests + LLDTest.cpp +) + +target_link_libraries(EVMLLDTests + PRIVATE + lldCommon + lldC + lldELF + LLVMCore + LLVMEVMCodeGen + LLVMEVMInfo + LLVMEVMDesc + LLVMIRReader + LLVMTarget +) diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp new file mode 100644 index 000000000000..3aa9bb2a62b8 --- /dev/null +++ b/lld/unittests/EVM/LLDTest.cpp @@ -0,0 +1,236 @@ +//===---------- llvm/unittests/MC/AssemblerTest.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 +// +//===----------------------------------------------------------------------===// + +#include "lld-c/LLDAsLibraryC.h" +#include "llvm-c/Core.h" +#include "llvm-c/IRReader.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/KECCAK.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +class LLDCTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + LLVMInitializeEVMAsmPrinter(); + + LLVMTargetRef Target = 0; + const char *Triple = "evm"; + char *ErrMsg = 0; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + + Context = LLVMContextCreate(); + } + + void TearDown() override { + LLVMDisposeTargetMachine(TM); + LLVMContextDispose(Context); + } + +public: + LLVMTargetMachineRef TM; + LLVMContextRef Context; +}; + +TEST_F(LLDCTest, IterativeLinkage) { + StringRef DeployIr = "\ +target datalayout = \"E-p:256:256-i256:256:256-S256-a:256:256\" \n\ +target triple = \"evm\" \n\ +declare i256 @llvm.evm.datasize(metadata) \n\ +declare i256 @llvm.evm.dataoffset(metadata) \n\ +declare i256 @llvm.evm.linkersymbol(metadata) \n\ + \n\ +define i256 @foo() { \n\ + %res = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ + ret i256 %res \n\ +} \n\ + \n\ +define i256 @bar() { \n\ + %linkersym = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) \n\ + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !2) \n\ + %tmp = add i256 %datasize, %dataoffset \n\ + %res = add i256 %tmp, %linkersym \n\ + ret i256 %res \n\ +} \n\ +!1 = !{!\"library_id\"} \n\ +!2 = !{!\"Test_26_deployed\"}"; + + StringRef DeployedIr = "\ +target datalayout = \"E-p:256:256-i256:256:256-S256-a:256:256\" \n\ +target triple = \"evm\" \n\ +declare i256 @llvm.evm.linkersymbol(metadata) \n\ + \n\ +define i256 @foo() { \n\ + %res = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ + ret i256 %res \n\ +} \n\ +!1 = !{!\"library_id2\"}"; + + // Wrap Source in a MemoryBuffer + LLVMMemoryBufferRef DeployIrMemBuffer = LLVMCreateMemoryBufferWithMemoryRange( + DeployIr.data(), DeployIr.size(), "deploy", 1); + char *ErrMsg = nullptr; + LLVMModuleRef DeployMod; + if (LLVMParseIRInContext(Context, DeployIrMemBuffer, &DeployMod, &ErrMsg)) { + FAIL() << "Failed to parse llvm ir:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + LLVMMemoryBufferRef DeployedIrMemBuffer = + LLVMCreateMemoryBufferWithMemoryRange(DeployedIr.data(), + DeployedIr.size(), "deploy", 1); + LLVMModuleRef DeployedMod; + if (LLVMParseIRInContext(Context, DeployedIrMemBuffer, &DeployedMod, + &ErrMsg)) { + FAIL() << "Failed to parse llvm ir:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + // Run CodeGen to produce the buffers. + LLVMMemoryBufferRef DeployObjMemBuffer; + LLVMMemoryBufferRef DeployedObjMemBuffer; + if (LLVMTargetMachineEmitToMemoryBuffer(TM, DeployMod, LLVMObjectFile, + &ErrMsg, &DeployObjMemBuffer)) { + FAIL() << "Failed to compile llvm ir:" << ErrMsg; + LLVMDisposeModule(DeployMod); + LLVMDisposeMessage(ErrMsg); + return; + } + if (LLVMTargetMachineEmitToMemoryBuffer(TM, DeployedMod, LLVMObjectFile, + &ErrMsg, &DeployedObjMemBuffer)) { + FAIL() << "Failed to compile llvm ir:" << ErrMsg; + LLVMDisposeModule(DeployedMod); + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(LLVMIsELFEVM(DeployObjMemBuffer)); + EXPECT_TRUE(LLVMIsELFEVM(DeployedObjMemBuffer)); + + const char *LinkerSymbol[2] = {"library_id", "library_id2"}; + const char LinkerSymbolVal[2][20] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; + StringRef SymVal1(LinkerSymbolVal[0], 20); + StringRef SymVal2(LinkerSymbolVal[1], 20); + + std::array InMemBuf = {DeployObjMemBuffer, + DeployedObjMemBuffer}; + std::array OutMemBuf = {nullptr, nullptr}; + const char *InIDs[] = {"Test_26", "Test_26_deployed"}; + + // No linker symbol definitions are provided, so we have to receive two ELF + // object files. + if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), nullptr, nullptr, + 0, &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[0])); + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[1])); + + char **UndefLinkerSyms = nullptr; + uint64_t NumLinkerUndefs = 0; + + LLVMGetUndefinedReferencesEVM(OutMemBuf[0], &UndefLinkerSyms, + &NumLinkerUndefs); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[0]) == 0)); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + LLVMGetUndefinedReferencesEVM(OutMemBuf[1], &UndefLinkerSyms, + &NumLinkerUndefs); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[1]) == 0)); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + InMemBuf.swap(OutMemBuf); + LLVMDisposeMemoryBuffer(OutMemBuf[0]); + LLVMDisposeMemoryBuffer(OutMemBuf[1]); + + // The first linker symbol definitions is provided, so we still have to + // receive two ELF object files, because of the undefined second reference. + if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), LinkerSymbol, + LinkerSymbolVal, 1, &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[0])); + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[1])); + + LLVMGetUndefinedReferencesEVM(OutMemBuf[0], &UndefLinkerSyms, + &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 0); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + LLVMGetUndefinedReferencesEVM(OutMemBuf[1], &UndefLinkerSyms, + &NumLinkerUndefs); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[1]) == 0)); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + InMemBuf.swap(OutMemBuf); + LLVMDisposeMemoryBuffer(OutMemBuf[0]); + LLVMDisposeMemoryBuffer(OutMemBuf[1]); + + // Both linker symbol definitions are provided, so we have to receive + // bytecodes files. + if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), LinkerSymbol, + LinkerSymbolVal, 2, &ErrMsg)) { + FAIL() << "Failed to link:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + + EXPECT_TRUE(!LLVMIsELFEVM(OutMemBuf[0])); + EXPECT_TRUE(!LLVMIsELFEVM(OutMemBuf[1])); + + LLVMGetUndefinedReferencesEVM(OutMemBuf[0], &UndefLinkerSyms, + &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 0); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + LLVMGetUndefinedReferencesEVM(OutMemBuf[1], &UndefLinkerSyms, + &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 0); + LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + + StringRef DeployBin(LLVMGetBufferStart(OutMemBuf[0]), + LLVMGetBufferSize(OutMemBuf[0])); + StringRef DeployedBin(LLVMGetBufferStart(OutMemBuf[1]), + LLVMGetBufferSize(OutMemBuf[1])); + + EXPECT_TRUE(DeployBin.find(SymVal1) != StringRef::npos); + EXPECT_TRUE(DeployedBin.find(SymVal2) != StringRef::npos); + + for (unsigned I = 0; I < 2; ++I) { + LLVMDisposeMemoryBuffer(OutMemBuf[I]); + LLVMDisposeMemoryBuffer(InMemBuf[I]); + } +} diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 6d8073f7860f..a0ddd401f617 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1028,6 +1028,13 @@ enum { #include "ELFRelocs/Xtensa.def" }; + +// Special values for the st_other field in the symbol table entry for EVM. +enum { + // Symbol denotes a sub symbol of a wide reference. + STO_EVM_REFERENCE_SYMBOL = 0x80, +}; + #undef ELF_RELOC // Section header. diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index 0d8ecf00f5f5..86b424b2a7f0 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -262,4 +262,14 @@ def int_evm_dataoffset : DefaultAttrsIntrinsic< [IntrNoMem, IntrSpeculatable] >; + +// Linking of libraries. + +// Inserts a library address placeholder to be replaced with +// a library address at linkage time. +def int_evm_linkersymbol : DefaultAttrsIntrinsic< + [llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn] +>; + } // TargetPrefix = "evm" diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h index 784ea521f6d0..aa275fc61d96 100644 --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -364,7 +364,7 @@ class MCSymbolRefExpr : public MCExpr { VK_TPREL, VK_DTPREL, // EVM local begin - VK_EVM_DATA, + VK_EVM_DATA // EVM local end }; diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 811943dcd708..f76fc20e6570 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1452,6 +1452,11 @@ template Triple::ArchType ELFObjectFile::getArch() const { case ELF::EM_XTENSA: return Triple::xtensa; + // EVM local begin + case ELF::EM_EVM: + return Triple::evm; + // EVM local end + default: return Triple::UnknownArch; } diff --git a/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp b/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp index 9f5015b7f9bf..d7274c17f3f3 100644 --- a/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp +++ b/llvm/lib/Target/EVM/Disassembler/EVMDisassembler.cpp @@ -154,19 +154,24 @@ MCDisassembler::DecodeStatus EVMDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &CStream) const { - + Size = 0; if (Bytes.empty()) - return MCDisassembler::Fail; + return Fail; + + const size_t BytesNum = Bytes.size(); + for (Size = 1; Size <= 33; ++Size) { + if (Size > BytesNum) + break; - for (unsigned InstSize = 1; InstSize <= 33; ++InstSize) { - Size = InstSize; - APInt Insn(33 * 8, toHex(ArrayRef(Bytes.begin(), Bytes.begin() + InstSize)), + APInt Insn(33 * 8, toHex(ArrayRef(Bytes.begin(), Bytes.begin() + Size)), 16); - DecodeStatus Result = decodeInstruction(getDecoderTable(InstSize), Instr, - Insn, Address, this, STI); - if (Result != MCDisassembler::Fail) + DecodeStatus Result = decodeInstruction(getDecoderTable(Size), Instr, Insn, + Address, this, STI); + if (Result != Fail) return Result; } - return MCDisassembler::Fail; + // Need to decrement after the loop. + --Size; + return Fail; } diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index 999ac042d2bd..df5257c8a4bb 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -17,6 +17,7 @@ #include "MCTargetDesc/EVMTargetStreamer.h" #include "TargetInfo/EVMTargetInfo.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/MC/MCAsmInfo.h" @@ -40,6 +41,9 @@ class EVMAsmPrinter : public AsmPrinter { using VRegRCMap = DenseMap; VRegRCMap VRegMapping; + // Maps a linker symbol name to corresponding MCSymbol. + StringSet<> WideRelocSymbolsSet; + public: EVMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : AsmPrinter(TM, std::move(Streamer)) {} @@ -58,8 +62,11 @@ class EVMAsmPrinter : public AsmPrinter { bool isBlockOnlyReachableByFallthrough( const MachineBasicBlock *MBB) const override; + void emitEndOfAsmFile(Module &) override; + private: - void emitLinkerSymbol(const MachineInstr *MI); + void emitAssemblySymbol(const MachineInstr *MI); + void emitWideRelocatableSymbol(const MachineInstr *MI); }; } // end of anonymous namespace @@ -104,10 +111,13 @@ void EVMAsmPrinter::emitFunctionEntryLabel() { void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping, MF->getRegInfo()); - unsigned Opc = MI->getOpcode(); if (Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S) { - emitLinkerSymbol(MI); + emitAssemblySymbol(MI); + return; + } + if (Opc == EVM::LINKERSYMBOL_S) { + emitWideRelocatableSymbol(MI); return; } @@ -122,26 +132,81 @@ bool EVMAsmPrinter::isBlockOnlyReachableByFallthrough( return false; } -void EVMAsmPrinter::emitLinkerSymbol(const MachineInstr *MI) { +void EVMAsmPrinter::emitAssemblySymbol(const MachineInstr *MI) { MCSymbol *LinkerSymbol = MI->getOperand(0).getMCSymbol(); StringRef LinkerSymbolName = LinkerSymbol->getName(); unsigned Opc = MI->getOpcode(); assert(Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S); - std::string SymbolNameHash = EVM::getLinkerSymbolHash(LinkerSymbolName); - std::string DataSymbolNameHash = - (Opc == EVM::DATASIZE_S) ? EVM::getDataSizeSymbol(SymbolNameHash) - : EVM::getDataOffsetSymbol(SymbolNameHash); + std::string NameHash = EVM::getLinkerSymbolHash(LinkerSymbolName); + std::string SymbolNameHash = (Opc == EVM::DATASIZE_S) + ? EVM::getDataSizeSymbol(NameHash) + : EVM::getDataOffsetSymbol(NameHash); MCInst MCI; MCI.setOpcode(EVM::PUSH4_S); - MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_EVM_DATA; - MCOperand MCOp = MCOperand::createExpr( - MCSymbolRefExpr::create(DataSymbolNameHash, Kind, OutContext)); + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + SymbolNameHash, MCSymbolRefExpr::VariantKind::VK_EVM_DATA, OutContext)); MCI.addOperand(MCOp); EmitToStreamer(*OutStreamer, MCI); } +// Lowers LINKERSYMBOL_S instruction as shown below: +// +// LINKERSYMBOL_S @BaseSymbol +// -> +// PUSH20_S @"__linker_symbol__$KECCAK256(BaseSymbol)$__" +// +// .section ".symbol_name__$KECCAK256(BaseName)$__","S",@progbits +// .ascii "~ \".%%^ [];,<.>? .sol:GreaterHelper" + +void EVMAsmPrinter::emitWideRelocatableSymbol(const MachineInstr *MI) { + constexpr unsigned LinkerSymbolSize = 20; + MCSymbol *BaseSymbol = MI->getOperand(0).getMCSymbol(); + StringRef BaseSymbolName = BaseSymbol->getName(); + std::string BaseSymbolNameHash = EVM::getLinkerSymbolHash(BaseSymbolName); + std::string SymbolName = EVM::getLinkerSymbolName(BaseSymbolNameHash); + + MCInst MCI; + // This represents a library address symbol which is 20-bytes wide. + MCI.setOpcode(EVM::PUSH20_S); + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + BaseSymbolNameHash, MCSymbolRefExpr::VariantKind::VK_EVM_DATA, + OutContext)); + MCI.addOperand(MCOp); + + if (!WideRelocSymbolsSet.contains(SymbolName)) { + for (unsigned Idx = 0; Idx < LinkerSymbolSize / sizeof(uint32_t); ++Idx) { + std::string SubSymName = EVM::getSymbolIndexedName(SymbolName, Idx); + if (OutContext.lookupSymbol(SubSymName)) + report_fatal_error(Twine("MC: duplicating reference sub-symbol ") + + SubSymName); + } + } + + auto *TS = static_cast(OutStreamer->getTargetStreamer()); + TS->emitWideRelocatableSymbol(MCI, SymbolName, LinkerSymbolSize); + + // The linker symbol and the related section already exist, so just exit. + if (WideRelocSymbolsSet.contains(SymbolName)) + return; + + WideRelocSymbolsSet.insert(SymbolName); + + MCSection *CurrentSection = OutStreamer->getCurrentSectionOnly(); + + // Emit the .symbol_name section that contains the actual symbol + // name. + std::string SymbolSectionName = EVM::getSymbolSectionName(SymbolName); + MCSection *SymbolSection = OutContext.getELFSection( + SymbolSectionName, ELF::SHT_PROGBITS, ELF::SHF_STRINGS); + OutStreamer->switchSection(SymbolSection); + OutStreamer->emitBytes(BaseSymbolName); + OutStreamer->switchSection(CurrentSection); +} + +void EVMAsmPrinter::emitEndOfAsmFile(Module &) { WideRelocSymbolsSet.clear(); } + 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 7f83fb39adef..4ea3ea26464b 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -153,17 +153,21 @@ SDValue EVMTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, default: return SDValue(); case Intrinsic::evm_datasize: - case Intrinsic::evm_dataoffset: { + case Intrinsic::evm_dataoffset: + case Intrinsic::evm_linkersymbol: { 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(); MCSymbol *Sym = MF.getContext().getOrCreateSymbol(ContractID); - unsigned Opc = - (IntrID == Intrinsic::evm_datasize) ? EVM::DATASIZE : EVM::DATAOFFSET; - return SDValue( - DAG.getMachineNode(Opc, DL, Ty, DAG.getMCSymbol(Sym, MVT::i256)), 0); + DenseMap OpcMap = { + {Intrinsic::evm_datasize, EVM::DATASIZE}, + {Intrinsic::evm_dataoffset, EVM::DATAOFFSET}, + {Intrinsic::evm_linkersymbol, EVM::LINKERSYMBOL}}; + return SDValue(DAG.getMachineNode(OpcMap.at(IntrID), DL, Ty, + DAG.getMCSymbol(Sym, MVT::i256)), + 0); } break; } } diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index fe34e814aff9..bc8a1f0a4502 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -1122,12 +1122,19 @@ def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32 $imm", } // Pseudo instructions for linkage -let isCodeGenOnly = 1, BaseName = "DATASIZE" in { - def DATASIZE : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; - def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; -} +let isAsCheapAsAMove = 1, isReMaterializable = 1, isCodeGenOnly = 1, hasSideEffects = 0 in { + let BaseName = "DATASIZE" in { + def DATASIZE : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; + } -let isCodeGenOnly = 1, BaseName = "DATAOFFSET" in { - def DATAOFFSET : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; - def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; + let BaseName = "DATAOFFSET" in { + def DATAOFFSET : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; + def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; + } + + let BaseName = "LINKERSYMBOL" in { + def LINKERSYMBOL : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; + def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>; + } } diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index 3278761f0d95..d87599be0ae8 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -28,7 +28,6 @@ using namespace llvm; extern cl::opt EVMKeepRegisters; // 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; diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp index 658a617b81e0..47c4c83b9d8b 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.cpp @@ -28,4 +28,6 @@ EVMMCAsmInfo::EVMMCAsmInfo(const Triple &TT) { SupportsDebugInformation = true; } -bool EVMMCAsmInfo::shouldOmitSectionDirective(StringRef) const { return true; } +bool EVMMCAsmInfo::shouldOmitSectionDirective(StringRef Name) const { + return !Name.starts_with(".symbol_name"); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp index 6d0d503d3d8a..586d6612eaa8 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -51,19 +51,14 @@ class EVMMCCodeEmitter final : public MCCodeEmitter { }; EVM::Fixups getFixupForOpc(unsigned Opcode, MCSymbolRefExpr::VariantKind Kind) { + if (Kind == MCSymbolRefExpr::VariantKind::VK_EVM_DATA) + return EVM::fixup_Data_i32; + switch (Opcode) { default: llvm_unreachable("Unexpected MI for the SymbolRef MO"); case EVM::PUSH4_S: - 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; + return EVM::fixup_SecRel_i32; case EVM::PUSH3_S: return EVM::fixup_SecRel_i24; case EVM::PUSH2_S: diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp index b4b701f6b66c..7616c7e97981 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -21,6 +21,7 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/KECCAK.h" +#include "llvm/Support/Regex.h" using namespace llvm; @@ -125,6 +126,33 @@ std::string EVM::getLinkerSymbolHash(StringRef SymName) { return (Twine("__$") + HexHash + "$__").str(); } +// Returns concatenation of the \p Name with the \p SubIdx. +std::string EVM::getSymbolIndexedName(StringRef Name, unsigned SubIdx) { + return (Twine(Name) + std::to_string(SubIdx)).str(); +} + +// Returns concatenation of '.symbol_name' with the \p Name. +std::string EVM::getSymbolSectionName(StringRef Name) { + return (Twine(".symbol_name") + Name).str(); +} + +// Strips index from the \p Name. +std::string EVM::getNonIndexedSymbolName(StringRef Name) { + Regex suffixRegex(R"(.*[0-4]$)"); + if (!suffixRegex.match(Name)) + llvm_unreachable("Unexpected indexed symbol name"); + + return Name.drop_back().str(); +} + +std::string EVM::getLinkerSymbolName(StringRef Name) { + return (Twine("__linker_symbol") + Name).str(); +} + +bool EVM::isLinkerSymbolName(StringRef Name) { + return Name.find("__linker_symbol") == 0; +} + std::string EVM::getDataSizeSymbol(StringRef SymbolName) { return (Twine("__datasize") + SymbolName).str(); } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h index 55463908ee02..1f51fa863694 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h @@ -39,6 +39,11 @@ std::unique_ptr createEVMELFObjectWriter(uint8_t OSABI); namespace EVM { std::string getLinkerSymbolHash(StringRef SymName); +std::string getSymbolIndexedName(StringRef Name, unsigned SubIdx); +std::string getSymbolSectionName(StringRef Name); +std::string getNonIndexedSymbolName(StringRef Name); +std::string getLinkerSymbolName(StringRef Name); +bool isLinkerSymbolName(StringRef Name); std::string getDataSizeSymbol(StringRef SymbolName); std::string getDataOffsetSymbol(StringRef SymbolName); } // namespace EVM diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp index b601421607e9..5f717cd2a7b6 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp @@ -11,7 +11,14 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/EVMTargetStreamer.h" +#include "EVMFixupKinds.h" +#include "EVMMCTargetDesc.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCFragment.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/Support/Casting.h" @@ -28,12 +35,58 @@ void EVMTargetStreamer::emitLabel(MCSymbol *Symbol) { ELFSymbol->setBinding(ELF::STB_LOCAL); } -EVMTargetObjStreamer::EVMTargetObjStreamer(MCStreamer &S) +EVMTargetAsmStreamer::EVMTargetAsmStreamer(MCStreamer &S) : EVMTargetStreamer(S) {} -EVMTargetObjStreamer::~EVMTargetObjStreamer() = default; +void EVMTargetAsmStreamer::emitWideRelocatableSymbol(const MCInst &PushInst, + StringRef SymbolName, + unsigned SymbolSize) { + MCContext &Ctx = Streamer.getContext(); + const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); + Streamer.emitInstruction(PushInst, *STI); +} -EVMTargetAsmStreamer::EVMTargetAsmStreamer(MCStreamer &S) +EVMTargetObjStreamer::EVMTargetObjStreamer(MCStreamer &S) : EVMTargetStreamer(S) {} -EVMTargetAsmStreamer::~EVMTargetAsmStreamer() = default; +// Emits a PUSH instruction with relocatable symbol of the size up to 32 bytes. +// The symbol value is represented as an array of 4-byte relocatable +// sub-symbols. +void EVMTargetObjStreamer::emitWideRelocatableSymbol(const MCInst &PushInst, + StringRef SymbolName, + unsigned SymbolSize) { + if (SymbolSize > 32) + report_fatal_error("MC: relocatable symbol size exceeds 32 bytes"); + + // The code below is based on the MCObjectStreamer::emitInstToFragment() + // implementation. + MCContext &Ctx = Streamer.getContext(); + const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); + auto &S = static_cast(Streamer); + + auto *DF = new MCDataFragment(); + S.insert(DF); + SmallString<128> Code; + S.getAssembler().getEmitter().encodeInstruction(PushInst, Code, + DF->getFixups(), *STI); + // Remove a fixup corresponding to the initial symbol operand. + DF->getFixups().clear(); + DF->getContents().append(Code.begin(), Code.end()); + + // Emit 4-byte fixups to cover a wide symbol value. + assert(DF->getContents().size() == SymbolSize + 1 /* opcode byte */); + assert(!((DF->getContents().size() - 1) % 4)); + for (unsigned Idx = 0; Idx < SymbolSize / sizeof(uint32_t); ++Idx) { + std::string SubSymName = EVM::getSymbolIndexedName(SymbolName, Idx); + auto *Sym = cast(Ctx.getOrCreateSymbol(SubSymName)); + Sym->setOther(ELF::STO_EVM_REFERENCE_SYMBOL); + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + S.visitUsedExpr(*Expr); + + assert(SymbolSize > Idx * 4); + // The byte index of start of the relocation is always 1, as + // we need to skip the instruction opcode which is always one byte. + constexpr auto FK = static_cast(EVM::fixup_Data_i32); + DF->getFixups().push_back(MCFixup::create((Idx * 4) + 1, Expr, FK)); + } +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h index a09b7b8c86c8..b6eca5053a63 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.h @@ -26,6 +26,10 @@ class EVMTargetStreamer : public MCTargetStreamer { EVMTargetStreamer &operator=(EVMTargetStreamer &&) = delete; void emitLabel(MCSymbol *Symbol) override; + + virtual void emitWideRelocatableSymbol(const MCInst &PushInst, + StringRef SymbolName, + unsigned SymbolSize) {}; }; /// This part is for ASCII assembly output @@ -36,7 +40,9 @@ class EVMTargetAsmStreamer final : public EVMTargetStreamer { EVMTargetAsmStreamer(EVMTargetAsmStreamer &&) = delete; EVMTargetAsmStreamer &operator=(const EVMTargetAsmStreamer &) = delete; EVMTargetAsmStreamer &operator=(EVMTargetAsmStreamer &&) = delete; - ~EVMTargetAsmStreamer() override; + + void emitWideRelocatableSymbol(const MCInst &PushInst, StringRef SymbolName, + unsigned SymbolSize) override; }; // This part is for EVM object output @@ -47,7 +53,9 @@ class EVMTargetObjStreamer final : public EVMTargetStreamer { EVMTargetObjStreamer(EVMTargetObjStreamer &&) = delete; EVMTargetObjStreamer &operator=(const EVMTargetObjStreamer &) = delete; EVMTargetObjStreamer &operator=(EVMTargetObjStreamer &&) = delete; - ~EVMTargetObjStreamer() override; + + void emitWideRelocatableSymbol(const MCInst &PushInst, StringRef SymbolName, + unsigned SymbolSize) override; }; } // namespace llvm diff --git a/llvm/test/MC/EVM/data-linker-symbol-relocs.ll b/llvm/test/MC/EVM/data-linker-symbol-relocs.ll index 8c0fb82b1e84..efac51802a69 100644 --- a/llvm/test/MC/EVM/data-linker-symbol-relocs.ll +++ b/llvm/test/MC/EVM/data-linker-symbol-relocs.ll @@ -1,11 +1,23 @@ -; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump -r - | FileCheck %s +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump -r --headers - | FileCheck %s + +; CHECK: .symbol_name__linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__ 0000000b ; CHECK: RELOCATION RECORDS FOR [.text]: ; CHECK-NEXT: OFFSET TYPE VALUE -; CHECK-NEXT: {{\d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ -; CHECK-NEXT: {{\d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ -; CHECK-NEXT: {{\d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ -; CHECK-NEXT: {{\d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__0 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__1 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__2 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__3 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__4 +; CHECK-NEXT: {{d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__0 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__1 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__2 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__3 +; CHECK-NEXT: {{d*}} R_EVM_DATA __linker_symbol__$65c539c969b08d62684b6c973d3f0f4f37a85c2d0cbe39fffa3022cb69aac23a$__4 +; CHECK-NEXT: {{d*}} R_EVM_DATA __dataoffset__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ +; CHECK-NEXT: {{d*}} R_EVM_DATA __datasize__$0336fd807c0716a535e520df5b63ecc41ba7984875fdfa2241fcf3c8d0107e26$__ ; TODO: CRP-1575. Rewrite the test in assembly. target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" @@ -14,12 +26,15 @@ target triple = "evm-unknown-unknown" ; Function Attrs: nounwind declare i256 @llvm.evm.datasize(metadata) declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.linkersymbol(metadata) define i256 @foo() { entry: %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) - %res = add i256 %deployed_size, %deployed_off + %lib_addr = call i256 @llvm.evm.linkersymbol(metadata !2) + %tmp = sub i256 %deployed_size, %deployed_off + %res = sub i256 %tmp, %lib_addr ret i256 %res } @@ -27,8 +42,11 @@ define i256 @bar() { entry: %deployed_size = tail call i256 @llvm.evm.datasize(metadata !1) %deployed_off = tail call i256 @llvm.evm.dataoffset(metadata !1) - %res = sub i256 %deployed_size, %deployed_off + %lib_addr = call i256 @llvm.evm.linkersymbol(metadata !2) + %tmp = sub i256 %deployed_size, %deployed_off + %res = sub i256 %tmp, %lib_addr ret i256 %res } !1 = !{!"D_105_deployed"} +!2 = !{!"library_id2"} diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index a0f164cb7359..e578217463d1 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1749,6 +1749,11 @@ const EnumEntry ElfMips16SymOtherFlags[] = { const EnumEntry ElfRISCVSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STO_RISCV_VARIANT_CC)}; +// EVM local begin +const EnumEntry ElfEVMSymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_EVM_REFERENCE_SYMBOL)}; +// EVM local end + static const char *getElfMipsOptionsOdkType(unsigned Odk) { switch (Odk) { LLVM_READOBJ_ENUM_CASE(ELF, ODK_NULL); @@ -3488,6 +3493,11 @@ ELFDumper::getOtherFlagsFromSymbol(const Elf_Ehdr &Header, } else if (Header.e_machine == EM_RISCV) { SymOtherFlags.insert(SymOtherFlags.end(), std::begin(ElfRISCVSymOtherFlags), std::end(ElfRISCVSymOtherFlags)); + // EVM local begin + } else if (Header.e_machine == EM_EVM) { + SymOtherFlags.insert(SymOtherFlags.end(), std::begin(ElfEVMSymOtherFlags), + std::end(ElfEVMSymOtherFlags)); + // EVM local end } return SymOtherFlags; } @@ -4302,6 +4312,12 @@ void GNUELFDumper::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, Fields[5].Str.append(" | " + utohexstr(Other, /*LowerCase=*/true)); Fields[5].Str.append("]"); } + // EVM local begin + } else if (this->Obj.getHeader().e_machine == ELF::EM_EVM) { + uint8_t Other = Symbol.st_other & ~0x3; + if (Other == STO_EVM_REFERENCE_SYMBOL) + Fields[5].Str += " [REFERENCE_SYMBOL]"; + // EVM local end } else { Fields[5].Str += " []"; From 07b34294e6c3a459214f9277ddc0fe2f1bb582e4 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Tue, 28 Jan 2025 01:47:25 +0100 Subject: [PATCH 084/203] [EVM][LLD] relocate: remove wrong numeric limit --- lld/ELF/Arch/EVM.cpp | 4 +--- lld/unittests/EVM/LLDTest.cpp | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lld/ELF/Arch/EVM.cpp b/lld/ELF/Arch/EVM.cpp index ac2ab5ff9f25..a61dab9e1be3 100644 --- a/lld/ELF/Arch/EVM.cpp +++ b/lld/ELF/Arch/EVM.cpp @@ -60,9 +60,7 @@ RelExpr EVM::getRelExpr(RelType type, const Symbol &s, 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); + write32be(loc, static_cast(val)); break; } default: diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp index 3aa9bb2a62b8..eda6b0077e76 100644 --- a/lld/unittests/EVM/LLDTest.cpp +++ b/lld/unittests/EVM/LLDTest.cpp @@ -135,7 +135,8 @@ define i256 @foo() { \n\ const char *LinkerSymbol[2] = {"library_id", "library_id2"}; const char LinkerSymbolVal[2][20] = { {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; + {'\x9f', 0x1E, '\xBB', '\xF1', 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; StringRef SymVal1(LinkerSymbolVal[0], 20); StringRef SymVal2(LinkerSymbolVal[1], 20); From 984cd174df49924ca4cf76aa1d61237da2771fe8 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Sat, 1 Feb 2025 01:27:01 +0100 Subject: [PATCH 085/203] [EVM] Add support of immutables This includes: - New LLVM IR intrinsic / instruction - Its lowering to MC Layer - C-API for retrieval of immutables from ELF file --- lld/include/lld-c/LLDAsLibraryC.h | 11 +++ lld/lld-c/LLDAsLibraryC.cpp | 68 +++++++++++++++++++ lld/unittests/EVM/LLDTest.cpp | 21 +++++- llvm/include/llvm/IR/IntrinsicsEVM.td | 7 ++ llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 42 +++++++++++- llvm/lib/Target/EVM/EVMISelLowering.cpp | 2 + llvm/lib/Target/EVM/EVMInstrInfo.td | 5 ++ llvm/lib/Target/EVM/EVMMCInstLower.cpp | 3 +- .../EVM/MCTargetDesc/EVMMCTargetDesc.cpp | 29 +++++++- .../Target/EVM/MCTargetDesc/EVMMCTargetDesc.h | 3 + .../test/MC/EVM/immutable-symbols-clashing.ll | 15 ++++ llvm/test/MC/EVM/immutable-symbols.ll | 22 ++++++ 12 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 llvm/test/MC/EVM/immutable-symbols-clashing.ll create mode 100644 llvm/test/MC/EVM/immutable-symbols.ll diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h index 4327e4f052c3..a0640b9be73c 100644 --- a/lld/include/lld-c/LLDAsLibraryC.h +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -114,6 +114,17 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef *inBuffers, const char *inBuffersIDs[], const char *const *linkerSymbolNames, const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], uint64_t numLinkerSymbols, char **errorMessage); + +/// Returns immutables and their offsets of the ELF object +/// file passed in \p inBuffer. +uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, + char ***immutableIDs, + uint64_t **immutableOffsets); + +/// Disposes immutable names and their offsets returned by the +/// LLVMGetImmutablesEVM. +void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, + uint64_t numOfImmutables); 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 64dbf87cbe6b..393c25cc51d0 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -38,6 +38,8 @@ std::string getDataSizeSymbol(StringRef SymbolName); std::string getDataOffsetSymbol(StringRef SymbolName); std::string getNonIndexedSymbolName(StringRef Name); bool isLinkerSymbolName(StringRef Name); +bool isLoadImmutableSymbolName(StringRef Name); +std::string getImmutableId(StringRef Name); } // namespace EVM } // namespace llvm @@ -554,3 +556,69 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], return false; } + +/// Returns immutables and their offsets of the ELF object +/// file passed in \p inBuffer. +uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, + char ***immutableIDs, + uint64_t **immutableOffsets) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + const auto *objFile = static_cast(inBinary.get()); + + // Maps immutable IDs to their references in the object code. + StringMap> immutablesMap; + for (const SymbolRef &sym : objFile->symbols()) { + section_iterator symSec = cantFail(sym.getSection()); + if (symSec == objFile->section_end()) + continue; + + StringRef symName = cantFail(sym.getName()); + if (EVM::isLoadImmutableSymbolName(symName)) { + std::string Id = EVM::getImmutableId(symName); + uint64_t symOffset = cantFail(sym.getValue()); + // The symbol points to the beginning of a PUSH32 instruction. + // We have to add 1 (opcode size) to get offset to the PUSH32 + // instruction operand. + immutablesMap[Id].push_back(symOffset + 1); + } + } + + if (immutablesMap.empty()) { + *immutableIDs = nullptr; + *immutableOffsets = nullptr; + return 0; + } + + uint64_t numOfImmutables = 0; + for (const auto &[id, offsets] : immutablesMap) { + numOfImmutables += offsets.size(); + }; + + *immutableIDs = + reinterpret_cast(std::malloc(numOfImmutables * sizeof(char *))); + *immutableOffsets = reinterpret_cast( + std::malloc(numOfImmutables * sizeof(uint64_t))); + + unsigned idx = 0; + for (const auto &[id, offsets] : immutablesMap) { + for (uint64_t offset : offsets) { + assert(idx < numOfImmutables); + (*immutableIDs)[idx] = strdup(id.str().c_str()); + (*immutableOffsets)[idx++] = offset; + } + } + + return numOfImmutables; +} + +/// Disposes immutable names and their offsets returned by the +/// LLVMGetImmutablesEVM. +void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, + uint64_t numOfImmutables) { + for (unsigned idx = 0; idx < numOfImmutables; ++idx) + std::free(immutableIDs[idx]); + + std::free(immutableIDs); + std::free(immutableOffsets); +} diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp index eda6b0077e76..818bb9eee8c2 100644 --- a/lld/unittests/EVM/LLDTest.cpp +++ b/lld/unittests/EVM/LLDTest.cpp @@ -64,7 +64,7 @@ declare i256 @llvm.evm.linkersymbol(metadata) \n\ \n\ define i256 @foo() { \n\ %res = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ - ret i256 %res \n\ + ret i256 %res \n\ } \n\ \n\ define i256 @bar() { \n\ @@ -82,12 +82,16 @@ define i256 @bar() { \n\ target datalayout = \"E-p:256:256-i256:256:256-S256-a:256:256\" \n\ target triple = \"evm\" \n\ declare i256 @llvm.evm.linkersymbol(metadata) \n\ +declare i256 @llvm.evm.loadimmutable(metadata) \n\ \n\ define i256 @foo() { \n\ %res = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ - ret i256 %res \n\ + %res2 = call i256 @llvm.evm.loadimmutable(metadata !2) \n\ + %res3 = add i256 %res, %res2 \n\ + ret i256 %res3 \n\ } \n\ -!1 = !{!\"library_id2\"}"; +!1 = !{!\"library_id2\"} \n\ +!2 = !{!\"id\"}"; // Wrap Source in a MemoryBuffer LLVMMemoryBufferRef DeployIrMemBuffer = LLVMCreateMemoryBufferWithMemoryRange( @@ -145,6 +149,17 @@ define i256 @foo() { \n\ std::array OutMemBuf = {nullptr, nullptr}; const char *InIDs[] = {"Test_26", "Test_26_deployed"}; + // Check load immutable references + { + char **ImmutableIDs = nullptr; + uint64_t *ImmutableOffsets = nullptr; + uint64_t ImmCount = + LLVMGetImmutablesEVM(InMemBuf[1], &ImmutableIDs, &ImmutableOffsets); + EXPECT_TRUE(ImmCount == 1); + EXPECT_TRUE(std::strcmp(ImmutableIDs[0], "id") == 0); + LLVMDisposeImmutablesEVM(ImmutableIDs, ImmutableOffsets, ImmCount); + } + // No linker symbol definitions are provided, so we have to receive two ELF // object files. if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), nullptr, nullptr, diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index 86b424b2a7f0..fbf81c3bcbf1 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -272,4 +272,11 @@ def int_evm_linkersymbol : DefaultAttrsIntrinsic< [IntrNoMem, IntrWillReturn] >; +// Immutables support. + +// Loads immutable value. Should be used only in runtime code. +def int_evm_loadimmutable : DefaultAttrsIntrinsic< + [llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn] +>; } // TargetPrefix = "evm" diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index df5257c8a4bb..b8536b8e351c 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -17,6 +17,7 @@ #include "MCTargetDesc/EVMTargetStreamer.h" #include "TargetInfo/EVMTargetInfo.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -43,6 +44,7 @@ class EVMAsmPrinter : public AsmPrinter { // Maps a linker symbol name to corresponding MCSymbol. StringSet<> WideRelocSymbolsSet; + StringMap ImmutablesMap; public: EVMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -67,6 +69,7 @@ class EVMAsmPrinter : public AsmPrinter { private: void emitAssemblySymbol(const MachineInstr *MI); void emitWideRelocatableSymbol(const MachineInstr *MI); + void emitLoadImmutableLabel(const MachineInstr *MI); }; } // end of anonymous namespace @@ -120,6 +123,10 @@ void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { emitWideRelocatableSymbol(MI); return; } + if (Opc == EVM::LOADIMMUTABLE_S) { + emitLoadImmutableLabel(MI); + return; + } MCInst TmpInst; MCInstLowering.Lower(MI, TmpInst); @@ -132,6 +139,36 @@ bool EVMAsmPrinter::isBlockOnlyReachableByFallthrough( return false; } +// Lowers LOADIMMUTABLE_S as show below: +// LOADIMMUTABLE_S @immutable_id +// -> +// __load_immutable__immutable_id.N: +// PUSH32 0 +// +// where N = 1 ... 'number of @immutable_id +// references in the current module'. +// +void EVMAsmPrinter::emitLoadImmutableLabel(const MachineInstr *MI) { + assert(MI->getOpcode() == EVM::LOADIMMUTABLE_S); + + const MCSymbol *Symbol = MI->getOperand(0).getMCSymbol(); + StringRef ImmutableId = Symbol->getName(); + std::string LoadImmutableLabel = + EVM::getLoadImmutableSymbol(ImmutableId, ++ImmutablesMap[ImmutableId]); + if (OutContext.lookupSymbol(LoadImmutableLabel)) + report_fatal_error(Twine("MC: duplicating immutable label ") + + LoadImmutableLabel); + + MCSymbol *Sym = OutContext.getOrCreateSymbol(LoadImmutableLabel); + // Emit load immutable label right before PUSH32 instruction. + OutStreamer->emitLabel(Sym); + + MCInst MCI; + MCI.setOpcode(EVM::PUSH32_S); + MCI.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, MCI); +} + void EVMAsmPrinter::emitAssemblySymbol(const MachineInstr *MI) { MCSymbol *LinkerSymbol = MI->getOperand(0).getMCSymbol(); StringRef LinkerSymbolName = LinkerSymbol->getName(); @@ -205,7 +242,10 @@ void EVMAsmPrinter::emitWideRelocatableSymbol(const MachineInstr *MI) { OutStreamer->switchSection(CurrentSection); } -void EVMAsmPrinter::emitEndOfAsmFile(Module &) { WideRelocSymbolsSet.clear(); } +void EVMAsmPrinter::emitEndOfAsmFile(Module &) { + WideRelocSymbolsSet.clear(); + ImmutablesMap.clear(); +} 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 4ea3ea26464b..d7e02f653c2a 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -154,6 +154,7 @@ SDValue EVMTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, return SDValue(); case Intrinsic::evm_datasize: case Intrinsic::evm_dataoffset: + case Intrinsic::evm_loadimmutable: case Intrinsic::evm_linkersymbol: { const SDLoc DL(Op); EVT Ty = Op.getValueType(); @@ -164,6 +165,7 @@ SDValue EVMTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, DenseMap OpcMap = { {Intrinsic::evm_datasize, EVM::DATASIZE}, {Intrinsic::evm_dataoffset, EVM::DATAOFFSET}, + {Intrinsic::evm_loadimmutable, EVM::LOADIMMUTABLE}, {Intrinsic::evm_linkersymbol, EVM::LINKERSYMBOL}}; return SDValue(DAG.getMachineNode(OpcMap.at(IntrID), DL, Ty, DAG.getMCSymbol(Sym, MVT::i256)), diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index bc8a1f0a4502..b2d3b9691436 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -1137,4 +1137,9 @@ let isAsCheapAsAMove = 1, isReMaterializable = 1, isCodeGenOnly = 1, hasSideEffe def LINKERSYMBOL : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>; } + + let BaseName = "LOADIMMUTABLE" in { + def LOADIMMUTABLE : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; + def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>; + } } diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index d87599be0ae8..bd896467552b 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -127,7 +127,8 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { #ifndef NDEBUG unsigned Opc = MI->getOpcode(); // We handle the linkage-related instructions in the EVMAsmPrinter. - assert(Opc != EVM::DATASIZE_S && Opc != EVM::DATAOFFSET_S); + assert(Opc != EVM::DATASIZE_S && Opc != EVM::DATAOFFSET_S && + Opc != EVM::LINKERSYMBOL_S && Opc != EVM::LOADIMMUTABLE_S); #endif // NDEBUG MCOp = MCOperand::createExpr( diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp index 7616c7e97981..a721a9eb03bc 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -140,7 +140,7 @@ std::string EVM::getSymbolSectionName(StringRef Name) { std::string EVM::getNonIndexedSymbolName(StringRef Name) { Regex suffixRegex(R"(.*[0-4]$)"); if (!suffixRegex.match(Name)) - llvm_unreachable("Unexpected indexed symbol name"); + report_fatal_error("Unexpected indexed symbol name"); return Name.drop_back().str(); } @@ -160,3 +160,30 @@ std::string EVM::getDataSizeSymbol(StringRef SymbolName) { std::string EVM::getDataOffsetSymbol(StringRef SymbolName) { return (Twine("__dataoffset") + SymbolName).str(); } + +std::string EVM::getLoadImmutableSymbol(StringRef Name, unsigned Idx) { + return (Twine("__load_immutable__") + Name + "." + std::to_string(Idx)).str(); +} + +bool EVM::isLoadImmutableSymbolName(StringRef Name) { + return Name.find("__load_immutable__") == 0; +} + +// extract immutable ID from the load immutable symbol name. +// '__load_immutable__ID.N' -> 'ID'. +std::string EVM::getImmutableId(StringRef Name) { + SmallVector Matches; + Regex suffixRegex(R"(\.[0-9]+$)"); + if (!suffixRegex.match(Name, &Matches)) + report_fatal_error("No immutable symbol index"); + assert(Matches.size() == 1); + + // Strip suffix + Name.consume_back(Matches[0]); + + // Strip prefix + if (!Name.consume_front("__load_immutable__")) + report_fatal_error("Unexpected load immutable symbol format"); + + return Name.str(); +} diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h index 1f51fa863694..d72cbd61a189 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h @@ -46,6 +46,9 @@ std::string getLinkerSymbolName(StringRef Name); bool isLinkerSymbolName(StringRef Name); std::string getDataSizeSymbol(StringRef SymbolName); std::string getDataOffsetSymbol(StringRef SymbolName); +std::string getLoadImmutableSymbol(StringRef Name, unsigned Idx); +bool isLoadImmutableSymbolName(StringRef Name); +std::string getImmutableId(StringRef Name); } // namespace EVM } // namespace llvm diff --git a/llvm/test/MC/EVM/immutable-symbols-clashing.ll b/llvm/test/MC/EVM/immutable-symbols-clashing.ll new file mode 100644 index 000000000000..933f6eedc833 --- /dev/null +++ b/llvm/test/MC/EVM/immutable-symbols-clashing.ll @@ -0,0 +1,15 @@ +; RUN: not --crash llc -O2 -filetype=obj --mtriple=evm %s 2>&1 | FileCheck %s + +; CHECK: LLVM ERROR: MC: duplicating immutable label __load_immutable__imm_id.1 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @__load_immutable__imm_id.1() { + %ret = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %ret +} + +!1 = !{!"imm_id"} diff --git a/llvm/test/MC/EVM/immutable-symbols.ll b/llvm/test/MC/EVM/immutable-symbols.ll new file mode 100644 index 000000000000..0471b94f0897 --- /dev/null +++ b/llvm/test/MC/EVM/immutable-symbols.ll @@ -0,0 +1,22 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --syms - | FileCheck %s + +; CHECK: SYMBOL TABLE: +; CHECK: {{d*}} l .text 00000000 __load_immutable__imm_id.1 +; CHECK: {{d*}} l .text 00000000 __load_immutable__imm_id.2 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @foo() { + %ret = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %ret +} + +define i256 @bar() { + %ret = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %ret +} + +!1 = !{!"imm_id"} From 648a94c1360ce4e3740ce3ba7e683e725615c8e6 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 13 Mar 2025 12:43:51 +0200 Subject: [PATCH 086/203] [EVM] Add backward propagation stackification The patch implements backward propagation stackification similar to what is used in Ethereum Foundation Solidity. Co-authored-by: Pavel Kopyl Co-authored-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/CMakeLists.txt | 6 + llvm/lib/Target/EVM/EVM.h | 11 + llvm/lib/Target/EVM/EVMArgumentMove.cpp | 32 +- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 118 +++- .../EVMBackwardPropagationStackification.cpp | 96 +++ llvm/lib/Target/EVM/EVMInstrFormats.td | 9 +- llvm/lib/Target/EVM/EVMInstrInfo.cpp | 159 +++-- llvm/lib/Target/EVM/EVMInstrInfo.h | 14 + llvm/lib/Target/EVM/EVMInstrInfo.td | 65 +- llvm/lib/Target/EVM/EVMLinkRuntime.cpp | 9 - .../lib/Target/EVM/EVMSingleUseExpression.cpp | 7 +- llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp | 90 +++ llvm/lib/Target/EVM/EVMStackModel.cpp | 156 +++++ llvm/lib/Target/EVM/EVMStackModel.h | 338 ++++++++++ llvm/lib/Target/EVM/EVMStackShuffler.cpp | 283 ++++++++ llvm/lib/Target/EVM/EVMStackShuffler.h | 158 +++++ llvm/lib/Target/EVM/EVMStackSolver.cpp | 613 ++++++++++++++++++ llvm/lib/Target/EVM/EVMStackSolver.h | 105 +++ llvm/lib/Target/EVM/EVMStackify.cpp | 69 +- .../lib/Target/EVM/EVMStackifyCodeEmitter.cpp | 470 ++++++++++++++ llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h | 85 +++ llvm/lib/Target/EVM/EVMSubtarget.h | 3 +- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 20 +- llvm/lib/Target/EVM/EVMTargetTransformInfo.h | 5 +- .../Target/EVM/TargetInfo/EVMTargetInfo.cpp | 78 +++ .../lib/Target/EVM/TargetInfo/EVMTargetInfo.h | 2 + llvm/test/CodeGen/EVM/add.ll | 2 +- llvm/test/CodeGen/EVM/call.ll | 4 +- llvm/test/CodeGen/EVM/div.ll | 4 +- llvm/test/CodeGen/EVM/fallthrough.mir | 45 -- llvm/test/CodeGen/EVM/globals.ll | 2 +- llvm/test/CodeGen/EVM/intrinsic.ll | 112 ++-- llvm/test/CodeGen/EVM/logical.ll | 6 +- llvm/test/CodeGen/EVM/memory.ll | 4 +- llvm/test/CodeGen/EVM/mod.ll | 4 +- llvm/test/CodeGen/EVM/mul.ll | 2 +- llvm/test/CodeGen/EVM/select.ll | 6 +- llvm/test/CodeGen/EVM/stack-ops-commutable.ll | 416 ++++++++++++ llvm/test/CodeGen/EVM/stack-ops.ll | 354 ++++++++++ llvm/test/CodeGen/EVM/storage.ll | 2 +- llvm/test/CodeGen/EVM/sub.ll | 2 +- llvm/test/CodeGen/EVM/tstorage.ll | 2 +- .../CodeGen/EVM/unused_function_arguments.ll | 52 ++ .../Generic/2007-01-15-LoadSelectCycle.ll | 1 + .../Generic/2008-08-07-PtrToInt-SmallerInt.ll | 3 +- .../Generic/2009-04-28-i128-cmp-crash.ll | 1 + .../Generic/2011-07-07-ScheduleDAGCrash.ll | 1 + .../CodeGen/Generic/2012-06-08-APIntCrash.ll | 1 + llvm/test/CodeGen/Generic/i128-addsub.ll | 1 + ...e-return-values-cross-block-with-invoke.ll | 1 + llvm/test/CodeGen/Generic/undef-phi.ll | 1 + .../2007-10-19-InlineAsmDirectives.ll | 1 + .../Inputs/evm-basic.ll.expected | 13 +- llvm/unittests/Target/EVM/CMakeLists.txt | 25 + llvm/unittests/Target/EVM/StackModel.cpp | 147 +++++ llvm/unittests/Target/EVM/StackShuffler.cpp | 199 ++++++ 56 files changed, 4061 insertions(+), 354 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp create mode 100644 llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp create mode 100644 llvm/lib/Target/EVM/EVMStackModel.cpp create mode 100644 llvm/lib/Target/EVM/EVMStackModel.h create mode 100644 llvm/lib/Target/EVM/EVMStackShuffler.cpp create mode 100644 llvm/lib/Target/EVM/EVMStackShuffler.h create mode 100644 llvm/lib/Target/EVM/EVMStackSolver.cpp create mode 100644 llvm/lib/Target/EVM/EVMStackSolver.h create mode 100644 llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp create mode 100644 llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h delete mode 100644 llvm/test/CodeGen/EVM/fallthrough.mir create mode 100644 llvm/test/CodeGen/EVM/stack-ops-commutable.ll create mode 100644 llvm/test/CodeGen/EVM/stack-ops.ll create mode 100644 llvm/test/CodeGen/EVM/unused_function_arguments.ll create mode 100644 llvm/unittests/Target/EVM/CMakeLists.txt create mode 100644 llvm/unittests/Target/EVM/StackModel.cpp create mode 100644 llvm/unittests/Target/EVM/StackShuffler.cpp diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 0d0158d8793e..5ed6d7406daa 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -39,6 +39,7 @@ add_llvm_target(EVMCodeGen EVMAllocaHoisting.cpp EVMArgumentMove.cpp EVMAsmPrinter.cpp + EVMBackwardPropagationStackification.cpp EVMCodegenPrepare.cpp EVMFrameLowering.cpp EVMISelDAGToDAG.cpp @@ -52,7 +53,12 @@ add_llvm_target(EVMCodeGen EVMRegColoring.cpp EVMRegisterInfo.cpp EVMSingleUseExpression.cpp + EVMSplitCriticalEdges.cpp + EVMStackSolver.cpp + EVMStackModel.cpp EVMStackify.cpp + EVMStackifyCodeEmitter.cpp + EVMStackShuffler.cpp EVMSubtarget.cpp EVMTargetMachine.cpp EVMTargetTransformInfo.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index f43d0a036248..432fd869460d 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -35,6 +35,13 @@ enum AddressSpaces { }; } // namespace EVMAS +namespace EVMCOST { +unsigned constexpr SWAP = 3; +unsigned constexpr DUP = 3; +unsigned constexpr POP = 2; +unsigned constexpr PUSH = 3; +} // namespace EVMCOST + // LLVM IR passes. ModulePass *createEVMLowerIntrinsicsPass(); FunctionPass *createEVMCodegenPreparePass(); @@ -50,7 +57,9 @@ ModulePass *createEVMLinkRuntimePass(); FunctionPass *createEVMOptimizeLiveIntervals(); FunctionPass *createEVMRegColoring(); FunctionPass *createEVMSingleUseExpression(); +FunctionPass *createEVMSplitCriticalEdges(); FunctionPass *createEVMStackify(); +FunctionPass *createEVMBPStackification(); // PassRegistry initialization declarations. void initializeEVMCodegenPreparePass(PassRegistry &); @@ -61,7 +70,9 @@ void initializeEVMLinkRuntimePass(PassRegistry &); void initializeEVMOptimizeLiveIntervalsPass(PassRegistry &); void initializeEVMRegColoringPass(PassRegistry &); void initializeEVMSingleUseExpressionPass(PassRegistry &); +void initializeEVMSplitCriticalEdgesPass(PassRegistry &); void initializeEVMStackifyPass(PassRegistry &); +void initializeEVMBPStackificationPass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { EVMLinkRuntimePass() = default; diff --git a/llvm/lib/Target/EVM/EVMArgumentMove.cpp b/llvm/lib/Target/EVM/EVMArgumentMove.cpp index 7aac4b7bc93c..206add7946ec 100644 --- a/llvm/lib/Target/EVM/EVMArgumentMove.cpp +++ b/llvm/lib/Target/EVM/EVMArgumentMove.cpp @@ -6,7 +6,8 @@ // //===----------------------------------------------------------------------===// // -// This file moves ARGUMENT instructions after ScheduleDAG scheduling. +// This file moves and orders ARGUMENT instructions after ScheduleDAG +// scheduling. // // Arguments are really live-in registers, however, since we use virtual // registers and LLVM doesn't support live-in virtual registers, we're @@ -67,21 +68,24 @@ bool EVMArgumentMove::runOnMachineFunction(MachineFunction &MF) { bool Changed = false; MachineBasicBlock &EntryMBB = MF.front(); + SmallVector Args; + for (MachineInstr &MI : EntryMBB) { + if (EVM::ARGUMENT == MI.getOpcode()) + Args.push_back(&MI); + } - // Look for the first NonArg instruction. - const auto InsertPt = - std::find_if_not(EntryMBB.begin(), EntryMBB.end(), [](auto &MI) { - return EVM::ARGUMENT == MI.getOpcode(); - }); + // Sort ARGUMENT instructions in ascending order of their arguments. + std::sort(Args.begin(), Args.end(), + [](const MachineInstr *MI1, const MachineInstr *MI2) { + int64_t Arg1Idx = MI1->getOperand(1).getImm(); + int64_t Arg2Idx = MI2->getOperand(1).getImm(); + return Arg1Idx < Arg2Idx; + }); - // Now move any argument instructions later in the block - // to before our first NonArg instruction. - for (MachineInstr &MI : llvm::make_range(InsertPt, EntryMBB.end())) { - if (EVM::ARGUMENT == MI.getOpcode()) { - EntryMBB.insert(InsertPt, MI.removeFromParent()); - Changed = true; - } + for (MachineInstr *MI : reverse(Args)) { + MachineInstr *Arg = MI->removeFromParent(); + EntryMBB.insert(EntryMBB.begin(), Arg); + Changed = true; } - return Changed; } diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index b8536b8e351c..9328c5802bef 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -31,6 +31,8 @@ using namespace llvm; +extern cl::opt EVMKeepRegisters; + #define DEBUG_TYPE "asm-printer" namespace { @@ -52,17 +54,11 @@ 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; + void emitBasicBlockStart(const MachineBasicBlock &MBB) 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; + void emitFunctionEntryLabel() override; void emitEndOfAsmFile(Module &) override; @@ -70,25 +66,10 @@ class EVMAsmPrinter : public AsmPrinter { void emitAssemblySymbol(const MachineInstr *MI); void emitWideRelocatableSymbol(const MachineInstr *MI); void emitLoadImmutableLabel(const MachineInstr *MI); + void emitJumpDest(); }; } // 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(); @@ -111,19 +92,84 @@ void EVMAsmPrinter::emitFunctionEntryLabel() { } } +void EVMAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { + AsmPrinter::emitBasicBlockStart(MBB); + + // Emit JUMPDEST instruction at the beginning of the basic block, if + // this is not a block that is only reachable by fallthrough. + if (!EVMKeepRegisters && !AsmPrinter::isBlockOnlyReachableByFallthrough(&MBB)) + emitJumpDest(); +} + void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping, MF->getRegInfo()); - unsigned Opc = MI->getOpcode(); - if (Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S) { - emitAssemblySymbol(MI); + + switch (MI->getOpcode()) { + default: + break; + case EVM::PseudoCALL: { + // Generate push instruction with the address of a function. + MCInst Push; + Push.setOpcode(EVM::PUSH4_S); + assert(MI->getOperand(0).isGlobal() && + "The first operand of PseudoCALL should be a GlobalValue."); + + // TODO: #745: Refactor EVMMCInstLower::Lower so we could use lowerOperand + // instead of creating a MCOperand directly. + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + getSymbol(MI->getOperand(0).getGlobal()), OutContext)); + Push.addOperand(MCOp); + EmitToStreamer(*OutStreamer, Push); + + // Jump to a function. + MCInst Jump; + Jump.setOpcode(EVM::JUMP_S); + EmitToStreamer(*OutStreamer, Jump); + + // In case a function has a return label, emit it, and also + // emit a JUMPDEST instruction. + if (MI->getNumExplicitOperands() > 1) { + assert(MI->getOperand(1).isMCSymbol() && + "The second operand of PseudoCALL should be a MCSymbol."); + OutStreamer->emitLabel(MI->getOperand(1).getMCSymbol()); + emitJumpDest(); + } return; } - if (Opc == EVM::LINKERSYMBOL_S) { - emitWideRelocatableSymbol(MI); + case EVM::PseudoRET: { + // TODO: #746: Use PseudoInstExpansion and do this expansion in tblgen. + MCInst Jump; + Jump.setOpcode(EVM::JUMP_S); + EmitToStreamer(*OutStreamer, Jump); return; } - if (Opc == EVM::LOADIMMUTABLE_S) { + case EVM::PseudoJUMP: + case EVM::PseudoJUMPI: { + MCInst Push; + Push.setOpcode(EVM::PUSH4_S); + + // TODO: #745: Refactor EVMMCInstLower::Lower so we could use lowerOperand + // instead of creating a MCOperand directly. + MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create( + MI->getOperand(0).getMBB()->getSymbol(), OutContext)); + Push.addOperand(MCOp); + EmitToStreamer(*OutStreamer, Push); + + MCInst Jump; + Jump.setOpcode(MI->getOpcode() == EVM::PseudoJUMP ? EVM::JUMP_S + : EVM::JUMPI_S); + EmitToStreamer(*OutStreamer, Jump); + return; + } + case EVM::LINKERSYMBOL_S: + emitWideRelocatableSymbol(MI); + return; + case EVM::DATASIZE_S: + case EVM::DATAOFFSET_S: + emitAssemblySymbol(MI); + return; + case EVM::LOADIMMUTABLE_S: emitLoadImmutableLabel(MI); return; } @@ -133,12 +179,6 @@ void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, TmpInst); } -bool EVMAsmPrinter::isBlockOnlyReachableByFallthrough( - const MachineBasicBlock *MBB) const { - // For simplicity, always emit BB labels. - return false; -} - // Lowers LOADIMMUTABLE_S as show below: // LOADIMMUTABLE_S @immutable_id // -> @@ -247,6 +287,12 @@ void EVMAsmPrinter::emitEndOfAsmFile(Module &) { ImmutablesMap.clear(); } +void EVMAsmPrinter::emitJumpDest() { + MCInst JumpDest; + JumpDest.setOpcode(EVM::JUMPDEST_S); + EmitToStreamer(*OutStreamer, JumpDest); +} + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMAsmPrinter() { const RegisterAsmPrinter X(getTheEVMTarget()); } diff --git a/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp b/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp new file mode 100644 index 000000000000..179e9768ae14 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp @@ -0,0 +1,96 @@ +//===----- EVMBPStackification.cpp - BP stackification ---------*- 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 implements backward propagation (BP) stackification. +// Original idea was taken from the Ethereum's compiler (solc) stackification +// algorithm. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMStackSolver.h" +#include "EVMStackifyCodeEmitter.h" +#include "EVMSubtarget.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-backward-propagation-stackification" + +namespace { +class EVMBPStackification final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + + EVMBPStackification() : MachineFunctionPass(ID) {} + +private: + StringRef getPassName() const override { + return "EVM backward propagation stackification"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::TracksLiveness); + } +}; +} // end anonymous namespace + +char EVMBPStackification::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMBPStackification, DEBUG_TYPE, + "Backward propagation stackification", false, false) +INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass) +INITIALIZE_PASS_END(EVMBPStackification, DEBUG_TYPE, + "Backward propagation stackification", false, false) + +FunctionPass *llvm::createEVMBPStackification() { + return new EVMBPStackification(); +} + +bool EVMBPStackification::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Backward propagation stackification **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &LIS = getAnalysis().getLIS(); + MachineLoopInfo *MLI = &getAnalysis().getLI(); + + // We don't preserve SSA form. + MRI.leaveSSA(); + + assert(MRI.tracksLiveness() && "Stackification expects liveness"); + EVMStackModel StackModel(MF, LIS, + MF.getSubtarget().stackDepthLimit()); + EVMStackSolver(MF, StackModel, MLI).run(); + EVMStackifyCodeEmitter(StackModel, MF).run(); + + auto *MFI = MF.getInfo(); + MFI->setIsStackified(); + + // In a stackified code register liveness has no meaning. + MRI.invalidateLiveness(); + return true; +} diff --git a/llvm/lib/Target/EVM/EVMInstrFormats.td b/llvm/lib/Target/EVM/EVMInstrFormats.td index bd2e6b8b7b0b..dfb2dd75797a 100644 --- a/llvm/lib/Target/EVM/EVMInstrFormats.td +++ b/llvm/lib/Target/EVM/EVMInstrFormats.td @@ -53,6 +53,7 @@ class NI pattern, bit stack, let Opc = inst; let Inst{7-0} = Opc; let GasCost = cost; + let Defs = !if(stack, [], [ARGUMENTS]); } // Generates both register and stack based versions of one actual instruction. @@ -61,8 +62,8 @@ multiclass I pattern_r, int cost = 0, dag oops_s = (outs), dag iops_s = (ins), string argstr_s = ""> { let isCodeGenOnly = 1 in def "" : NI; - let BaseName = NAME in - def _S : NI; + let BaseName = NAME, Defs = [] in def _S + : NI; } // For pseudo instructions that have no real counterparts. These instructions @@ -73,8 +74,8 @@ class NRI pattern, string asmstr> : NI { } -class EVMPseudo pattern> - : NI { +class EVMPseudo pattern, bit stack = 0> + : NI { let isPseudo = 1; let isCodeGenOnly = 1; } diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index 340ecff24868..47f3d50293d6 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -59,6 +59,16 @@ bool EVMInstrInfo::analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&FBB, SmallVectorImpl &Cond, bool AllowModify) const { + SmallVector BranchInstrs; + BranchType BT = analyzeBranch(MBB, TBB, FBB, Cond, AllowModify, BranchInstrs); + return BT == BT_None; +} + +EVMInstrInfo::BranchType EVMInstrInfo::analyzeBranch( + MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, bool AllowModify, + SmallVectorImpl &BranchInstrs) const { + LLVM_DEBUG(dbgs() << "Analyzing branches of " << printMBBReference(MBB) << '\n'); @@ -70,79 +80,96 @@ bool EVMInstrInfo::analyzeBranch(MachineBasicBlock &MBB, const auto *MFI = MBB.getParent()->getInfo(); if (MFI->getIsStackified()) { LLVM_DEBUG(dbgs() << "Can't analyze terminators in stackified code"); - return true; + return BT_None; } - // Iterate backwards and analyze all terminators. - MachineBasicBlock::reverse_iterator I = MBB.rbegin(), E = MBB.rend(); - while (I != E) { - if (I->isUnconditionalBranch()) { - // There should be no other branches after the unconditional branch. - assert(!TBB && !FBB && Cond.empty() && "Unreachable branch found"); - TBB = I->getOperand(0).getMBB(); - - // Clean things up, if we're allowed to. - if (AllowModify) { - // There should be no instructions after the unconditional branch. - assert(I == MBB.rbegin()); - - // Delete the branch itself, if its target is the fall-through block. - if (MBB.isLayoutSuccessor(TBB)) { - LLVM_DEBUG(dbgs() << "Removing fall-through branch: "; I->dump()); - I->eraseFromParent(); - I = MBB.rbegin(); - TBB = nullptr; // Fall-through case. - continue; - } - } - } else if (I->isConditionalBranch()) { - // There can't be several conditional branches in a single block just now. - assert(Cond.empty() && "Several conditional branches?"); - - // Set FBB to the destination of the previously encountered unconditional - // branch (if there was any). - FBB = TBB; - TBB = I->getOperand(0).getMBB(); - - // Put the "use" of the condition into Cond[0]. - const MachineOperand &UseMO = I->getOperand(1); - Cond.push_back(UseMO); - - // reverseBranch needs the instruction which feeds the branch, but only - // supports comparisons. See if we can find one. - for (MachineBasicBlock::reverse_iterator CI = I; CI != E; ++CI) { - // If it is the right comparison, put its result into Cond[1]. - // TODO: This info is required for branch reversing, but this - // is not yet implemented. - if (CI->isCompare()) { - const MachineOperand &DefMO = CI->getOperand(0); - if (DefMO.getReg() == UseMO.getReg()) - Cond.push_back(DefMO); - // Only give it one shot, this should be enough. - break; - } - } - } else if (I->isTerminator()) { - // Return, indirect branch, fall-through, or some other unrecognized - // terminator. Give up. - LLVM_DEBUG(dbgs() << "Unrecognized terminator: "; I->dump()); - return true; - } else if (!I->isDebugValue()) { - // This is an ordinary instruction, meaning there are no terminators left - // to process. Finish the analysis. - break; - } + MachineBasicBlock::reverse_iterator I = MBB.rbegin(), REnd = MBB.rend(); + // Skip all the debug instructions. + while (I != REnd && I->isDebugInstr()) ++I; + + if (I == REnd || !isUnpredicatedTerminator(*I)) { + // This block ends with no branches (it just falls through to its succ). + // Leave TBB/FBB null. + return BT_NoBranch; } + if (!I->isBranch()) { + // Not a branch terminator. + return BT_None; + } + + MachineInstr *LastInst = &*I++; + MachineInstr *SecondLastInst = nullptr; + + // Skip any debug instruction to see if the second last is a branch. + while (I != REnd && I->isDebugInstr()) + ++I; + + if (I != REnd && I->isBranch()) + SecondLastInst = &*I++; // Check that there are no unaccounted terminators left. - assert(std::none_of(I, E, [](MachineBasicBlock::reverse_iterator I) { - return I->isTerminator(); - })); + assert(I == REnd || + std::none_of(I, REnd, [](MachineBasicBlock::reverse_iterator I) { + return I->isTerminator(); + })); + + if (LastInst->isUnconditionalBranch()) { + TBB = LastInst->getOperand(0).getMBB(); + + // Clean things up, if we're allowed to. + if (AllowModify) { + // There should be no instructions after the unconditional branch. + assert(LastInst == MBB.rbegin()); + + // Delete the branch itself, if its target is the fall-through block. + if (MBB.isLayoutSuccessor(TBB)) { + LLVM_DEBUG(dbgs() << "Removing fall-through branch: "; + LastInst->dump()); + LastInst->eraseFromParent(); + TBB = nullptr; // Fall-through case. + } + } + if (TBB) + BranchInstrs.push_back(LastInst); + + if (!SecondLastInst) + return TBB ? BT_Uncond : BT_NoBranch; + } + + MachineInstr *CondBr = SecondLastInst ? SecondLastInst : LastInst; + assert(CondBr->isConditionalBranch()); + BranchInstrs.push_back(CondBr); + + // Set FBB to the destination of the previously encountered unconditional + // branch (if there was any). + FBB = TBB; + TBB = CondBr->getOperand(0).getMBB(); + + // Put the "use" of the condition into Cond[0]. + const MachineOperand &UseMO = CondBr->getOperand(1); + Cond.push_back(UseMO); + + // reverseBranch needs the instruction which feeds the branch, but only + // supports comparisons. See if we can find one. + for (MachineBasicBlock::reverse_iterator CI = CondBr; CI != REnd; ++CI) { + // If it is the right comparison, put its result into Cond[1]. + // TODO: This info is required for branch reversing, but this + // is not yet implemented. + if (CI->isCompare()) { + const MachineOperand &DefMO = CI->getOperand(0); + if (DefMO.getReg() == UseMO.getReg()) + Cond.push_back(DefMO); + // Only give it one shot, this should be enough. + break; + } + } + + if (!SecondLastInst) + return BT_Cond; - // If we didn't bail out earlier, the analysis was successful. - return false; + return BT_CondUncond; } unsigned EVMInstrInfo::removeBranch(MachineBasicBlock &MBB, @@ -174,7 +201,7 @@ unsigned EVMInstrInfo::insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *FBB, ArrayRef Cond, const DebugLoc &DL, int *BytesAdded) const { - assert(!BytesAdded && "Code is size not handled"); + assert(!BytesAdded && "Code size not handled"); // The number of instructions inserted. unsigned InstrCount = 0; diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h index 216de0cdebe3..87931ad678b8 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.h +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -38,6 +38,14 @@ class EVMInstrInfo final : public EVMGenInstrInfo { public: explicit EVMInstrInfo(); + enum BranchType : uint8_t { + BT_None, // Couldn't analyze branch. + BT_NoBranch, // No branches found. + BT_Uncond, // One unconditional branch. + BT_Cond, // One conditional branch. + BT_CondUncond // A conditional branch followed by an unconditional branch. + }; + const EVMRegisterInfo &getRegisterInfo() const { return RI; } bool isReallyTriviallyReMaterializable(const MachineInstr &MI) const override; @@ -52,6 +60,12 @@ class EVMInstrInfo final : public EVMGenInstrInfo { SmallVectorImpl &Cond, bool AllowModify) const override; + BranchType analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify, + SmallVectorImpl &BranchInstrs) const; + unsigned removeBranch(MachineBasicBlock &MBB, int *BytesRemoved) const override; diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index b2d3b9691436..347b49449c97 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -200,12 +200,11 @@ def ADJCALLSTACKUP [(EVMcallseq_end timm:$amt1, timm:$amt2)]>; } -let isCodeGenOnly = 1 in { -let hasSideEffects = 1 in -def ARGUMENT - : NRI<(outs GPR:$res), (ins i256imm:$argno), - [(set GPR:$res, (EVMargument timm:$argno))], - "ARGUMENT $res, $argno">; +let hasSideEffects = 1, Defs = [], + Uses = [ARGUMENTS] in def ARGUMENT + : NRI<(outs GPR:$res), + (ins i256imm:$argno), [(set GPR:$res, (EVMargument timm:$argno))], + "ARGUMENT $res, $argno">; // This is not real EVM instruction. It should be eliminted while // stackification. @@ -216,10 +215,8 @@ def CONST_I256 // This is not real EVM instruction. It should be eliminted while // stackification. -let isAsCheapAsAMove = 1 in -def COPY_I256 - : NRI<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; -} +let isAsCheapAsAMove = 1 in def COPY_I256 + : NRI<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; def : Pat<(i256 (EVMTargetAddrWrapper tglobaladdr:$addr)), (CONST_I256 tglobaladdr:$addr)>; @@ -252,6 +249,8 @@ def FCALL "FCALL\t$callee">; } // Uses = [SP], isCall = 1 +let isCall = 1 in def PseudoCALL + : EVMPseudo<(outs), (ins jmptarget:$callee, variable_ops), [], true>; //===----------------------------------------------------------------------===// // EVM arithmetic instructions. @@ -272,17 +271,17 @@ defm SDIV : BinaryInst; defm MOD : BinaryInst; defm SMOD : BinaryInst; -defm ADDMOD - : I<(outs GPR:$dst), (ins GPR:$add_op1, GPR:$add_op2, GPR:$denom), - [(set GPR:$dst, - (int_evm_addmod GPR:$add_op1, GPR:$add_op2, GPR:$denom))], - "ADDMOD", " $dst, $add_op1, $add_op2, $denom", 0x08, 8>; - -defm MULMOD - : I<(outs GPR:$dst), (ins GPR:$mul_op1, GPR:$mul_op2, GPR:$denom), - [(set GPR:$dst, - (int_evm_mulmod GPR:$mul_op1, GPR:$mul_op2, GPR:$denom))], - "MULMOD", " $dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; +let isCommutable = 1 in { + defm ADDMOD : I<(outs GPR:$dst), (ins GPR:$add_op1, GPR:$add_op2, GPR:$denom), + [(set GPR:$dst, (int_evm_addmod GPR:$add_op1, GPR:$add_op2, + GPR:$denom))], + "ADDMOD", " $dst, $add_op1, $add_op2, $denom", 0x08, 8>; + + defm MULMOD : I<(outs GPR:$dst), (ins GPR:$mul_op1, GPR:$mul_op2, GPR:$denom), + [(set GPR:$dst, (int_evm_mulmod GPR:$mul_op1, GPR:$mul_op2, + GPR:$denom))], + "MULMOD", " $dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; +} defm EXP : I<(outs GPR:$dst), (ins GPR:$base, GPR:$exp), @@ -405,19 +404,23 @@ let isBranch = 1, isTerminator = 1 in { defm JUMPI : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], "JUMPI", " $dst, $cond", 0x57, 10>; +def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; -let isBarrier = 1 in -defm JUMP - : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", " $dst", 0x56, 8>; +let isBarrier = 1 in { + defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", " $dst", + 0x56, 8>; + def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; +} // isBarrier = 1 } // isBranch = 1, isTerminator = 1 // This isn't really a control flow instruction, but it should be used to mark // destination of jump instructions. defm JUMPDEST : I<(outs), (ins), [], "JUMPDEST", "", 0x5B, 1>; -let isBarrier = 1, isTerminator = 1, isReturn = 1 in -def RET : NRI<(outs), (ins variable_ops), [(EVMret)], "RET">; - +let isBarrier = 1, isTerminator = 1, isReturn = 1 in { + def RET : NRI<(outs), (ins variable_ops), [(EVMret)], "RET">; + def PseudoRET : EVMPseudo<(outs), (ins), [], true>; +} //===----------------------------------------------------------------------===// // EVM memory/storage instructions. @@ -750,7 +753,7 @@ defm CREATE2 // EVM instructions to return with error. //===----------------------------------------------------------------------===// -let isTerminator = 1, isBarrier = 1, isReturn = 1 in { +let isTerminator = 1, isBarrier = 1 in { defm REVERT : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_revert GPR:$offset, GPR:$size)], @@ -767,7 +770,7 @@ let hasSideEffects = 1 in { : I<(outs), (ins GPR:$addr), [(int_evm_selfdestruct GPR:$addr)], "SELFDESTRUCT", " $addr", 0xFF, 5000>; - let isTerminator = 1, isBarrier = 1, isReturn = 1 in { + let isTerminator = 1, isBarrier = 1 in { defm STOP : I<(outs), (ins), [(int_evm_stop)], "STOP", "", 0x00, 0>; defm INVALID : I<(outs), (ins), [(int_evm_invalid)], "INVALID", "", 0xFE, 0>; } @@ -794,8 +797,8 @@ foreach I = {1-16} in { defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; -def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], false, "", 0, 0> { - let isCodeGenOnly = 1; +def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], true, "", 0, 0> { + let isCodeGenOnly = 1; } // Define register PUSH* instructions diff --git a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp index 79cd13b0b385..eab58999400f 100644 --- a/llvm/lib/Target/EVM/EVMLinkRuntime.cpp +++ b/llvm/lib/Target/EVM/EVMLinkRuntime.cpp @@ -16,9 +16,7 @@ //============================================================================// #include "llvm/IR/Function.h" -#include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" @@ -26,7 +24,6 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Transforms/IPO/Internalize.h" -#include "llvm/Transforms/Scalar.h" #include @@ -81,12 +78,6 @@ static bool EVMLinkRuntimeImpl(Module &M, const char *ModuleToLink) { exit(1); } - for (auto &F : M.functions()) { - if (!F.isDeclaration()) { - F.addFnAttr(Attribute::NoInline); - } - } - bool LinkErr = false; LinkErr = L.linkInModule( std::move(RTM), Flags, [](Module &M, const StringSet<> &GVS) { diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index 44d74fc01f48..7c14b057c78d 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -17,13 +17,10 @@ #include "EVMMachineFunctionInfo.h" #include "EVMSubtarget.h" #include "MCTargetDesc/EVMMCTargetDesc.h" // for EVM::ARGUMENT_* -#include "llvm/ADT/SmallPtrSet.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/CodeGen/LiveIntervals.h" #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" #include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/IR/GlobalAlias.h" @@ -312,6 +309,10 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, if (NextI == Insert) return true; + // Don't move ARGUMENT instructions, as stackification pass relies on this. + if (DefI->getOpcode() == EVM::ARGUMENT) + return false; + // Check for register dependencies. SmallVector MutableRegisters; for (const MachineOperand &MO : DefI->operands()) { diff --git a/llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp b/llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp new file mode 100644 index 000000000000..ee39d4691c17 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSplitCriticalEdges.cpp @@ -0,0 +1,90 @@ +//===----- EVMSplitCriticalEdges.cpp - Split Critical Edges ----*- 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 performs splitting of CFG critical edges. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-split-critical-edges" + +namespace { +class EVMSplitCriticalEdges final : public MachineFunctionPass { +public: + static char ID; + + EVMSplitCriticalEdges() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "EVM split critical edges"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + MachineFunction *MF = nullptr; + + bool splitCriticalEdges(); +}; +} // end anonymous namespace + +char EVMSplitCriticalEdges::ID = 0; + +INITIALIZE_PASS(EVMSplitCriticalEdges, DEBUG_TYPE, "Split critical edges", + false, false) + +FunctionPass *llvm::createEVMSplitCriticalEdges() { + return new EVMSplitCriticalEdges(); +} + +bool EVMSplitCriticalEdges::splitCriticalEdges() { + SetVector> ToSplit; + for (MachineBasicBlock &MBB : *MF) { + if (MBB.pred_size() > 1) { + for (MachineBasicBlock *Pred : MBB.predecessors()) { + if (Pred->succ_size() > 1) + ToSplit.insert(std::make_pair(Pred, &MBB)); + } + } + } + + bool Changed = false; + for (const auto &Pair : ToSplit) { + auto *NewSucc = Pair.first->SplitCriticalEdge(Pair.second, *this); + if (NewSucc != nullptr) { + Pair.first->updateTerminator(NewSucc); + NewSucc->updateTerminator(Pair.second); + LLVM_DEBUG(dbgs() << " *** Splitting critical edge: " + << printMBBReference(*Pair.first) << " -- " + << printMBBReference(*NewSucc) << " -- " + << printMBBReference(*Pair.second) << '\n'); + Changed = true; + } else { + llvm_unreachable("Cannot break critical edge"); + } + } + return Changed; +} + +bool EVMSplitCriticalEdges::runOnMachineFunction(MachineFunction &Mf) { + MF = &Mf; + LLVM_DEBUG({ + dbgs() << "********** Splitting critical edges **********\n" + << "********** Function: " << Mf.getName() << '\n'; + }); + + bool Changed = splitCriticalEdges(); + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMStackModel.cpp b/llvm/lib/Target/EVM/EVMStackModel.cpp new file mode 100644 index 000000000000..6788a22e7b9f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackModel.cpp @@ -0,0 +1,156 @@ +//===----- EVMEVMStackModel.cpp - EVM Stack Model ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "EVMStackModel.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "llvm/CodeGen/MachineFunction.h" + +using namespace llvm; + +bool llvm::isLinkerPseudoMI(const MachineInstr &MI) { + return MI.getOpcode() == EVM::DATASIZE || MI.getOpcode() == EVM::DATAOFFSET || + MI.getOpcode() == EVM::LOADIMMUTABLE || + MI.getOpcode() == EVM::LINKERSYMBOL; +} +bool llvm::isPushOrDupLikeMI(const MachineInstr &MI) { + return isLinkerPseudoMI(MI) || MI.getOpcode() == EVM::CONST_I256 || + MI.getOpcode() == EVM::COPY_I256; +} +bool llvm::isNoReturnCallMI(const MachineInstr &MI) { + assert(MI.getOpcode() == EVM::FCALL && "Unexpected call instruction"); + const MachineOperand *FuncOp = MI.explicit_uses().begin(); + const auto *F = cast(FuncOp->getGlobal()); + return F->hasFnAttribute(Attribute::NoReturn); +} + +static std::string getInstName(const MachineInstr *MI) { + const MachineFunction *MF = MI->getParent()->getParent(); + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + return TII->getName(MI->getOpcode()).str(); +} + +std::string SymbolSlot::toString() const { + return getInstName(MI) + ":" + std::string(Symbol->getName()); +} +std::string CallerReturnSlot::toString() const { + const MachineOperand *FuncOp = Call->explicit_uses().begin(); + const auto *F = cast(FuncOp->getGlobal()); + return "RET[" + std::string(F->getName()) + "]"; +} + +EVMStackModel::EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS, + unsigned StackDepthLimit) + : MF(MF), LIS(LIS), StackDepthLimit(StackDepthLimit) { + for (MachineBasicBlock &MBB : MF) + for (const MachineInstr &MI : instructionsToProcess(&MBB)) + processMI(MI); +} + +Stack EVMStackModel::getFunctionParameters() const { + auto *MFI = MF.getInfo(); + Stack Parameters(MFI->getNumParams(), EVMStackModel::getUnusedSlot()); + for (const MachineInstr &MI : MF.front()) { + if (MI.getOpcode() == EVM::ARGUMENT) { + int64_t ArgIdx = MI.getOperand(1).getImm(); + Parameters[ArgIdx] = getRegisterSlot(MI.getOperand(0).getReg()); + } + } + return Parameters; +} + +StackSlot *EVMStackModel::getStackSlot(const MachineOperand &MO) const { + // If the virtual register defines a constant and this is the only + // definition, emit the literal slot as MI's input. + const LiveInterval *LI = &LIS.getInterval(MO.getReg()); + if (LI->containsOneValue()) { + SlotIndex Idx = LIS.getInstructionIndex(*MO.getParent()); + const VNInfo *VNI = LI->Query(Idx).valueIn(); + assert(VNI && "Use of non-existing value"); + assert(!VNI->isPHIDef()); + const MachineInstr *DefMI = LIS.getInstructionFromIndex(VNI->def); + assert(DefMI && "Dead valno in interval"); + if (DefMI->getOpcode() == EVM::CONST_I256) + return getLiteralSlot(DefMI->getOperand(1).getCImm()->getValue()); + } + return getRegisterSlot(MO.getReg()); +} + +Stack EVMStackModel::getSlotsForInstructionUses(const MachineInstr &MI) const { + Stack In; + for (const auto &MO : reverse(MI.explicit_uses())) { + // All the non-register operands are handled in instruction specific + // handlers. + if (!MO.isReg()) + continue; + + // SP is not used anyhow. + if (MO.getReg() == EVM::SP) + continue; + + In.push_back(getStackSlot(MO)); + } + return In; +} + +void EVMStackModel::processMI(const MachineInstr &MI) { + unsigned Opc = MI.getOpcode(); + assert(Opc != EVM::STACK_LOAD && Opc != EVM::STACK_STORE && + "Unexpected stack memory instruction"); + assert(all_of(MI.implicit_operands(), + [](const MachineOperand &MO) { + return MO.getReg() == EVM::VALUE_STACK || + MO.getReg() == EVM::ARGUMENTS || + MO.getReg() == EVM::SP; + }) && + "Unexpected implicit def or use"); + + assert(all_of(MI.explicit_operands(), + [](const MachineOperand &MO) { + return !MO.isReg() || + Register::isVirtualRegister(MO.getReg()); + }) && + "Unexpected explicit def or use"); + + if (Opc == EVM::FCALL) { + Stack Input; + if (!isNoReturnCallMI(MI)) + Input.push_back(getCallerReturnSlot(&MI)); + + append_range(Input, getSlotsForInstructionUses(MI)); + MIInputMap[&MI] = Input; + return; + } + if (Opc == EVM::CONST_I256) { + MIInputMap[&MI] = + Stack(1, getLiteralSlot(MI.getOperand(1).getCImm()->getValue())); + return; + } + if (isLinkerPseudoMI(MI)) { + MCSymbol *Sym = MI.getOperand(1).getMCSymbol(); + MIInputMap[&MI] = Stack(1, getSymbolSlot(Sym, &MI)); + return; + } + + MIInputMap[&MI] = getSlotsForInstructionUses(MI); +} + +Stack EVMStackModel::getReturnArguments(const MachineInstr &MI) const { + assert(MI.getOpcode() == EVM::RET); + Stack Input = getSlotsForInstructionUses(MI); + // We need to reverse input operands to restore original ordering, + // in the instruction. + // Calling convention: return values are passed in stack such that the + // last one specified in the RET instruction is passed on the stack TOP. + std::reverse(Input.begin(), Input.end()); + Input.push_back(getCalleeReturnSlot(&MF)); + return Input; +} diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h new file mode 100644 index 000000000000..ba6cad8860a7 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -0,0 +1,338 @@ +//===------------- EVMStackModel.h - Stack Model ----------------*- 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 defines a representation used by the backwards propagation +// stackification algorithm. It consists of 'StackSlot' and 'Stack' entities. +// New stack representation is derived from Machine IR as following: +// MachineOperand -> StackSlot +// MI's defs/uses -> Stack (array of StackSlot) +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H + +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +namespace llvm { + +class MachineFunction; +class MachineBasicBlock; +class MCSymbol; + +class StackSlot { +public: + enum SlotKind : uint8_t { + SK_Literal, + SK_Register, + SK_Symbol, + SK_CallerReturn, + SK_CalleeReturn, + SK_Unused, + SK_Unknown + }; + +private: + const SlotKind KindID; + +protected: + explicit StackSlot(SlotKind KindID) : KindID(KindID) {} + +public: + virtual ~StackSlot() = default; + + unsigned getSlotKind() const { return KindID; } + + // 'isRematerializable()' returns true, if a slot always has a known value + // at compile time and therefore can safely be removed from the stack at any + // time and then regenerated later. + virtual bool isRematerializable() const = 0; + virtual std::string toString() const = 0; +}; + +/// A slot containing a literal value. +class LiteralSlot final : public StackSlot { + APInt Value; + +public: + explicit LiteralSlot(const APInt &V) : StackSlot(SK_Literal), Value(V) {} + const APInt &getValue() const { return Value; } + + bool isRematerializable() const override { return true; } + std::string toString() const override { + SmallString<64> S; + Value.toStringSigned(S); + return std::string(S.str()); + } + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Literal; + } +}; + +/// A slot containing a register def. +class RegisterSlot final : public StackSlot { + Register Reg; + +public: + explicit RegisterSlot(const Register &R) : StackSlot(SK_Register), Reg(R) {} + const Register &getReg() const { return Reg; } + + bool isRematerializable() const override { return false; } + std::string toString() const override { + SmallString<64> S; + raw_svector_ostream OS(S); + OS << printReg(Reg, nullptr, 0, nullptr); + return std::string(S.str()); + } + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Register; + } +}; + +/// A slot containing a MCSymbol. +class SymbolSlot final : public StackSlot { + MCSymbol *Symbol; + const MachineInstr *MI = nullptr; + +public: + SymbolSlot(MCSymbol *S, const MachineInstr *MI) + : StackSlot(SK_Symbol), Symbol(S), MI(MI) {} + const MachineInstr *getMachineInstr() const { return MI; } + MCSymbol *getSymbol() const { return Symbol; } + + bool isRematerializable() const override { return true; } + std::string toString() const override; + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Symbol; + } +}; + +/// The label pushed as the return address seen from the caller. +class CallerReturnSlot final : public StackSlot { + const MachineInstr *Call = nullptr; + +public: + explicit CallerReturnSlot(const MachineInstr *Call) + : StackSlot(SK_CallerReturn), Call(Call) {} + const MachineInstr *getCall() const { return Call; } + + bool isRematerializable() const override { return true; } + std::string toString() const override; + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_CallerReturn; + } +}; + +/// The label pushed as the return address seen from the callee. +class CalleeReturnSlot final : public StackSlot { + const MachineFunction *MF = nullptr; + +public: + explicit CalleeReturnSlot(const MachineFunction *MF) + : StackSlot(SK_CalleeReturn), MF(MF) {} + const MachineFunction *getMachineFunction() { return MF; } + + bool isRematerializable() const override { return false; } + std::string toString() const override { return "RET"; } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_CalleeReturn; + } +}; + +/// A slot containing an arbitrary value that is always eventually popped and +/// never used. Used to maintain stack balance on control flow joins. +class UnusedSlot final : public StackSlot { +public: + UnusedSlot() : StackSlot(SK_Unused) {} + + bool isRematerializable() const override { return true; } + std::string toString() const override { return "Unused"; } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Unused; + } +}; + +class UnknownSlot final : public StackSlot { + size_t Index = 0; + +public: + explicit UnknownSlot(size_t Index) : StackSlot(SK_Unknown), Index(Index) {} + + size_t getIndex() const { return Index; } + bool isRematerializable() const override { return true; } + std::string toString() const override { return "UNKNOWN"; } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Unknown; + } +}; + +/// The stack top is the last element of the vector. +class Stack : public SmallVector { +public: + explicit Stack(const StackSlot **Start, const StackSlot **End) + : SmallVector(Start, End) {} + explicit Stack(size_t Size, const StackSlot *Value) + : SmallVector(Size, Value) {} + explicit Stack(SmallVector &&Slots) + : SmallVector(std::move(Slots)) {} + // TODO: should it be explicit? If yes, fix all build errors. + Stack(const SmallVector &Slots) : SmallVector(Slots) {} + Stack() = default; + + std::string toString() const { + std::string Result("[ "); + for (const auto *It : *this) + Result += It->toString() + ' '; + Result += ']'; + return Result; + } +}; + +bool isPushOrDupLikeMI(const MachineInstr &MI); +bool isLinkerPseudoMI(const MachineInstr &MI); +bool isNoReturnCallMI(const MachineInstr &MI); + +class EVMStackModel { + MachineFunction &MF; + const LiveIntervals &LIS; + unsigned StackDepthLimit; + + // Storage for stack slots. + mutable DenseMap> LiteralStorage; + mutable DenseMap> RegStorage; + mutable DenseMap, + std::unique_ptr> + SymbolStorage; + mutable DenseMap> + CallerReturnStorage; + + // There should be a single CalleeReturnSlot for the MF. + mutable std::unique_ptr TheCalleeReturnSlot; + + using MBBStackMap = DenseMap; + using InstStackMap = DenseMap; + + // Map MBB to its entry and exit stacks. + MBBStackMap MBBEntryStackMap; + // Note: For branches ending with a conditional jump, the exit stack + // retains the jump condition slot, even though the jump consumes it. + MBBStackMap MBBExitStackMap; + + // Map an MI to its entry stack. + InstStackMap InstEntryStackMap; + + // Map an MI to its input slots. + DenseMap MIInputMap; + + // Mutable getters for EVMStackSolver to manage the maps. + MBBStackMap &getMBBEntryMap() { return MBBEntryStackMap; } + MBBStackMap &getMBBExitMap() { return MBBExitStackMap; } + InstStackMap &getInstEntryMap() { return InstEntryStackMap; } + friend class EVMStackSolver; + +public: + EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS, + unsigned StackDepthLimit); + Stack getFunctionParameters() const; + Stack getReturnArguments(const MachineInstr &MI) const; + + const Stack &getMIInput(const MachineInstr &MI) const { + return MIInputMap.at(&MI); + } + Stack getSlotsForInstructionDefs(const MachineInstr *MI) const { + Stack Defs; + for (const auto &MO : MI->defs()) + Defs.push_back(getRegisterSlot(MO.getReg())); + return Defs; + } + + // Get or create a requested stack slot. + StackSlot *getStackSlot(const MachineOperand &MO) const; + LiteralSlot *getLiteralSlot(const APInt &V) const { + if (LiteralStorage.count(V) == 0) + LiteralStorage[V] = std::make_unique(V); + return LiteralStorage[V].get(); + } + RegisterSlot *getRegisterSlot(const Register &R) const { + if (RegStorage.count(R) == 0) + RegStorage[R] = std::make_unique(R); + return RegStorage[R].get(); + } + SymbolSlot *getSymbolSlot(MCSymbol *S, const MachineInstr *MI) const { + auto Key = std::make_pair(S, MI); + if (SymbolStorage.count(Key) == 0) + SymbolStorage[Key] = std::make_unique(S, MI); + return SymbolStorage[Key].get(); + } + CallerReturnSlot *getCallerReturnSlot(const MachineInstr *Call) const { + if (CallerReturnStorage.count(Call) == 0) + CallerReturnStorage[Call] = std::make_unique(Call); + return CallerReturnStorage[Call].get(); + } + CalleeReturnSlot *getCalleeReturnSlot(const MachineFunction *MF) const { + if (!TheCalleeReturnSlot) + TheCalleeReturnSlot = std::make_unique(MF); + assert(MF == TheCalleeReturnSlot->getMachineFunction()); + return TheCalleeReturnSlot.get(); + } + // Unused is always the same slot. + static UnusedSlot *getUnusedSlot() { + static UnusedSlot TheUnusedSlot; + return &TheUnusedSlot; + } + + const Stack &getMBBEntryStack(const MachineBasicBlock *MBB) const { + return MBBEntryStackMap.at(MBB); + } + const Stack &getMBBExitStack(const MachineBasicBlock *MBB) const { + return MBBExitStackMap.at(MBB); + } + const Stack &getInstEntryStack(const MachineInstr *MI) const { + return InstEntryStackMap.at(MI); + } + + unsigned stackDepthLimit() const { return StackDepthLimit; } + +private: + Stack getSlotsForInstructionUses(const MachineInstr &MI) const; + void processMI(const MachineInstr &MI); + +public: + bool skipMI(const MachineInstr &MI) const { + auto Opc = MI.getOpcode(); + // If the virtual register has the only definition, ignore this instruction, + // as we create literal slots from the immediate value at the register uses. + if (Opc == EVM::CONST_I256 && + LIS.getInterval(MI.getOperand(0).getReg()).containsOneValue()) + return true; + return Opc == EVM::ARGUMENT || Opc == EVM::RET || Opc == EVM::JUMP || + Opc == EVM::JUMPI; + } + auto instructionsToProcess(const MachineBasicBlock *MBB) const { + return make_filter_range( + MBB->instrs(), [this](const MachineInstr &MI) { return !skipMI(MI); }); + } + auto reverseInstructionsToProcess(const MachineBasicBlock *MBB) const { + return make_filter_range( + make_range(MBB->rbegin(), MBB->rend()), + [this](const MachineInstr &MI) { return !skipMI(MI); }); + } +}; +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H diff --git a/llvm/lib/Target/EVM/EVMStackShuffler.cpp b/llvm/lib/Target/EVM/EVMStackShuffler.cpp new file mode 100644 index 000000000000..f2cea3660758 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackShuffler.cpp @@ -0,0 +1,283 @@ +//===---- EVMStackShuffler.cpp - Implementation of stack shuffling ---- 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 +// +//===----------------------------------------------------------------------===// + +#include "EVMStackShuffler.h" +#include + +using namespace llvm; + +bool EVMStackShuffler::copyMissingSlotFromTarget(size_t StartRIdx, + bool CannotFail) { + assert(StartRIdx < Target.size()); + std::deque Worklist{StartRIdx}; + DenseSet Visited; + + while (!Worklist.empty()) { + size_t RIdx = Worklist.front(); + Worklist.pop_front(); + Visited.insert(RIdx); + // If a slot is missing or underrepresented in the current stack, copy it + // from the target. + if (getTargetNumOccurrences(Target[RIdx]) > 0) { + rematerialize(Target[RIdx]); + return true; + } + // Otherwise, the target slot already exists in the current stack, + // indicating that matching slots are present but not in the correct + // positions. Continue searching among the corresponding target slots. + assert(Current.size() <= Target.size()); + for (size_t NextRIdx = 0, Size = Current.size(); NextRIdx < Size; + ++NextRIdx) { + if (Visited.count(NextRIdx)) + continue; + if (match(Current[NextRIdx], Target[NextRIdx])) + continue; + if (match(Current[NextRIdx], Target[RIdx])) + Worklist.push_back(NextRIdx); + } + } + if (CannotFail) + report_fatal_error("Unexpected stack state."); + + return false; +} + +bool EVMStackShuffler::rematerializeUnreachableSlot() { + size_t Limit = StackDepthLimit - 1; + size_t Size = Current.size(); + if (Size < Limit) + return false; + + assert(Size <= Target.size()); + // Check whether any deep slot might still be needed later (i.e. we still + // need to reach it with a DUP or SWAP). + for (size_t RIdx = 0, REndIdx = Size - Limit; RIdx < REndIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) { + if (getCurrentNumOccurrences(Current[RIdx]) > 0) { + // If this slot occurs again later, we skip this occurrence. + auto *It = std::find_if( + Current.begin() + RIdx + 1, Current.end(), + [&](const StackSlot *S) { return Current[RIdx] == S; }); + if (It != Current.end()) + continue; + + // Duplicate unreachable slot. + for (const auto *TargetSlot : Target) { + if (isa(TargetSlot)) + continue; + if (match(Current[RIdx], TargetSlot)) { + rematerialize(TargetSlot); + return true; + } + } + } + continue; + } + + // This slot needs to be moved. + // If the current top matches the slot, swap it down. + if (match(Current[Size - 1], Target[RIdx])) { + swap(Size - RIdx - 1); + return true; + } + // Otherwise try to copy it on top from target. + if (copyMissingSlotFromTarget(RIdx)) + return true; + // Otherwise swap up with the first slot that matches the target one. + if (swapIfCurrent(RIdx + 1, Size, [this, RIdx](const StackSlot *S) { + return match(S, Target[RIdx]); + })) + return true; + } + return false; +} + +bool EVMStackShuffler::step() { + if (Current.size() <= Target.size() && + all_of(zip(Current, Target), [&](auto Slots) { + return match(std::get<0>(Slots), std::get<1>(Slots)); + })) { + if (Current.size() < Target.size()) { + if (!rematerializeUnreachableSlot()) + // Since all current slots already match their target counterparts, + // a deficit must exist. + copyMissingSlotFromTarget(Current.size(), /* can't fail */ true); + return true; + } + return false; + } + + size_t Top = Current.size() - 1; + const auto *SrcTop = Current[Top]; + const auto *TgtTop = Top < Target.size() ? Target[Top] : nullptr; + + if (getCurrentNumOccurrences(SrcTop) < 0 && + !isa_and_nonnull(TgtTop)) { + pop(); + return true; + } + + assert(!Target.empty()); + // If the top slot is not on its position, swap it down to increase number of + // matching slots. + if (!match(SrcTop, TgtTop) || isa_and_nonnull(TgtTop)) { + for (size_t RIdx = 0, REndIdx = std::min(Current.size(), Target.size()); + RIdx < REndIdx; ++RIdx) { + bool CanSwap = SrcTop != Current[RIdx] && + !match(Current[RIdx], Target[RIdx]) && + match(SrcTop, Target[RIdx]); + if (!CanSwap) + continue; + + if (Current.size() - RIdx - 1 > StackDepthLimit) { + // If there is a reachable slot to be removed, swap it and remove. + for (size_t SwapDepth = StackDepthLimit; SwapDepth > 0; --SwapDepth) { + if (getCurrentNumOccurrences( + Current[Current.size() - 1 - SwapDepth]) < 0) { + swap(SwapDepth); + // Pop the redundant slot to compress the stack, even if the + // target's top slot is unused and therefore match with anything. + pop(); + return true; + } + } + } + swap(Current.size() - RIdx - 1); + return true; + } + } + + // If the top of the current stack was not needed, it was popped; + // if it was needed, it was swapped into the correct position. + assert(Current.size() <= Target.size()); + + // If a non-top slot is to be removed, try coping its corresponding target + // slot so that a swap can occur and the slot can be popped in the next + // iteration. + for (size_t RIdx = 0, REndIdx = Current.size(); RIdx < REndIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) + continue; + + if (getCurrentNumOccurrences(Current[RIdx]) < 0) { + if (!rematerializeUnreachableSlot()) + // Given that the current stack is no larger than the target, + // any excess implies a corresponding deficit, so copying is guaranteed. + copyMissingSlotFromTarget(RIdx, /* can't fail */ true); + return true; + } + } + + for ([[maybe_unused]] const auto *CurrentSlot : Current) + assert(getCurrentNumOccurrences(CurrentSlot) >= 0); + + // Put the top at the right position. + if (!match(SrcTop, TgtTop)) + if (swapIfCurrent(0, Current.size(), + [TgtTop, this](const StackSlot *SrcSlot) { + return match(SrcSlot, TgtTop); + })) + return true; + + if (Current.size() < Target.size()) { + if (!rematerializeUnreachableSlot()) + copyMissingSlotFromTarget(Current.size(), /* can't fail */ true); + return true; + } + + assert(Current.size() == Target.size()); + for (size_t RIdx = 0, REndIdx = Current.size(); RIdx < REndIdx; ++RIdx) { + assert(getCurrentNumOccurrences(Current[RIdx]) == 0 && + "Current stack should have required number of slot copies."); + assert((isa(Target[RIdx]) || + getTargetNumOccurrences(Target[RIdx]) == 0) && + "Target stack should have required number of copies of used slots."); + } + assert(match(SrcTop, TgtTop) && "The top slot must match at this point."); + + size_t StartRIdx = Current.size() > (StackDepthLimit + 1) + ? Current.size() - (StackDepthLimit + 1) + : 0U; + // If a lower slot that mismatches the target matches the top slot, + // swap it so that it can be processed in the next iteration. + if (swapIfTarget(StartRIdx, Current.size(), + [SrcTop, this](const StackSlot *TgtSlot) { + return match(SrcTop, TgtSlot); + })) + return true; + + // Swap any mismatch reachable slot. + if (swapIfCurrent( + StartRIdx, Current.size(), + [SrcTop](const StackSlot *SrcSlot) { return SrcTop != SrcSlot; })) + return true; + + // Stack has unreachable slots, remove slots that match with unused. + if (isa(TgtTop)) { + pop(); + return true; + } + for (size_t RIdx = 0, REndIdx = Current.size(); RIdx < REndIdx; ++RIdx) { + if (isa(Target[RIdx])) { + swap(Current.size() - RIdx - 1); + pop(); + return true; + } + } + + // Unreachable slots cannot be handled; equate stacks without enforcing the + // depth limit. + if (swapIfTarget(0, Current.size(), [SrcTop, this](const StackSlot *TgtSlot) { + return match(SrcTop, TgtSlot); + })) + return true; + + if (swapIfCurrent(0, Current.size(), [SrcTop](const StackSlot *SrcSlot) { + return SrcTop != SrcSlot; + })) + return true; + + llvm_unreachable("Unexpected state"); +} + +void llvm::calculateStack( + Stack &CurrentStack, Stack const &TargetStack, unsigned StackDepthLimit, + const std::function &Swap, + const std::function &Rematerialize, + const std::function &Pop) { + EVMStackShuffler TheShuffler = + EVMStackShuffler(CurrentStack, TargetStack, StackDepthLimit); + auto getNumOccurrences = [](const StackSlot *Slot, Stack &C, const Stack &T) { + int CUses = -count(C, Slot); + if (T.size() > C.size()) + CUses += std::count(T.begin() + C.size(), T.end(), Slot); + CUses += count_if(zip(C, T), [Slot](auto In) { + auto [CSlot, TSlot] = In; + return isa(TSlot) ? CSlot == Slot : TSlot == Slot; + }); + return CUses; + }; + + TheShuffler.setGetCurrentNumOccurrences(getNumOccurrences); + TheShuffler.setGetTargetNumOccurrences(getNumOccurrences); + + TheShuffler.setSwap([&Swap](size_t Idx, Stack &C) { Swap(Idx); }); + TheShuffler.setPop(Pop); + TheShuffler.setRematerialize(Rematerialize); + + TheShuffler.shuffle(); + + assert(CurrentStack.size() == TargetStack.size()); + for (unsigned I = 0; I < CurrentStack.size(); ++I) { + const StackSlot *&Current = CurrentStack[I]; + const StackSlot *Target = TargetStack[I]; + if (isa(Target)) + Current = EVMStackModel::getUnusedSlot(); + else + assert(Current == Target); + } +} diff --git a/llvm/lib/Target/EVM/EVMStackShuffler.h b/llvm/lib/Target/EVM/EVMStackShuffler.h new file mode 100644 index 000000000000..c5b8c8305416 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackShuffler.h @@ -0,0 +1,158 @@ +//===---- EVMStackShuffler.h - Implementation of stack shuffling ---- 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 +// +//===----------------------------------------------------------------------===// +// +// Attempts to find the cheapest transition between two stacks. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKSHUFFLER_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKSHUFFLER_H + +#include "EVMStackModel.h" + +namespace llvm { + +/// Attempts to find the cheapest transition between two stacks. +class EVMStackShuffler { + Stack &Current; + const Stack &Target; + unsigned StackDepthLimit; + + using MatchFTy = std::function; + using GetNumOccurrencesFTy = + std::function; + using SwapFTy = std::function; + using PopFTy = std::function; + using RematerializeFTy = std::function; + + MatchFTy MatchF = nullptr; + GetNumOccurrencesFTy GetCurrentNumOccurrencesF = nullptr; + GetNumOccurrencesFTy GetTargetNumOccurrencesF = nullptr; + SwapFTy SwapF = nullptr; + PopFTy PopF = nullptr; + RematerializeFTy RematerializeF = nullptr; + +public: + EVMStackShuffler(Stack &Current, const Stack &Target, + unsigned StackDepthLimit) + : Current(Current), Target(Target), StackDepthLimit(StackDepthLimit) {} + + void setMatch(MatchFTy F) { MatchF = std::move(F); } + void setGetCurrentNumOccurrences(GetNumOccurrencesFTy F) { + GetCurrentNumOccurrencesF = std::move(F); + } + void setGetTargetNumOccurrences(GetNumOccurrencesFTy F) { + GetTargetNumOccurrencesF = std::move(F); + } + void setSwap(SwapFTy F) { SwapF = std::move(F); } + void setPop(PopFTy F) { PopF = std::move(F); } + void setRematerialize(RematerializeFTy F) { RematerializeF = std::move(F); } + +private: + /// Checks if \p SrcSlot can share the same position in a stack with + /// \p TgtSlot. + /// \return true if the slots are equal or if \p TgtSlot is + /// unused allowing any slot to be placed at that position. + bool match(const StackSlot *SrcSlot, const StackSlot *TgtSlot) { + // nullptr is used to pad the smaller stack so it matches the larger one. + // A missing slot does not match any slot. + if (!SrcSlot || !TgtSlot) + return false; + if (isa(TgtSlot)) + return true; + return MatchF ? MatchF(SrcSlot, TgtSlot) : SrcSlot == TgtSlot; + } + int getCurrentNumOccurrences(const StackSlot *SrcSlot) { + if (GetCurrentNumOccurrencesF) + return GetCurrentNumOccurrencesF(SrcSlot, Current, Target); + return 0; + } + int getTargetNumOccurrences(const StackSlot *TgtSlot) { + if (GetTargetNumOccurrencesF) + return GetTargetNumOccurrencesF(TgtSlot, Current, Target); + return 0; + } + bool swapIfCurrent(size_t StartRIdx, size_t EndRIdx, + const std::function &P) { + for (size_t RIdx = StartRIdx; RIdx < EndRIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) + continue; + if (P(Current[RIdx])) { + swap(Current.size() - RIdx - 1); + return true; + } + } + return false; + } + bool swapIfTarget(size_t StartRIdx, size_t EndRIdx, + const std::function &P) { + for (size_t RIdx = StartRIdx; RIdx < EndRIdx; ++RIdx) { + if (match(Current[RIdx], Target[RIdx])) + continue; + if (P(Target[RIdx])) { + swap(Current.size() - RIdx - 1); + return true; + } + } + return false; + } + void swap(size_t I) { + if (SwapF) + SwapF(I, Current); + std::swap(Current[Current.size() - I - 1], Current.back()); + } + void pop() { + if (PopF) + PopF(); + Current.pop_back(); + } + void rematerialize(const StackSlot *S) { + if (RematerializeF) + RematerializeF(S); + Current.push_back(S); + } + +public: + /// After `shuffle`, the source and target stacks are of equal size and + /// corresponding slots match. + /// TODO: assert + void shuffle() { + // The shuffling algorithm should always terminate in polynomial time, but + // we provide a limit in case it does not terminate due to a bug. + for (unsigned I = 0; I < 1000; ++I) + if (!step()) + return; + report_fatal_error("Could not create stack after 1000 iterations."); + } + +private: + /// Perform one stack manipulation (push, pop, dup, swap). + /// \return false if shuffling is done. + bool step(); + + /// Copies a slot from the target stack into the current stack if it is either + /// missing or present in fewer copies. + /// \param StartRIdx The index from which to start searching. + bool copyMissingSlotFromTarget(size_t StartRIdx, bool CannotFail = false); + + // If dupping an ideal slot causes a slot that will still be required to + // become unreachable, then dup the latter slot first. + // \return true, if it performed a dup. + bool rematerializeUnreachableSlot(); +}; + +/// Transforms \p CurrentStack to \p TargetStack. Modifies `CurrentStack` itself +/// after each step(). +void calculateStack(Stack &CurrentStack, Stack const &TargetStack, + unsigned StackDepthLimit, + const std::function &Swap, + const std::function &Rematerialize, + const std::function &Pop); + +} // end namespace llvm +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKSHUFFLER_H diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp new file mode 100644 index 000000000000..a34b7a4aa706 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -0,0 +1,613 @@ +//===--------------------- EVMStackSolver.cpp -------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "EVMStackSolver.h" + +#include "EVM.h" +#include "EVMInstrInfo.h" +#include "EVMRegisterInfo.h" +#include "EVMStackShuffler.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-stack-solver" + +namespace { + +/// \return index of \p Item in \p RangeOrContainer or std::nullopt. +template +std::optional offset(T &&RangeOrContainer, V &&Item) { + auto &&Range = std::forward(RangeOrContainer); + auto It = find(Range, std::forward(Item)); + return (It == adl_end(Range)) + ? std::nullopt + : std::optional(std::distance(adl_begin(Range), It)); +} + +/// \return a range covering the last N elements of \p RangeOrContainer. +template auto take_back(T &&RangeOrContainer, size_t N = 1) { + return make_range(std::prev(adl_end(std::forward(RangeOrContainer)), N), + adl_end(std::forward(RangeOrContainer))); +} + +template +std::optional add(std::optional a, std::optional b) { + if (a && b) + return *a + *b; + return std::nullopt; +} + +/// Given stack \p AfterInst, compute stack before the instruction excluding +/// instruction input operands. +/// \param CompressStack: remove duplicates and rematerializable slots. +Stack calculateStackBeforeInst(const Stack &InstDefs, const Stack &AfterInst, + bool CompressStack, unsigned StackDepthLimit) { + // Number of slots on stack before the instruction excluding its inputs. + size_t BeforeInstSize = count_if(AfterInst, [&](const StackSlot *S) { + return !is_contained(InstDefs, S) && + !(CompressStack && S->isRematerializable()); + }); + + // To use StackTransformer, the computed stack must be transformable to the + // AfterInst stack. Initialize it as follows: + // UnknownSlot{0}, ..., UnknownSlot{BeforeInstSize - 1}, [def<0>], ..., + // [def] + // where UnknownSlot{I} denotes an element to be copied from the AfterInst + // stack to the I-th position in the BeforeInst stack. + SmallVector UnknownSlots; + for (size_t Index = 0; Index < BeforeInstSize; ++Index) + UnknownSlots.emplace_back(Index); + + Stack Tmp; + for (auto &S : UnknownSlots) + Tmp.push_back(&S); + append_range(Tmp, InstDefs); + + if (Tmp.empty()) + return Stack{}; + + EVMStackShuffler Shuffler(Tmp, AfterInst, StackDepthLimit); + + auto canSkipSlot = [&InstDefs, CompressStack](const StackSlot *Slot) { + return count(InstDefs, Slot) || + (CompressStack && Slot->isRematerializable()); + }; + auto countOccurences = [&canSkipSlot](const StackSlot *Slot, Stack &C, + const Stack &T) { + int Num = -count(C, Slot); + if (canSkipSlot(Slot)) + Num = Num + count(T, Slot); + return Num; + }; + + Shuffler.setMatch( + [&canSkipSlot](const StackSlot *SrcSlot, const StackSlot *TgtSlot) { + return isa(SrcSlot) ? !canSkipSlot(TgtSlot) + : SrcSlot == TgtSlot; + }); + + Shuffler.setGetCurrentNumOccurrences( + [&countOccurences](const StackSlot *Slot, Stack &C, const Stack &T) { + return isa(Slot) ? 0 : countOccurences(Slot, C, T); + }); + + Shuffler.setGetTargetNumOccurrences( + [&countOccurences, &canSkipSlot](const StackSlot *Slot, Stack &C, + const Stack &T) { + return !canSkipSlot(Slot) ? 0 : countOccurences(Slot, C, T); + }); + + Shuffler.setSwap([](size_t I, Stack &C) { + assert(!isa(C[C.size() - I - 1]) || + !isa(C.back())); + }); + + Shuffler.shuffle(); + + // After the stack transformation, for each index I, move the AfterInst slot + // corresponding to UnknownSlot{I} from Tmp to the I-th position of the + // BeforeInst. + assert(Tmp.size() == AfterInst.size()); + SmallVector BeforeInst(BeforeInstSize, nullptr); + for (unsigned Idx = 0; Idx < Tmp.size(); ++Idx) { + if (const auto *Slot = dyn_cast(Tmp[Idx])) + BeforeInst[Slot->getIndex()] = AfterInst[Idx]; + } + assert(all_of(BeforeInst, + [](const StackSlot *Slot) { return Slot != nullptr; })); + + return BeforeInst; +} + +} // end anonymous namespace + +BranchInfoTy llvm::getBranchInfo(const MachineBasicBlock *MBB) { + const auto *EVMTII = static_cast( + MBB->getParent()->getSubtarget().getInstrInfo()); + auto *UnConstMBB = const_cast(MBB); + SmallVector Cond; + SmallVector BranchInstrs; + MachineBasicBlock *TBB = nullptr, *FBB = nullptr; + auto BT = EVMTII->analyzeBranch(*UnConstMBB, TBB, FBB, Cond, + /* AllowModify */ false, BranchInstrs); + if (BT == EVMInstrInfo::BT_Cond && !FBB) + FBB = UnConstMBB->getFallThrough(); + + if (BT == EVMInstrInfo::BT_NoBranch && !TBB && !MBB->succ_empty()) + TBB = UnConstMBB->getFallThrough(); + + assert((BT != EVMInstrInfo::BT_Cond && BT != EVMInstrInfo::BT_CondUncond) || + !Cond.empty() && + "Condition should be defined for a condition branch."); + assert(BranchInstrs.size() <= 2); + return {BT, TBB, FBB, BranchInstrs, + Cond.empty() ? std::nullopt : std::optional(Cond[0])}; +} + +/// Return cost of transformation in gas from \p Source to \p Target or +/// nullopt if unreachable stack elements are detected during the +/// transformation. +/// An element is considered unreachable if the transformation requires a stack +/// manipulation on an element deeper than \p StackDepthLimit, which EVM does +/// not support. +/// \note The copy of \p Source is intentional since it is modified during +/// computation. We retain the original \p Source to reattempt the +/// transformation using a compressed stack. +std::optional +llvm::calculateStackTransformCost(Stack Source, const Stack &Target, + unsigned StackDepthLimit) { + unsigned Gas = 0; + bool HasError = false; + auto Swap = [&Gas, &HasError, StackDepthLimit](unsigned SwapDepth) { + if (SwapDepth > StackDepthLimit) + HasError = true; + Gas += EVMCOST::SWAP; + }; + + auto Rematerialize = [&Gas, &HasError, &Source, + StackDepthLimit](const StackSlot *Slot) { + if (Slot->isRematerializable()) + Gas += EVMCOST::PUSH; + else { + std::optional Depth = offset(reverse(Source), Slot); + if (Depth && *Depth >= StackDepthLimit) + HasError = true; + Gas += EVMCOST::DUP; + } + }; + auto Pop = [&Gas]() { Gas += EVMCOST::POP; }; + + calculateStack(Source, Target, StackDepthLimit, Swap, Rematerialize, Pop); + return HasError ? std::nullopt : std::optional(Gas); +} + +EVMStackSolver::EVMStackSolver(const MachineFunction &MF, + EVMStackModel &StackModel, + const MachineLoopInfo *MLI) + : MF(MF), StackModel(StackModel), MLI(MLI) {} + +void EVMStackSolver::run() { + runPropagation(); + LLVM_DEBUG({ + dbgs() << "************* Stack *************\n"; + dump(dbgs()); + }); +} + +Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, + const MachineInstr &MI, + bool CompressStack) { + const Stack MIDefs = StackModel.getSlotsForInstructionDefs(&MI); + // Stack before MI except for MI inputs. + Stack BeforeMI = calculateStackBeforeInst(MIDefs, ExitStack, CompressStack, + StackModel.stackDepthLimit()); + +#ifndef NDEBUG + // Ensure MI defs are not present in BeforeMI stack. + for (const auto *StackSlot : BeforeMI) + if (const auto *RegSlot = dyn_cast(StackSlot)) + assert(!MI.definesRegister(RegSlot->getReg(), /*TRI=*/nullptr)); +#endif // NDEBUG + + BeforeMI.append(StackModel.getMIInput(MI)); + // Store computed stack to StackModel. + insertInstEntryStack(&MI, BeforeMI); + + // If the top stack slots can be rematerialized just before MI, remove it + // from propagation to reduce pressure. + while (!BeforeMI.empty()) { + if (BeforeMI.back()->isRematerializable()) { + BeforeMI.pop_back(); + } else if (auto Offset = + offset(drop_begin(reverse(BeforeMI), 1), BeforeMI.back())) { + if (*Offset + 2 < StackModel.stackDepthLimit()) + BeforeMI.pop_back(); + else + break; + } else + break; + } + + return BeforeMI; +} + +Stack EVMStackSolver::propagateThroughMBB(const Stack &ExitStack, + const MachineBasicBlock *MBB, + bool CompressStack) { + Stack CurrentStack = ExitStack; + for (const auto &MI : StackModel.reverseInstructionsToProcess(MBB)) { + Stack BeforeMI = propagateThroughMI(CurrentStack, MI, CompressStack); + if (!CompressStack && + !calculateStackTransformCost(BeforeMI, CurrentStack, + StackModel.stackDepthLimit())) + return propagateThroughMBB(ExitStack, MBB, + /*CompressStack*/ true); + CurrentStack = std::move(BeforeMI); + } + return CurrentStack; +} + +void EVMStackSolver::runPropagation() { + std::deque Worklist{&MF.front()}; + DenseSet Visited; + + // Collect all the backedges in the MF. + // TODO: CPR-1847. Consider changing CFG before the stackification such that + // every loop has only one backedge. + SmallVector, + 64> + Backedges; + for (const MachineLoop *TopLevelLoop : *MLI) { + // TODO: CPR-1847. Investigate in which order it's better to traverse + // loops. + for (const MachineLoop *L : depth_first(TopLevelLoop)) { + SmallVector Latches; + L->getLoopLatches(Latches); + const MachineBasicBlock *Header = L->getHeader(); + transform(Latches, std::back_inserter(Backedges), + [Header](const MachineBasicBlock *MBB) { + return std::make_pair(MBB, Header); + }); + } + } + + // Propagate stack information until no new information is discovered. + while (!Worklist.empty()) { + // Process blocks in the worklist without following backedges first. + while (!Worklist.empty()) { + const MachineBasicBlock *MBB = *Worklist.begin(); + Worklist.pop_front(); + if (Visited.count(MBB)) + continue; + + // Get the MBB exit stack. + std::optional ExitStack = std::nullopt; + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); + + switch (BranchTy) { + case EVMInstrInfo::BT_None: { + ExitStack = MBB->isReturnBlock() + ? StackModel.getReturnArguments(MBB->back()) + : Stack{}; + } break; + case EVMInstrInfo::BT_Uncond: + case EVMInstrInfo::BT_NoBranch: { + const MachineBasicBlock *Target = MBB->getSingleSuccessor(); + // Currently a basic block could end with FCALL and have no successors, + // but FCALL is not a terminator, so we fall into BT_NoBranch. + // TODO: #788 address it + if (!Target) { // No successors. + ExitStack = Stack{}; + break; + } + if (MachineLoop *ML = MLI->getLoopFor(MBB); + ML && ML->isLoopLatch(MBB)) { + // If the loop header's entry stack has been computed, + // use it as the latch's exit stack; otherwise, assume an empty + // exit stack to avoid non-termination. + auto It = StackModel.getMBBEntryMap().find(Target); + ExitStack = + (It == StackModel.getMBBEntryMap().end() ? Stack{} : It->second); + } else { + if (Visited.count(Target)) + ExitStack = StackModel.getMBBEntryStack(Target); + else + Worklist.push_front(Target); + } + } break; + case EVMInstrInfo::BT_Cond: + case EVMInstrInfo::BT_CondUncond: { + bool FBBVisited = Visited.count(FBB); + bool TBBVisited = Visited.count(TBB); + + if (!FBBVisited) + Worklist.push_front(FBB); + if (!TBBVisited) + Worklist.push_front(TBB); + + if (FBBVisited && TBBVisited) { + Stack CombinedStack = combineStack(StackModel.getMBBEntryStack(FBB), + StackModel.getMBBEntryStack(TBB)); + // Retain the jump condition in ExitStack because StackSolver doesn't + // propagate stacks through branch instructions. + CombinedStack.emplace_back(StackModel.getStackSlot(*Condition)); + ExitStack = std::move(CombinedStack); + } + } + } + + if (ExitStack) { + Visited.insert(MBB); + insertMBBExitStack(MBB, *ExitStack); + insertMBBEntryStack(MBB, propagateThroughMBB(*ExitStack, MBB)); + append_range(Worklist, MBB->predecessors()); + } + } + + // Revisit blocks connected by loop backedges. + for (auto [Latch, Header] : Backedges) { + const Stack &HeaderEntryStack = StackModel.getMBBEntryStack(Header); + const Stack &LatchExitStack = StackModel.getMBBExitStack(Latch); + if (all_of(HeaderEntryStack, [LatchExitStack](const StackSlot *Slot) { + return is_contained(LatchExitStack, Slot); + })) + continue; + + // The latch block does not provide all the slots required by the loop + // header. Mark the subgraph between the latch and header for + // reprocessing. + assert(Latch); + Worklist.emplace_back(Latch); + + // Since the loop header's entry may be permuted, revisit its predecessors + // to minimize stack transformation costs. + for (const MachineBasicBlock *Pred : Header->predecessors()) + Visited.erase(Pred); + + // DFS upwards traversal from latch to the header. + for (auto I = idf_begin(Latch), E = idf_end(Latch); I != E;) { + const MachineBasicBlock *MBB = *I; + Visited.erase(MBB); + if (MBB == Header) { + I.skipChildren(); + continue; + } + ++I; + } + } + } + + // For an MBB with multiple successors the exit stack is a union of the entry + // stack slots from all successors. Each successor's entry stack may be + // missing some slots present in the exit stack. Adjust each successor's entry + // stack to mirror the basic block's exit stack. For any slot that is dead in + // a successor, mark it as 'unused'. + for (const MachineBasicBlock &MBB : MF) { + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(&MBB); + + if (BranchTy != EVMInstrInfo::BT_Cond && + BranchTy != EVMInstrInfo::BT_CondUncond) + continue; + + Stack ExitStack = StackModel.getMBBExitStack(&MBB); + + // The condition must be on top of ExitStack, but a conditional jump + // consumes it. + assert(ExitStack.back() == StackModel.getStackSlot(*Condition)); + ExitStack.pop_back(); + + for (const MachineBasicBlock *Succ : MBB.successors()) { + const Stack &SuccEntryStack = StackModel.getMBBEntryStack(Succ); + Stack NewSuccEntryStack = ExitStack; + for (const StackSlot *&Slot : NewSuccEntryStack) + if (!is_contained(SuccEntryStack, Slot)) + Slot = EVMStackModel::getUnusedSlot(); + +#ifndef NDEBUG + for (const StackSlot *Slot : SuccEntryStack) + assert(Slot->isRematerializable() || + is_contained(NewSuccEntryStack, Slot)); +#endif // NDEBUG + + insertMBBEntryStack(Succ, NewSuccEntryStack); + } + } + + // The entry MBB's stack contains the function parameters, which cannot be + // inferred; put them to the stack. + Stack EntryStack; + if (!MF.getFunction().hasFnAttribute(Attribute::NoReturn)) + EntryStack.push_back(StackModel.getCalleeReturnSlot(&MF)); + + // Calling convention: input arguments are passed in stack such that the + // first one specified in the function declaration is passed on the stack TOP. + append_range(EntryStack, reverse(StackModel.getFunctionParameters())); + insertMBBEntryStack(&MF.front(), EntryStack); +} + +Stack EVMStackSolver::combineStack(const Stack &Stack1, const Stack &Stack2) { + auto [it1, it2] = + std::mismatch(Stack1.begin(), Stack1.end(), Stack2.begin(), Stack2.end()); + + Stack CommonPrefix; + CommonPrefix.append(Stack1.begin(), it1); + + Stack Stack1Tail, Stack2Tail; + Stack1Tail.append(it1, Stack1.end()); + Stack2Tail.append(it2, Stack2.end()); + + if (Stack1Tail.empty()) { + CommonPrefix.append(compressStack(Stack2Tail)); + return CommonPrefix; + } + + if (Stack2Tail.empty()) { + CommonPrefix.append(compressStack(Stack1Tail)); + return CommonPrefix; + } + + Stack Candidate; + for (const auto *Slot : concat(Stack1Tail, Stack2Tail)) + if (!is_contained(Candidate, Slot) && !Slot->isRematerializable()) + Candidate.push_back(Slot); + + auto evaluate = [&CommonPrefix, &Stack1, &Stack2, + this](const Stack &Candidate) { + Stack Test = CommonPrefix; + Test.append(Candidate); + return add(calculateStackTransformCost(Test, Stack1, + StackModel.stackDepthLimit()), + calculateStackTransformCost(Test, Stack2, + StackModel.stackDepthLimit())) + .value_or(std::numeric_limits::max()); + }; + + // This is a modified implementation of the Heap algorithm that does + // not restart I at 1 for each swap. + size_t N = Candidate.size(); + Stack BestCandidate = Candidate; + size_t BestCost = evaluate(Candidate); + + // Initialize counters for Heap's algorithm. + SmallVector C(N, 0); + size_t I = 1; + while (I < N) { + if (C[I] < I) { + if (I & 1) + std::swap(Candidate.front(), Candidate[I]); + else + std::swap(Candidate[C[I]], Candidate[I]); + + size_t Cost = evaluate(Candidate); + if (Cost < BestCost) { + BestCost = Cost; + BestCandidate = Candidate; + } + ++C[I]; + // Note: A proper implementation would reset I to 1 here, + // but doing so would generate all N! permutations, + ++I; + } else { + C[I] = 0; + ++I; + } + } + + CommonPrefix.append(BestCandidate); + return CommonPrefix; +} + +Stack EVMStackSolver::compressStack(Stack CurStack) { + auto ShouldRemove = + [&CurStack, this](SmallVector::reverse_iterator I) { + size_t RIdx = std::distance(CurStack.rbegin(), I); + if ((*I)->isRematerializable()) + return true; + if (auto DistanceToCopy = + offset(make_range(std::next(I), CurStack.rend()), *I)) + return (RIdx + *DistanceToCopy <= StackModel.stackDepthLimit()); + return false; + }; + for (auto I = CurStack.rbegin(); I != CurStack.rend();) { + if (ShouldRemove(I)) { + std::swap(*I, CurStack.back()); + ++I; /* In case I == rbegin(), pop_back() will invalidate it */ + CurStack.pop_back(); + continue; + } + ++I; + } + return CurStack; +} + +#ifndef NDEBUG +static std::string getMBBId(const MachineBasicBlock *MBB) { + return std::to_string(MBB->getNumber()) + "." + std::string(MBB->getName()); +} + +void EVMStackSolver::dump(raw_ostream &OS) { + OS << "Function: " << MF.getName() << "("; + for (const StackSlot *ParamSlot : StackModel.getFunctionParameters()) + OS << ParamSlot->toString(); + OS << ");\n"; + OS << "Entry MBB: " << getMBBId(&MF.front()) << ";\n"; + + for (const MachineBasicBlock &MBB : MF) + dumpMBB(OS, &MBB); +} + +static std::string getMIName(const MachineInstr *MI) { + if (MI->getOpcode() == EVM::FCALL) { + const MachineOperand *Callee = MI->explicit_uses().begin(); + return Callee->getGlobal()->getName().str(); + } + const MachineFunction *MF = MI->getParent()->getParent(); + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + return TII->getName(MI->getOpcode()).str(); +} + +void EVMStackSolver::dumpMBB(raw_ostream &OS, const MachineBasicBlock *MBB) { + OS << getMBBId(MBB) << ":\n"; + OS << '\t' << StackModel.getMBBEntryStack(MBB).toString() << '\n'; + for (const auto &MI : StackModel.instructionsToProcess(MBB)) { + const Stack &MIOutput = StackModel.getSlotsForInstructionDefs(&MI); + const Stack &MIInput = StackModel.getMIInput(MI); + if (isPushOrDupLikeMI(MI) && MIInput == MIOutput) + continue; + + OS << '\n'; + Stack MIEntry = StackModel.getInstEntryStack(&MI); + OS << '\t' << MIEntry.toString() << '\n'; + OS << '\t' << getMIName(&MI) << '\n'; + assert(MIInput.size() <= MIEntry.size()); + MIEntry.resize(MIEntry.size() - MIInput.size()); + MIEntry.append(MIOutput); + OS << '\t' << MIEntry.toString() << "\n"; + } + OS << '\n'; + OS << '\t' << StackModel.getMBBExitStack(MBB).toString() << "\n"; + + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); + switch (BranchTy) { + case EVMInstrInfo::BT_None: { + if (MBB->isReturnBlock()) { + OS << "Exit type: function return, " << MF.getName() << '\n'; + OS << "Return values: " + << StackModel.getReturnArguments(MBB->back()).toString() << '\n'; + } else { + OS << "Exit type: terminate\n"; + } + } break; + case EVMInstrInfo::BT_Uncond: + case EVMInstrInfo::BT_NoBranch: { + if (TBB) { + OS << "Exit type: unconditional branch\n"; + OS << "Target: " << getMBBId(TBB) << '\n'; + } else { + OS << "Exit type: terminate\n"; + } + } break; + case EVMInstrInfo::BT_Cond: + case EVMInstrInfo::BT_CondUncond: { + OS << "Exit type: conditional branch, "; + OS << "cond: " << StackModel.getStackSlot(*Condition)->toString() << '\n'; + OS << "False branch: " << getMBBId(FBB) << '\n'; + OS << "True branch: " << getMBBId(TBB) << '\n'; + } break; + } + OS << '\n'; +} +#endif // NDEBUG diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h new file mode 100644 index 000000000000..7b604ebdc944 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -0,0 +1,105 @@ +//===--------- EVMStackSolver.h - Calculate stack states --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKSOLVER_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKSOLVER_H + +#include "EVMInstrInfo.h" +#include "EVMStackModel.h" +#include "llvm/ADT/DenseMap.h" + +namespace llvm { + +class MachineLoopInfo; + +/// Compute the gas cost to transform \p Source into \p Target. +/// \note The copy of \p Source is intentional because the function modifies +/// it during computation. +std::optional calculateStackTransformCost(Stack Source, + const Stack &Target, + unsigned StackDepthLimit); + +using BranchInfoTy = + std::tuple, + std::optional>; + +BranchInfoTy getBranchInfo(const MachineBasicBlock *MBB); + +class EVMStackSolver { +public: + EVMStackSolver(const MachineFunction &MF, EVMStackModel &StackModel, + const MachineLoopInfo *MLI); + void run(); + +private: + /// Given \p MI's \p ExitStack, compute the entry stack so that after + /// executing the instruction the cost to transform to \p ExitStack is + /// minimal. + /// Side effect: the computed entry stack is stored in StackModel. + /// \param CompressStack: remove duplicates and rematerializable slots. + Stack propagateThroughMI(const Stack &ExitStack, const MachineInstr &MI, + bool CompressStack = false); + + /// Given \p ExitStack, compute the stack at the entry of \p MBB. + /// \param CompressStack: remove duplicates and rematerializable slots. + Stack propagateThroughMBB(const Stack &ExitStack, + const MachineBasicBlock *MBB, + bool CompressStack = false); + + /// Main algorithm walking the graph from entry to exit and propagating stack + /// states back to the entries. Iteratively reruns itself along backward jumps + /// until the state is stabilized. + void runPropagation(); + +#ifndef NDEBUG + void dump(raw_ostream &OS); + void dumpMBB(raw_ostream &OS, const MachineBasicBlock *MBB); +#endif + + /// Compute a stack S that minimizes the number of permutations + /// needed to transform S into \p Stack1 and \p Stack2. + /// TODO: Compute weighted sum based on branch probabilities. + Stack combineStack(const Stack &Stack1, const Stack &Stack2); + + /// Returns a copy of \p Stack with duplicates and rematerializable + /// slots removed. + /// Used when stackification faces slot accessibility issues. + /// Otherwise, copies and rematerializable entities are kept on stack. + Stack compressStack(Stack Stack); + + // Manage StackModel. + void insertMBBEntryStack(const MachineBasicBlock *MBB, const Stack &S) { + StackModel.getMBBEntryMap()[MBB] = S; + } + void insertMBBExitStack(const MachineBasicBlock *MBB, const Stack &S) { + StackModel.getMBBExitMap()[MBB] = S; + } + void insertInstEntryStack(const MachineInstr *MI, const Stack &S) { + StackModel.getInstEntryMap()[MI] = S; + } + void insertMBBEntryStack(const MachineBasicBlock *MBB, Stack &&S) { + StackModel.getMBBEntryMap()[MBB] = std::move(S); + } + void insertMBBExitStack(const MachineBasicBlock *MBB, Stack &&S) { + StackModel.getMBBExitMap()[MBB] = std::move(S); + } + void insertInstEntryStack(const MachineInstr *MI, Stack &&S) { + StackModel.getInstEntryMap()[MI] = std::move(S); + } + + const MachineFunction &MF; + EVMStackModel &StackModel; + const MachineLoopInfo *MLI; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKSOLVER_H diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp index 2fb40b2da3d6..b2fd43ca6387 100644 --- a/llvm/lib/Target/EVM/EVMStackify.cpp +++ b/llvm/lib/Target/EVM/EVMStackify.cpp @@ -720,21 +720,21 @@ void StackModel::handleArgument(MachineInstr *MI) { void StackModel::handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, const Register &Reg) { + assert(MI->getOpcode() == EVM::JUMP || MI->getOpcode() == EVM::JUMPI); + // If the condition register is in the L-stack, we need to move it to // the bottom of the L-stack. After that we should clean clean the L-stack. // In case of an unconditional jump, the Reg value should be // EVM::NoRegister. clearPhysStackAtInst(StackType::L, MI, Reg); - // 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::PUSH_LABEL)) + // Insert pseudo jump instruciton that will be replaced with PUSH and JUMP + // instructions in AsmPrinter. + ToErase.push_back(MI); + unsigned PseudoJumpOpc = + MI->getOpcode() == EVM::JUMP ? EVM::PseudoJUMP : EVM::PseudoJUMPI; + BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(PseudoJumpOpc)) .addMBB(MBB); - - // Add JUMPDEST at the beginning of the target MBB. - if (MBB->empty() || MBB->begin()->getOpcode() != EVM::JUMPDEST) - BuildMI(*MBB, MBB->begin(), DebugLoc(), TII->get(EVM::JUMPDEST)); } void StackModel::handleCondJump(MachineInstr *MI) { @@ -752,7 +752,7 @@ void StackModel::handleJump(MachineInstr *MI) { void StackModel::handleReturn(MachineInstr *MI) { ToErase.push_back(MI); BuildMI(*MI->getParent(), std::next(MIIter(MI)), DebugLoc(), - TII->get(EVM::JUMP)); + TII->get(EVM::PseudoRET)); // Collect the use registers of the RET instruction. SmallVector ReturnRegs; @@ -864,25 +864,21 @@ void StackModel::handleCall(MachineInstr *MI) { // Callee removes them form the stack and pushes return values. MachineBasicBlock &MBB = *MI->getParent(); - // Create return destination. - MIIter It = BuildMI(MBB, MI, MI->getDebugLoc(), TII->get(EVM::JUMPDEST)); // Add symbol just after the jump that will be used as the return // address from the function. MCSymbol *RetSym = MF->getContext().createTempSymbol("FUNC_RET", true); - // Create jump to the callee. - It = BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::JUMP)); - It->setPostInstrSymbol(*MF, RetSym); + // Create pseudo jump to the callee, that will be expanded into PUSH, JUMP + // return label and JUMPDEST instructions in the AsmPrinter. + const MachineOperand *CalleeOp = MI->explicit_uses().begin(); + assert(CalleeOp->isGlobal()); + MIIter It = BuildMI(MBB, MI, MI->getDebugLoc(), TII->get(EVM::PseudoCALL)) + .addGlobalAddress(CalleeOp->getGlobal()) + .addSym(RetSym); // Create push of the return address. 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::PUSH_LABEL)) - .addGlobalAddress(CalleeOp->getGlobal()); } void StackModel::clearFrameObjsAtInst(MachineInstr *MI) { @@ -982,10 +978,6 @@ void StackModel::preProcess() { assert(!MF->empty()); allocateFrameObjects(); allocateXStack(); - // Add JUMPDEST at the beginning of the first MBB, - // so this function can be jumped to. - MachineBasicBlock &MBB = MF->front(); - BuildMI(MBB, MBB.begin(), DebugLoc(), TII->get(EVM::JUMPDEST)); } // Remove all registers operands of the \p MI and repaces the opcode with @@ -995,7 +987,9 @@ void StackModel::stackifyInstruction(MachineInstr *MI) { return; unsigned RegOpcode = MI->getOpcode(); - if (RegOpcode == EVM::PUSH_LABEL) + if (RegOpcode == EVM::PUSH_LABEL || RegOpcode == EVM::PseudoJUMP || + RegOpcode == EVM::PseudoJUMPI || RegOpcode == EVM::PseudoCALL || + RegOpcode == EVM::PseudoRET) return; // Remove register operands. @@ -1048,31 +1042,6 @@ void StackModel::postProcess() { // 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 { diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp new file mode 100644 index 000000000000..3f4928232011 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -0,0 +1,470 @@ +//===--- EVMStackifyCodeEmitter.h - Create stackified MIR -------*- 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 transforms MIR to the 'stackified' MIR. +// +//===----------------------------------------------------------------------===// + +#include "EVMStackifyCodeEmitter.h" +#include "EVMStackShuffler.h" +#include "EVMStackSolver.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/MC/MCContext.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-stackify-code-emitter" + +// Return the number of input arguments of the call instruction. +static size_t getCallNumArgs(const MachineInstr *Call) { + assert(Call->getOpcode() == EVM::FCALL && "Unexpected call instruction"); + assert(Call->explicit_uses().begin()->isGlobal() && + "First operand must be a function"); + size_t NumArgs = Call->getNumExplicitOperands() - Call->getNumExplicitDefs(); + // The first operand is a function, so don't count it. + NumArgs = NumArgs - 1; + // If function will return, we need to account for the return label. + return isNoReturnCallMI(*Call) ? NumArgs : NumArgs + 1; +} + +static std::string getUnreachableStackSlotError(const MachineFunction &MF, + const Stack &CurrentStack, + const StackSlot *Slot, + size_t Depth, bool isSwap) { + return (MF.getName() + Twine(": cannot ") + (isSwap ? "swap " : "dup ") + + std::to_string(Depth) + "-th stack item, " + Slot->toString() + + ".\nItem it located too deep in the stack: " + + CurrentStack.toString()) + .str(); +} + +size_t EVMStackifyCodeEmitter::CodeEmitter::stackHeight() const { + return StackHeight; +} + +void EVMStackifyCodeEmitter::CodeEmitter::enterMBB(MachineBasicBlock *MBB, + int Height) { + StackHeight = Height; + CurMBB = MBB; + LLVM_DEBUG(dbgs() << "\n" + << "Set stack height: " << StackHeight << "\n"); + LLVM_DEBUG(dbgs() << "Setting current location to: " << MBB->getNumber() + << "." << MBB->getName() << "\n"); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitInst(const MachineInstr *MI) { + unsigned Opc = MI->getOpcode(); + assert(Opc != EVM::JUMP && Opc != EVM::JUMPI && Opc != EVM::ARGUMENT && + Opc != EVM::RET && Opc != EVM::CONST_I256 && Opc != EVM::COPY_I256 && + Opc != EVM::FCALL && "Unexpected instruction"); + + size_t NumInputs = MI->getNumExplicitOperands() - MI->getNumExplicitDefs(); + assert(StackHeight >= NumInputs && "Not enough operands on the stack"); + StackHeight -= NumInputs; + StackHeight += MI->getNumExplicitDefs(); + + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitSWAP(unsigned Depth) { + unsigned Opc = EVM::getSWAPOpcode(Depth); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitDUP(unsigned Depth) { + StackHeight += 1; + unsigned Opc = EVM::getDUPOpcode(Depth); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitPOP() { + assert(StackHeight > 0 && "Expected at least one operand on the stack"); + StackHeight -= 1; + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::POP_S)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitConstant(const APInt &Val) { + StackHeight += 1; + unsigned Opc = EVM::getPUSHOpcode(Val); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), + TII->get(EVM::getStackOpcode(Opc))); + if (Opc != EVM::PUSH0) + NewMI.addCImm(ConstantInt::get(MF.getFunction().getContext(), Val)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitConstant(uint64_t Val) { + emitConstant(APInt(256, Val)); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitSymbol(const MachineInstr *MI, + MCSymbol *Symbol) { + assert(isLinkerPseudoMI(*MI) && "Unexpected symbol instruction"); + StackHeight += 1; + // This is codegen-only instruction, that will be converted into PUSH4. + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::getStackOpcode(MI->getOpcode()))) + .addSym(Symbol); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitLabelReference( + const MachineInstr *Call) { + assert(Call->getOpcode() == EVM::FCALL && "Unexpected call instruction"); + StackHeight += 1; + auto [It, Inserted] = CallReturnSyms.try_emplace(Call); + if (Inserted) + It->second = MF.getContext().createTempSymbol("FUNC_RET", true); + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::PUSH_LABEL)) + .addSym(It->second); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitFuncCall(const MachineInstr *MI) { + assert(MI->getOpcode() == EVM::FCALL && "Unexpected call instruction"); + assert(CurMBB == MI->getParent()); + + size_t NumInputs = getCallNumArgs(MI); + assert(StackHeight >= NumInputs && "Not enough operands on the stack"); + StackHeight -= NumInputs; + + // PUSH_LABEL increases the stack height on 1, but we don't increase it + // explicitly here, as the label will be consumed by the following JUMP. + StackHeight += MI->getNumExplicitDefs(); + + // Create pseudo jump to the function, that will be expanded into PUSH and + // JUMP instructions in the AsmPrinter. + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoCALL)) + .addGlobalAddress(MI->explicit_uses().begin()->getGlobal()); + + // If this function returns, add a return label so we can emit it together + // with JUMPDEST. This is taken care in the AsmPrinter. + if (!isNoReturnCallMI(*MI)) + NewMI.addSym(CallReturnSyms.at(MI)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitRet(const MachineInstr *MI) { + assert(MI->getOpcode() == EVM::RET && "Unexpected ret instruction"); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoRET)); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitUncondJump( + const MachineInstr *MI, MachineBasicBlock *Target) { + assert(MI->getOpcode() == EVM::JUMP && + "Unexpected unconditional jump instruction"); + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoJUMP)) + .addMBB(Target); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitCondJump( + const MachineInstr *MI, MachineBasicBlock *Target) { + assert(MI->getOpcode() == EVM::JUMPI && + "Unexpected conditional jump instruction"); + assert(StackHeight > 0 && "Expected at least one operand on the stack"); + StackHeight -= 1; + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(EVM::PseudoJUMPI)) + .addMBB(Target); + verify(NewMI); +} + +// Verify that a stackified instruction doesn't have registers and dump it. +void EVMStackifyCodeEmitter::CodeEmitter::verify(const MachineInstr *MI) const { + assert(EVMInstrInfo::isStack(MI) && + "Only stackified instructions are allowed"); + assert(all_of(MI->operands(), + [](const MachineOperand &MO) { return !MO.isReg(); }) && + "Registers are not allowed in stackified instructions"); + + LLVM_DEBUG(dbgs() << "Adding: " << *MI << "stack height: " << StackHeight + << "\n"); +} +void EVMStackifyCodeEmitter::CodeEmitter::finalize() { + for (MachineBasicBlock &MBB : MF) + for (MachineInstr &MI : make_early_inc_range(MBB)) + // Remove all the instructions that are not stackified. + // TODO: #749: Fix debug info for stackified instructions and don't + // remove debug instructions. + if (!EVMInstrInfo::isStack(&MI)) + MI.eraseFromParent(); +} + +void EVMStackifyCodeEmitter::adjustStackForInst(const MachineInstr *MI, + size_t NumArgs) { + // Remove arguments from CurrentStack. + CurrentStack.erase(CurrentStack.end() - NumArgs, CurrentStack.end()); + + // Push return values to CurrentStack. + append_range(CurrentStack, StackModel.getSlotsForInstructionDefs(MI)); + assert(Emitter.stackHeight() == CurrentStack.size()); +} + +void EVMStackifyCodeEmitter::emitMI(const MachineInstr &MI) { + assert(Emitter.stackHeight() == CurrentStack.size()); + + if (MI.getOpcode() == EVM::FCALL) { + size_t NumArgs = getCallNumArgs(&MI); + assert(CurrentStack.size() >= NumArgs); + + // Assert that we got the correct return label on stack. + if (!isNoReturnCallMI(MI)) { + [[maybe_unused]] const auto *ReturnLabelSlot = dyn_cast( + CurrentStack[CurrentStack.size() - NumArgs]); + assert(ReturnLabelSlot && ReturnLabelSlot->getCall() == &MI); + } + Emitter.emitFuncCall(&MI); + adjustStackForInst(&MI, NumArgs); + } else if (!isPushOrDupLikeMI(MI)) { + size_t NumArgs = MI.getNumExplicitOperands() - MI.getNumExplicitDefs(); + assert(CurrentStack.size() >= NumArgs); + // TODO: assert that we got a correct stack for the call. + + Emitter.emitInst(&MI); + adjustStackForInst(&MI, NumArgs); + } + + // If the MI doesn't define anything, we are done. + if (!MI.getNumExplicitDefs()) + return; + + // Invalidate occurrences of the assigned variables. + for (auto *&CurrentSlot : CurrentStack) + if (const auto *RegSlot = dyn_cast(CurrentSlot)) + if (MI.definesRegister(RegSlot->getReg(), /*TRI=*/nullptr)) + CurrentSlot = EVMStackModel::getUnusedSlot(); + + // Assign variables to current stack top. + assert(CurrentStack.size() >= MI.getNumExplicitDefs()); + llvm::copy(StackModel.getSlotsForInstructionDefs(&MI), + CurrentStack.end() - MI.getNumExplicitDefs()); +} + +// Checks if it's valid to transition from \p SourceStack to \p TargetStack, +// that is \p SourceStack matches each slot in \p TargetStack that is not a +// UnusedSlot exactly. +[[maybe_unused]] static bool match(const Stack &Source, const Stack &Target) { + return Source.size() == Target.size() && + all_of(zip_equal(Source, Target), [](const auto &Pair) { + const auto [Src, Tgt] = Pair; + return isa(Tgt) || (Src == Tgt); + }); +} + +void EVMStackifyCodeEmitter::emitStackPermutations(const Stack &TargetStack) { + assert(Emitter.stackHeight() == CurrentStack.size()); + const unsigned StackDepthLimit = StackModel.stackDepthLimit(); + + calculateStack( + CurrentStack, TargetStack, StackDepthLimit, + // Swap. + [&](unsigned I) { + assert(CurrentStack.size() == Emitter.stackHeight()); + assert(I > 0 && I < CurrentStack.size()); + if (I <= StackDepthLimit) { + Emitter.emitSWAP(I); + return; + } + const StackSlot *Slot = CurrentStack[CurrentStack.size() - I - 1]; + std::string ErrMsg = getUnreachableStackSlotError( + MF, CurrentStack, Slot, I + 1, /* isSwap */ true); + report_fatal_error(ErrMsg.c_str()); + }, + // Push or dup. + [&](const StackSlot *Slot) { + assert(CurrentStack.size() == Emitter.stackHeight()); + + // Dup the slot, if already on stack and reachable. + auto SlotIt = llvm::find(llvm::reverse(CurrentStack), Slot); + if (SlotIt != CurrentStack.rend()) { + unsigned Depth = std::distance(CurrentStack.rbegin(), SlotIt); + if (Depth < StackDepthLimit) { + Emitter.emitDUP(static_cast(Depth + 1)); + return; + } + if (!Slot->isRematerializable()) { + std::string ErrMsg = getUnreachableStackSlotError( + MF, CurrentStack, Slot, Depth + 1, /* isSwap */ false); + report_fatal_error(ErrMsg.c_str()); + } + } + + // Rematerialize the slot. + assert(Slot->isRematerializable()); + if (const auto *L = dyn_cast(Slot)) { + Emitter.emitConstant(L->getValue()); + } else if (const auto *S = dyn_cast(Slot)) { + Emitter.emitSymbol(S->getMachineInstr(), S->getSymbol()); + } else if (const auto *CallRet = dyn_cast(Slot)) { + Emitter.emitLabelReference(CallRet->getCall()); + } else { + assert(isa(Slot)); + // Note: this will always be popped, so we can push anything. + Emitter.emitConstant(0); + } + }, + // Pop. + [&]() { Emitter.emitPOP(); }); + + assert(Emitter.stackHeight() == CurrentStack.size()); +} + +// Emit the stack required for enterting the MI. +void EVMStackifyCodeEmitter::emitMIEntryStack(const MachineInstr &MI) { + // Check if we can choose cheaper stack shuffling if the MI is commutable. + const Stack &TargetStack = StackModel.getInstEntryStack(&MI); + bool SwapCommutable = false; + if (MI.isCommutable()) { + assert(TargetStack.size() > 1); + + size_t DefaultCost = + calculateStackTransformCost(CurrentStack, TargetStack, + StackModel.stackDepthLimit()) + .value_or(std::numeric_limits::max()); + + // Swap the commutable stack items and measure the stack shuffling cost. + // Commutable operands always take top two stack slots. + Stack CommutedTargetStack = TargetStack; + std::swap(CommutedTargetStack[CommutedTargetStack.size() - 1], + CommutedTargetStack[CommutedTargetStack.size() - 2]); + size_t CommutedCost = + calculateStackTransformCost(CurrentStack, CommutedTargetStack, + StackModel.stackDepthLimit()) + .value_or(std::numeric_limits::max()); + + // Choose the cheapest transformation. + SwapCommutable = CommutedCost < DefaultCost; + emitStackPermutations(SwapCommutable ? CommutedTargetStack : TargetStack); + } else { + emitStackPermutations(TargetStack); + } + +#ifndef NDEBUG + // Assert that we have the inputs of the MI on stack top. + const Stack &SavedInput = StackModel.getMIInput(MI); + assert(CurrentStack.size() == Emitter.stackHeight()); + assert(CurrentStack.size() >= SavedInput.size()); + Stack Input(CurrentStack.end() - SavedInput.size(), CurrentStack.end()); + + // Adjust the Input if needed. + if (SwapCommutable) + std::swap(Input[Input.size() - 1], Input[Input.size() - 2]); + + assert(match(Input, SavedInput)); +#endif // NDEBUG +} + +void EVMStackifyCodeEmitter::run() { + assert(CurrentStack.empty() && Emitter.stackHeight() == 0); + + SmallPtrSet Visited; + SmallVector WorkList{&MF.front()}; + while (!WorkList.empty()) { + auto *MBB = WorkList.pop_back_val(); + if (!Visited.insert(MBB).second) + continue; + + CurrentStack = StackModel.getMBBEntryStack(MBB); + Emitter.enterMBB(MBB, CurrentStack.size()); + + // Get branch information before we start to change the BB. + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); + bool HasReturn = MBB->isReturnBlock(); + const MachineInstr *ReturnMI = HasReturn ? &MBB->back() : nullptr; + + for (const auto &MI : StackModel.instructionsToProcess(MBB)) { + // We are done if the MI is in the stack form. + if (EVMInstrInfo::isStack(&MI)) + break; + + emitMIEntryStack(MI); + + [[maybe_unused]] size_t BaseHeight = + CurrentStack.size() - StackModel.getMIInput(MI).size(); + + emitMI(MI); + +#ifndef NDEBUG + // Assert that the MI produced its proclaimed output. + size_t NumDefs = MI.getNumExplicitDefs(); + size_t StackSize = CurrentStack.size(); + assert(StackSize == Emitter.stackHeight()); + assert(StackSize == BaseHeight + NumDefs); + assert(StackSize >= NumDefs); + // Check that the top NumDefs slots are the MI defs. + for (size_t I = StackSize - NumDefs; I < StackSize; ++I) + assert(MI.definesRegister(cast(CurrentStack[I])->getReg(), + /*TRI=*/nullptr)); +#endif // NDEBUG + } + + // Exit the block. + if (BranchTy == EVMInstrInfo::BT_None) { + if (HasReturn) { + assert(!MF.getFunction().hasFnAttribute(Attribute::NoReturn)); + assert(StackModel.getReturnArguments(*ReturnMI) == + StackModel.getMBBExitStack(MBB)); + // Create the function return stack and jump. + emitStackPermutations(StackModel.getMBBExitStack(MBB)); + Emitter.emitRet(ReturnMI); + } + } else if (BranchTy == EVMInstrInfo::BT_Uncond || + BranchTy == EVMInstrInfo::BT_NoBranch) { + if (!MBB->succ_empty()) { + // Create the stack expected at the jump target. + emitStackPermutations(StackModel.getMBBEntryStack(TBB)); + assert(match(CurrentStack, StackModel.getMBBEntryStack(TBB))); + + if (!BrInsts.empty()) + Emitter.emitUncondJump(BrInsts[0], TBB); + + WorkList.push_back(TBB); + } + } else { + assert(BranchTy == EVMInstrInfo::BT_Cond || + BranchTy == EVMInstrInfo::BT_CondUncond); + // Create the shared entry stack of the jump targets, which is + // stored as exit stack of the current MBB. + emitStackPermutations(StackModel.getMBBExitStack(MBB)); + assert(!CurrentStack.empty() && + CurrentStack.back() == StackModel.getStackSlot(*Condition)); + + // Emit the conditional jump to the non-zero label and update the + // stored stack. + assert(!BrInsts.empty()); + Emitter.emitCondJump(BrInsts[BrInsts.size() - 1], TBB); + CurrentStack.pop_back(); + + // Assert that we have a valid stack for both jump targets. + assert(match(CurrentStack, StackModel.getMBBEntryStack(TBB))); + assert(match(CurrentStack, StackModel.getMBBEntryStack(FBB))); + + // Generate unconditional jump if needed. + if (BrInsts.size() == 2) + Emitter.emitUncondJump(BrInsts[0], FBB); + + WorkList.push_back(TBB); + WorkList.push_back(FBB); + } + } + Emitter.finalize(); +} diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h new file mode 100644 index 000000000000..7177ea706650 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h @@ -0,0 +1,85 @@ +//===--- EVMStackifyCodeEmitter.h - Create stackified MIR ------*- 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 transforms MIR to the 'stackified' MIR. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMSTACKIFYCODEEMITTER_H +#define LLVM_LIB_TARGET_EVM_EVMSTACKIFYCODEEMITTER_H + +#include "EVMStackModel.h" +#include "EVMSubtarget.h" + +namespace llvm { + +class MachineInstr; +class MCSymbol; + +class EVMStackifyCodeEmitter { +public: + EVMStackifyCodeEmitter(const EVMStackModel &StackModel, MachineFunction &MF) + : Emitter(MF), StackModel(StackModel), MF(MF) {} + + /// Stackify instructions, starting from the first MF's MBB. + void run(); + +private: + class CodeEmitter { + public: + explicit CodeEmitter(MachineFunction &MF) + : MF(MF), TII(MF.getSubtarget().getInstrInfo()) {} + size_t stackHeight() const; + void enterMBB(MachineBasicBlock *MBB, int Height); + void emitInst(const MachineInstr *MI); + void emitSWAP(unsigned Depth); + void emitDUP(unsigned Depth); + void emitPOP(); + void emitConstant(const APInt &Val); + void emitConstant(uint64_t Val); + void emitSymbol(const MachineInstr *MI, MCSymbol *Symbol); + void emitFuncCall(const MachineInstr *MI); + void emitRet(const MachineInstr *MI); + void emitCondJump(const MachineInstr *MI, MachineBasicBlock *Target); + void emitUncondJump(const MachineInstr *MI, MachineBasicBlock *Target); + void emitLabelReference(const MachineInstr *Call); + /// Remove all the instructions that are not in stack form. + void finalize(); + + private: + MachineFunction &MF; + const EVMInstrInfo *TII; + size_t StackHeight = 0; + MachineBasicBlock *CurMBB = nullptr; + DenseMap CallReturnSyms; + + void verify(const MachineInstr *MI) const; + }; + + CodeEmitter Emitter; + const EVMStackModel &StackModel; + MachineFunction &MF; + Stack CurrentStack; + + /// Emit stack operations to turn CurrentStack into \p TargetStack. + void emitStackPermutations(const Stack &TargetStack); + + /// Creates the MI's entry stack from the 'CurrentStack' taking into + /// account commutative property of the instruction. + void emitMIEntryStack(const MachineInstr &MI); + + /// Remove the arguments from the stack and push the return values. + void adjustStackForInst(const MachineInstr *MI, size_t NumArgs); + + /// Generate code for the instruction. + void emitMI(const MachineInstr &MI); +}; + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKIFYCODEEMITTER_H diff --git a/llvm/lib/Target/EVM/EVMSubtarget.h b/llvm/lib/Target/EVM/EVMSubtarget.h index 331fb28ad5bd..4b0a33799e96 100644 --- a/llvm/lib/Target/EVM/EVMSubtarget.h +++ b/llvm/lib/Target/EVM/EVMSubtarget.h @@ -17,7 +17,6 @@ #include "EVMISelLowering.h" #include "EVMInstrInfo.h" #include "EVMRegisterInfo.h" -#include "llvm/CodeGen/SelectionDAGTargetInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #define GET_SUBTARGETINFO_ENUM @@ -56,6 +55,8 @@ class EVMSubtarget final : public EVMGenSubtargetInfo { } bool useAA() const override; + + unsigned stackDepthLimit() const { return 16; } }; } // namespace llvm diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 4405511587a4..5db238303281 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -22,7 +22,6 @@ #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/InitializePasses.h" -#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Transforms/IPO/GlobalDCE.h" @@ -39,6 +38,10 @@ cl::opt cl::desc("EVM: output stack registers in" " instruction output for test purposes only."), cl::init(false)); +cl::opt + EVMUseLocalStakify("evm-use-local-stackify", cl::Hidden, + cl::desc("EVM: use the local stackification algorithm"), + cl::init(false)); extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { // Register the target. @@ -51,7 +54,9 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMOptimizeLiveIntervalsPass(PR); initializeEVMRegColoringPass(PR); initializeEVMSingleUseExpressionPass(PR); + initializeEVMSplitCriticalEdgesPass(PR); initializeEVMStackifyPass(PR); + initializeEVMBPStackificationPass(PR); } static std::string computeDataLayout() { @@ -75,7 +80,6 @@ EVMTargetMachine::EVMTargetMachine(const Target &T, const Triple &TT, getEffectiveCodeModel(CM, CodeModel::Small), OL), TLOF(std::make_unique()), Subtarget(TT, std::string(CPU), std::string(FS), *this) { - setRequiresStructuredCFG(true); initAsmInfo(); } @@ -200,11 +204,17 @@ void EVMPassConfig::addPreEmitPass() { // FIXME: enable all the passes below, but the Stackify with EVMKeepRegisters. if (!EVMKeepRegisters) { + addPass(createEVMSplitCriticalEdges()); + addPass(&MachineBlockPlacementID); addPass(createEVMOptimizeLiveIntervals()); addPass(createEVMSingleUseExpression()); - // Run the register coloring pass to reduce the total number of registers. - addPass(createEVMRegColoring()); - addPass(createEVMStackify()); + if (EVMUseLocalStakify) { + // Run the register coloring pass to reduce the total number of registers. + addPass(createEVMRegColoring()); + addPass(createEVMStackify()); + } else { + addPass(createEVMBPStackification()); + } } } diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h index 1ffe2724a5d9..9903fe510a73 100644 --- a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h @@ -36,7 +36,7 @@ class EVMTTIImpl final : public BasicTTIImplBase { const EVMTargetLowering *getTLI() const { return TLI; } public: - enum SyncVMRegisterClass { Vector /* Unsupported */, GPR }; + enum EVMRegisterClass { Vector /* Unsupported */, GPR }; EVMTTIImpl(const EVMTargetMachine *TM, const Function &F) : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), @@ -101,9 +101,6 @@ class EVMTTIImpl final : public BasicTTIImplBase { OpsOut.push_back(Type::getIntNTy(Context, RemainingBytes * 8)); } - // TODO: The value is copied from SyncVM, needs to be checked. - unsigned getInliningThresholdMultiplier() const { return 11; } - void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP, OptimizationRemarkEmitter *ORE); diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp index d9f73e7a1de7..cb4ae74f2672 100644 --- a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.cpp @@ -122,3 +122,81 @@ unsigned llvm::EVM::getPUSHOpcode(const APInt &Imm) { llvm_unreachable("Unexpected stack depth"); } } + +unsigned llvm::EVM::getDUPOpcode(unsigned Depth) { + switch (Depth) { + case 1: + return EVM::DUP1; + case 2: + return EVM::DUP2; + case 3: + return EVM::DUP3; + case 4: + return EVM::DUP4; + case 5: + return EVM::DUP5; + case 6: + return EVM::DUP6; + case 7: + return EVM::DUP7; + case 8: + return EVM::DUP8; + case 9: + return EVM::DUP9; + case 10: + return EVM::DUP10; + case 11: + return EVM::DUP11; + case 12: + return EVM::DUP12; + case 13: + return EVM::DUP13; + case 14: + return EVM::DUP14; + case 15: + return EVM::DUP15; + case 16: + return EVM::DUP16; + default: + llvm_unreachable("Unexpected stack depth"); + } +} + +unsigned llvm::EVM::getSWAPOpcode(unsigned Depth) { + switch (Depth) { + case 1: + return EVM::SWAP1; + case 2: + return EVM::SWAP2; + case 3: + return EVM::SWAP3; + case 4: + return EVM::SWAP4; + case 5: + return EVM::SWAP5; + case 6: + return EVM::SWAP6; + case 7: + return EVM::SWAP7; + case 8: + return EVM::SWAP8; + case 9: + return EVM::SWAP9; + case 10: + return EVM::SWAP10; + case 11: + return EVM::SWAP11; + case 12: + return EVM::SWAP12; + case 13: + return EVM::SWAP13; + case 14: + return EVM::SWAP14; + case 15: + return EVM::SWAP15; + case 16: + return EVM::SWAP16; + default: + llvm_unreachable("Unexpected stack depth"); + } +} diff --git a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h index 5b4abc84d6cc..e0c6998bbb5a 100644 --- a/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h +++ b/llvm/lib/Target/EVM/TargetInfo/EVMTargetInfo.h @@ -25,6 +25,8 @@ namespace EVM { unsigned getStackOpcode(unsigned Opcode); unsigned getRegisterOpcode(unsigned Opcode); unsigned getPUSHOpcode(const APInt &Imm); +unsigned getDUPOpcode(unsigned Depth); +unsigned getSWAPOpcode(unsigned Depth); } // namespace EVM diff --git a/llvm/test/CodeGen/EVM/add.ll b/llvm/test/CodeGen/EVM/add.ll index a4a49063d0b9..c20432a02b05 100644 --- a/llvm/test/CodeGen/EVM/add.ll +++ b/llvm/test/CodeGen/EVM/add.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @addrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @addrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[IN2]] %res = add i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/call.ll b/llvm/test/CodeGen/EVM/call.ll index ab6b309e16bc..4e57b307631c 100644 --- a/llvm/test/CodeGen/EVM/call.ll +++ b/llvm/test/CodeGen/EVM/call.ll @@ -8,8 +8,8 @@ declare void @foo2(i256) define i256 @call(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: @call -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ADD [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] ; CHECK: FCALL 1 [[RES1:\$[0-9]+]], @foo, [[TMP1]] @@ -20,8 +20,8 @@ define i256 @call(i256 %a, i256 %b) nounwind { define void @call2(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: @call2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ADD [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] ; CHECK: FCALL 0 @foo2, [[TMP1]] diff --git a/llvm/test/CodeGen/EVM/div.ll b/llvm/test/CodeGen/EVM/div.ll index af3296475505..f5965662751e 100644 --- a/llvm/test/CodeGen/EVM/div.ll +++ b/llvm/test/CodeGen/EVM/div.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @udivrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @udivrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: DIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = udiv i256 %rs1, %rs2 @@ -15,8 +15,8 @@ define i256 @udivrrr(i256 %rs1, i256 %rs2) nounwind { define i256 @sdivrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @sdivrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SDIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = sdiv i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/fallthrough.mir b/llvm/test/CodeGen/EVM/fallthrough.mir deleted file mode 100644 index b5efb4f62911..000000000000 --- a/llvm/test/CodeGen/EVM/fallthrough.mir +++ /dev/null @@ -1,45 +0,0 @@ -# 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/EVM/globals.ll b/llvm/test/CodeGen/EVM/globals.ll index 996282f7b566..df70cab339df 100644 --- a/llvm/test/CodeGen/EVM/globals.ll +++ b/llvm/test/CodeGen/EVM/globals.ll @@ -57,8 +57,8 @@ define i256 @load.fromarray(i256 %i) nounwind { define void @store.toarray(i256 %val, i256 %i) nounwind { ; CHECK-LABEL: store.toarray -; CHECK: ARGUMENT [[IDX:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IDX:\$[0-9]+]], 1 ; CHECK: CONST_I256 [[C:\$[0-9]+]], 5 ; CHECK: SHL [[SHL:\$[0-9]+]], [[IDX]], [[C]] ; CHECK: CONST_I256 [[TMP:\$[0-9]+]], @val.arr diff --git a/llvm/test/CodeGen/EVM/intrinsic.ll b/llvm/test/CodeGen/EVM/intrinsic.ll index d871191f3ab5..8398c33cf70a 100644 --- a/llvm/test/CodeGen/EVM/intrinsic.ll +++ b/llvm/test/CodeGen/EVM/intrinsic.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @sdiv(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @sdiv -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SDIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.sdiv(i256 %rs1, i256 %rs2) @@ -15,8 +15,8 @@ define i256 @sdiv(i256 %rs1, i256 %rs2) nounwind { define i256 @div(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @div -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: DIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.div(i256 %rs1, i256 %rs2) @@ -25,8 +25,8 @@ define i256 @div(i256 %rs1, i256 %rs2) nounwind { define i256 @smod(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @smod -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.smod(i256 %rs1, i256 %rs2) @@ -35,8 +35,8 @@ define i256 @smod(i256 %rs1, i256 %rs2) nounwind { define i256 @mod(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @mod -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: MOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.mod(i256 %rs1, i256 %rs2) @@ -45,8 +45,8 @@ define i256 @mod(i256 %rs1, i256 %rs2) nounwind { define i256 @shl(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @shl -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SHL [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] %res = call i256 @llvm.evm.shl(i256 %rs1, i256 %rs2) @@ -55,8 +55,8 @@ define i256 @shl(i256 %rs1, i256 %rs2) nounwind { define i256 @shr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @shr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SHR [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] %res = call i256 @llvm.evm.shr(i256 %rs1, i256 %rs2) @@ -65,8 +65,8 @@ define i256 @shr(i256 %rs1, i256 %rs2) nounwind { define i256 @sar(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @sar -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SAR [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] %res = call i256 @llvm.evm.sar(i256 %rs1, i256 %rs2) @@ -75,9 +75,9 @@ define i256 @sar(i256 %rs1, i256 %rs2) nounwind { define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { ; CHECK-LABEL: @addmod -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 ; CHECK: ADDMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) @@ -86,9 +86,9 @@ define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { ; CHECK-LABEL: @mulmod -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 ; CHECK: MULMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] %res = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) @@ -97,8 +97,8 @@ define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { define i256 @exp(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @exp -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: EXP [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) @@ -107,8 +107,8 @@ define i256 @exp(i256 %rs1, i256 %rs2) nounwind { define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { ; CHECK-LABEL: @sha3 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SHA3 [[RES1:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) @@ -117,8 +117,8 @@ define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { define i256 @signextend(i256 %bytesize, i256 %val) nounwind { ; CHECK-LABEL: @signextend -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SIGNEXTEND [[RES1:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) @@ -127,8 +127,8 @@ define i256 @signextend(i256 %bytesize, i256 %val) nounwind { define i256 @byte(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @byte -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: BYTE [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) @@ -236,10 +236,10 @@ define i256 @extcodesize(i256 %rs1) nounwind { define void @extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) nounwind { ; CHECK-LABEL: @extcodecopy -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 ; CHECK: EXTCODECOPY [[IN1]], [[IN2]], [[IN3]], [[IN4]] call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) @@ -355,8 +355,8 @@ define i256 @blobbasefee() nounwind { define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { ; CHECK-LABEL: @log0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: LOG0 [[IN1]], [[IN2]] call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) @@ -365,9 +365,9 @@ define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { ; CHECK-LABEL: @log1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 ; CHECK: LOG1 [[IN1]], [[IN2]], [[IN3]] call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) @@ -376,10 +376,10 @@ define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { ; CHECK-LABEL: @log2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 ; CHECK: LOG2 [[IN1]], [[IN2]], [[IN3]], [[IN4]] call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) @@ -388,11 +388,11 @@ define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwin define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { ; CHECK-LABEL: @log3 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 ; CHECK: LOG3 [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]] call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) @@ -401,12 +401,12 @@ define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { ; CHECK-LABEL: @log4 -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 ; CHECK: LOG4 [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) @@ -415,9 +415,9 @@ define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t define i256 @create(i256 %val, ptr addrspace(1) %off, i256 %size) nounwind { ; CHECK-LABEL: @create -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 ; CHECK: CREATE [[RES1:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] %ret = call i256 @llvm.evm.create(i256 %val, ptr addrspace(1) %off, i256 %size) @@ -426,13 +426,13 @@ define i256 @create(i256 %val, ptr addrspace(1) %off, i256 %size) nounwind { define i256 @call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { ; CHECK-LABEL: @call -; CHECK: ARGUMENT [[IN7:\$[0-9]+]], 6 -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 +; CHECK: ARGUMENT [[IN7:\$[0-9]+]], 6 ; CHECK: CALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]], [[IN7]] %ret = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) @@ -441,12 +441,12 @@ define i256 @call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i define i256 @delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { ; CHECK-LABEL: @delegatecall -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 ; CHECK: DELEGATECALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] %ret = call i256 @llvm.evm.delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace (1) %ret_off, i256 %ret_size) @@ -455,10 +455,10 @@ define i256 @delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 define i256 @create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) nounwind { ; CHECK-LABEL: @create2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 ; CHECK: CREATE2 [[RES1:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]] %ret = call i256 @llvm.evm.create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) @@ -467,12 +467,12 @@ define i256 @create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) n define i256 @staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { ; CHECK-LABEL: @staticcall -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 +; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 +; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 ; CHECK: STATICCALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] %ret = call i256 @llvm.evm.staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) @@ -490,8 +490,8 @@ define void @selfdestruct(i256 %addr) nounwind { define void @return(ptr addrspace(1) %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @return -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: RETURN [[IN1]], [[IN2]] call void @llvm.evm.return(ptr addrspace(1) %rs1, i256 %rs2) @@ -500,8 +500,8 @@ define void @return(ptr addrspace(1) %rs1, i256 %rs2) nounwind { define void @revert(ptr addrspace(1) %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @revert -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: REVERT [[IN1]], [[IN2]] call void @llvm.evm.revert(ptr addrspace(1) %rs1, i256 %rs2) diff --git a/llvm/test/CodeGen/EVM/logical.ll b/llvm/test/CodeGen/EVM/logical.ll index 4839fb4e6ffa..43b75a30a4fe 100644 --- a/llvm/test/CodeGen/EVM/logical.ll +++ b/llvm/test/CodeGen/EVM/logical.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @andrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @andrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: AND [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = and i256 %rs1, %rs2 @@ -15,8 +15,8 @@ define i256 @andrrr(i256 %rs1, i256 %rs2) nounwind { define i256 @orrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @orrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: OR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = or i256 %rs1, %rs2 @@ -25,8 +25,8 @@ define i256 @orrrr(i256 %rs1, i256 %rs2) nounwind { define i256 @xorrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @xorrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: XOR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = xor i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/memory.ll b/llvm/test/CodeGen/EVM/memory.ll index 2cfbf3f906c4..6c60d756ea85 100644 --- a/llvm/test/CodeGen/EVM/memory.ll +++ b/llvm/test/CodeGen/EVM/memory.ll @@ -5,8 +5,8 @@ target triple = "evm" define void @mstore8(ptr addrspace(1) %offset, i256 %val) nounwind { ; CHECK-LABEL: @mstore8 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: MSTORE8 [[IN1]], [[IN2]] call void @llvm.evm.mstore8(ptr addrspace(1) %offset, i256 %val) @@ -15,8 +15,8 @@ define void @mstore8(ptr addrspace(1) %offset, i256 %val) nounwind { define void @mstore(ptr addrspace(1) %offset, i256 %val) nounwind { ; CHECK-LABEL: @mstore -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: MSTORE [[IN1]], [[IN2]] store i256 %val, ptr addrspace(1) %offset, align 32 diff --git a/llvm/test/CodeGen/EVM/mod.ll b/llvm/test/CodeGen/EVM/mod.ll index c0b4e9976952..8ab6f36ba61a 100644 --- a/llvm/test/CodeGen/EVM/mod.ll +++ b/llvm/test/CodeGen/EVM/mod.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @umodrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @umodrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: MOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = urem i256 %rs1, %rs2 @@ -15,8 +15,8 @@ define i256 @umodrrr(i256 %rs1, i256 %rs2) nounwind { define i256 @smodrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @smodrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = srem i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll index 9c286d256707..e2b89fc873bd 100644 --- a/llvm/test/CodeGen/EVM/mul.ll +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @mulrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @mulrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: MUL [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = mul i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll index 084ec33481a4..d5cb9af50ba7 100644 --- a/llvm/test/CodeGen/EVM/select.ll +++ b/llvm/test/CodeGen/EVM/select.ll @@ -5,10 +5,10 @@ target triple = "evm" define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { ; CHECK-LABEL: @select -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 +; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 +; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 ; CHECK: EQ [[TMP1:\$[0-9]+]], [[IN3]], [[IN4]] ; CHECK: ISZERO [[COND:\$[0-9]+]], [[TMP1]] ; CHECK: JUMPI @.BB0_2, [[COND]] diff --git a/llvm/test/CodeGen/EVM/stack-ops-commutable.ll b/llvm/test/CodeGen/EVM/stack-ops-commutable.ll new file mode 100644 index 000000000000..05fa5ec4b4f8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-ops-commutable.ll @@ -0,0 +1,416 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @no_manipulations_needed_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: no_manipulations_needed_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define void @no_manipulations_needed_with_junk_eq(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: no_manipulations_needed_with_junk_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %cmp = icmp eq i256 %a1, %a2 + %x1 = zext i1 %cmp to i256 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable + +} + +define i256 @no_manipulations_needed_no_junk_addmod(i256 %a1, i256 %a2, i256 %a3) { +; CHECK-LABEL: no_manipulations_needed_no_junk_addmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = call i256 @llvm.evm.addmod(i256 %a2, i256 %a1, i256 %a3) + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_mulmod(i256 %a1, i256 %a2, i256 %a3) { +; CHECK-LABEL: no_manipulations_needed_no_junk_mulmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MULMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = call i256 @llvm.evm.mulmod(i256 %a2, i256 %a1, i256 %a3) + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_and(i256 %a1, i256 %a2) { +; CHECK-LABEL: no_manipulations_needed_no_junk_and: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = and i256 %a2, %a1 + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_or(i256 %a1, i256 %a2) { +; CHECK-LABEL: no_manipulations_needed_no_junk_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = or i256 %a2, %a1 + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk_xor(i256 %a1, i256 %a2) { +; CHECK-LABEL: no_manipulations_needed_no_junk_xor: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: XOR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = xor i256 %a2, %a1 + ret i256 %x1 +} + +define i256 @no_manipulations_needed_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: no_manipulations_needed_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + ret i256 %x1 +} + +define void @reorder_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: reorder_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a2, %a1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @reorder_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: reorder_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a1 + ret i256 %x1 +} + +define void @swap_first_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: swap_first_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a3, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @two_commutable(i256 %a1, i256 %a2, i256 %a3) { +; CHECK-LABEL: two_commutable: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a3, %a2 + %x2 = add i256 %a1, %x1 + ret i256 %x2 +} + +define void @swap_second_with_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) noreturn { +; CHECK-LABEL: swap_second_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a4 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @swap_first_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_first_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a4 + ret i256 %x1 +} + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_second_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a4, %a1 + ret i256 %x1 +} + +define void @first_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: first_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @first_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: first_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @second_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: second_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @second_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: second_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @both_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: both_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = add i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @both_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: both_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + ret i256 %x3 +} + +define i256 @same_arg_dead_with_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_dead_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + ret i256 %x1 +} + +define void @commutable_not_in_function_entry() noreturn { +; CHECK-LABEL: commutable_not_in_function_entry: +; CHECK: ; %bb.0: ; %enter +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: CALLDATALOAD +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: .BB22_1: ; %header +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB22_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.2: ; %do +; CHECK-NEXT: ; in Loop: Header=BB22_1 Depth=1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB22_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB22_3: ; %exit +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 4294967295 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: MSTORE +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN + +enter: + %offset = inttoptr i256 0 to ptr addrspace(2) + %load = call i256 @llvm.evm.calldataload(ptr addrspace(2) %offset) + %calldata = trunc i256 %load to i32 + br label %header + +header: + %phi = phi i32 [ %calldata, %enter ], [ %inc, %do ] + %phi2 = phi i32 [ 1, %enter ], [ %mul, %do ] + %cmp = icmp sgt i32 %phi, 0 + br i1 %cmp, label %do, label %exit + +do: + %mul = mul nsw i32 %phi2, %phi + %inc = add nsw i32 %phi, -1 + br label %header + +exit: + %res = zext i32 %phi2 to i256 + store i256 %res, ptr addrspace(1) null, align 4 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.calldataload(ptr addrspace(2)) +declare void @llvm.evm.return(ptr addrspace(1), i256) +declare void @llvm.evm.revert(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/stack-ops.ll b/llvm/test/CodeGen/EVM/stack-ops.ll new file mode 100644 index 000000000000..a5256b83d006 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-ops.ll @@ -0,0 +1,354 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @no_manipulations_needed_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: no_manipulations_needed_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @no_manipulations_needed_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: no_manipulations_needed_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + ret i256 %x1 +} + +define void @reorder_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: reorder_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a2, %a1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @reorder_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: reorder_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a2, %a1 + ret i256 %x1 +} + +define void @swap_first_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: swap_first_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a3, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define void @swap_second_with_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) noreturn { +; CHECK-LABEL: swap_second_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a4 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @swap_first_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_first_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a3, %a2 + ret i256 %x1 +} + +define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_second_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a4 + ret i256 %x1 +} + +define void @swap_both_with_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) noreturn { +; CHECK-LABEL: swap_both_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a4, %a1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + unreachable +} + +define i256 @swap_both_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwind { +; CHECK-LABEL: swap_both_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a4, %a1 + ret i256 %x1 +} + +define void @first_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: first_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @first_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: first_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a1, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @second_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: second_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @second_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: second_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + %x2 = sub i256 %a2, 4 + %x3 = udiv i256 %x2, %x1 + ret i256 %x3 +} + +define void @both_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { +; CHECK-LABEL: both_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT + %x1 = sub i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x3) + unreachable +} + +define i256 @both_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: both_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = sub i256 %a1, %a2 + %x2 = udiv i256 %a2, %a1 + %x3 = add i256 %x1, %x2 + ret i256 %x3 +} + +define i256 @same_arg_dead_with_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_dead_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x1) + ret i256 %x1 +} + +define i256 @same_arg_dead_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_dead_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + ret i256 %x1 +} + +define i256 @same_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_alive_with_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + %x2 = add i256 %a2, %x1 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 %x2) + ret i256 %x2 +} + +define i256 @same_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: same_arg_alive_no_junk: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %x1 = add i256 %a2, %a2 + %x2 = add i256 %a2, %x1 + ret i256 %x2 +} + +declare void @llvm.evm.revert(ptr addrspace(1), i256) diff --git a/llvm/test/CodeGen/EVM/storage.ll b/llvm/test/CodeGen/EVM/storage.ll index 24336cd0164f..e66d6adfa95d 100644 --- a/llvm/test/CodeGen/EVM/storage.ll +++ b/llvm/test/CodeGen/EVM/storage.ll @@ -5,8 +5,8 @@ target triple = "evm" define void @sstore(ptr addrspace(5) %key, i256 %val) nounwind { ; CHECK-LABEL: @sstore -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SSTORE [[IN1]], [[IN2]] store i256 %val, ptr addrspace(5) %key, align 32 diff --git a/llvm/test/CodeGen/EVM/sub.ll b/llvm/test/CodeGen/EVM/sub.ll index 6b88285ef48d..ef913e9a9434 100644 --- a/llvm/test/CodeGen/EVM/sub.ll +++ b/llvm/test/CodeGen/EVM/sub.ll @@ -5,8 +5,8 @@ target triple = "evm" define i256 @subrrr(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: @subrrr -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: SUB [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] %res = sub i256 %rs1, %rs2 diff --git a/llvm/test/CodeGen/EVM/tstorage.ll b/llvm/test/CodeGen/EVM/tstorage.ll index c100d67d9992..8bb70f90e0e0 100644 --- a/llvm/test/CodeGen/EVM/tstorage.ll +++ b/llvm/test/CodeGen/EVM/tstorage.ll @@ -5,8 +5,8 @@ target triple = "evm" define void @tstore(ptr addrspace(6) %key, i256 %val) nounwind { ; CHECK-LABEL: @tstore -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 +; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK: TSTORE [[IN1]], [[IN2]] store i256 %val, ptr addrspace(6) %key, align 32 diff --git a/llvm/test/CodeGen/EVM/unused_function_arguments.ll b/llvm/test/CodeGen/EVM/unused_function_arguments.ll new file mode 100644 index 000000000000..50e68f31e91c --- /dev/null +++ b/llvm/test/CodeGen/EVM/unused_function_arguments.ll @@ -0,0 +1,52 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @foo(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: @foo +; CHECK: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %x1 = add i256 %a1, %a1 + ret i256 %x1 +} + +define i256 @wat(i256 %a1, i256 %a2, i256 %a3) nounwind { +; CHECK-LABEL: @wat +; CHECK: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %x1 = add i256 %a2, %a2 + ret i256 %x1 +} + +define i256 @bar() nounwind { +; CHECK-LABEL: @bar +; CHECK: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH4 @foo +; CHECK-NEXT: JUMP +; CHECK-LABEL: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @foo(i256 1, i256 2, i256 3) + ret i256 %res +} diff --git a/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll b/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll index 0bd23db7c62c..1d667d736a70 100644 --- a/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll +++ b/llvm/test/CodeGen/Generic/2007-01-15-LoadSelectCycle.ll @@ -1,5 +1,6 @@ ; RUN: llc < %s ; PR1114 +; UNSUPPORTED: target=evm{{.*}} declare i1 @foo() diff --git a/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll b/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll index e6f75fe024e1..5b60f4bffeae 100644 --- a/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll +++ b/llvm/test/CodeGen/Generic/2008-08-07-PtrToInt-SmallerInt.ll @@ -1,6 +1,7 @@ ; RUN: llc < %s -; XFAIL: target=evm{{.*}} + ; TODO: CPR-920 support operators +; UNSUPPORTED: target=evm{{.*}} ; PR2603 %struct.A = type { i8 } %struct.B = type { i8, [1 x i8] } diff --git a/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll b/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll index 605fe346c9d3..bed186acb538 100644 --- a/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll +++ b/llvm/test/CodeGen/Generic/2009-04-28-i128-cmp-crash.ll @@ -2,6 +2,7 @@ ; rdar://6836460 ; rdar://7516906 ; PR5963 +; UNSUPPORTED: target=evm{{.*}} define i32 @test(ptr %P) nounwind { entry: diff --git a/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll b/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll index f5f2276ac820..f72452fc0cbf 100644 --- a/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll +++ b/llvm/test/CodeGen/Generic/2011-07-07-ScheduleDAGCrash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} ; This caused ScheduleDAG to crash in EmitPhysRegCopy when searching ; the uses of a copy to a physical register without ignoring non-data diff --git a/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll b/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll index f08923669ece..ce3c4ec680df 100644 --- a/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll +++ b/llvm/test/CodeGen/Generic/2012-06-08-APIntCrash.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @test1(ptr %ptr) { diff --git a/llvm/test/CodeGen/Generic/i128-addsub.ll b/llvm/test/CodeGen/Generic/i128-addsub.ll index e61658ed2430..7a5e3e6f1e1f 100644 --- a/llvm/test/CodeGen/Generic/i128-addsub.ll +++ b/llvm/test/CodeGen/Generic/i128-addsub.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} define void @test_add(i64 %AL, i64 %AH, i64 %BL, i64 %BH, ptr %RL, ptr %RH) { entry: diff --git a/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll b/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll index 6cc2b4040d18..e9a4fe5a1955 100644 --- a/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll +++ b/llvm/test/CodeGen/Generic/multiple-return-values-cross-block-with-invoke.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s +; UNSUPPORTED: target=evm{{.*}} declare { i64, double } @wild() define void @foo(ptr %p, ptr %q) nounwind personality ptr @__gxx_personality_v0 { diff --git a/llvm/test/CodeGen/Generic/undef-phi.ll b/llvm/test/CodeGen/Generic/undef-phi.ll index 480b01fbf544..dcdb2abebe81 100644 --- a/llvm/test/CodeGen/Generic/undef-phi.ll +++ b/llvm/test/CodeGen/Generic/undef-phi.ll @@ -1,6 +1,7 @@ ; XFAIL: target=evm{{.*}} ; TODO: CPR-922 Fix ; 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/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll b/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll index 43fcc6051210..e50f90ad94b2 100644 --- a/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll +++ b/llvm/test/Transforms/BranchFolding/2007-10-19-InlineAsmDirectives.ll @@ -1,5 +1,6 @@ ; RUN: opt < %s -O3 | llc -no-integrated-as | FileCheck %s ; REQUIRES: default_triple +; UNSUPPORTED: target=evm{{.*}} ;; We don't want branch folding to fold asm directives. diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected index 4b6359646f18..4a4f1e007b5d 100644 --- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/evm-basic.ll.expected @@ -5,17 +5,12 @@ define i256 @swap_second_no_junk(i256 %a1, i256 %a2, i256 %a3, i256 %a4) nounwin ; CHECK-LABEL: swap_second_no_junk: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DUP2 -; CHECK-NEXT: DUP6 -; CHECK-NEXT: SUB -; CHECK-NEXT: SWAP5 -; CHECK-NEXT: POP -; CHECK-NEXT: DUP1 -; CHECK-NEXT: SWAP4 -; CHECK-NEXT: POP -; CHECK-NEXT: POP +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP ; CHECK-NEXT: POP +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %x1 = sub i256 %a4, %a1 ret i256 %x1 diff --git a/llvm/unittests/Target/EVM/CMakeLists.txt b/llvm/unittests/Target/EVM/CMakeLists.txt new file mode 100644 index 000000000000..315aef1b0650 --- /dev/null +++ b/llvm/unittests/Target/EVM/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/EVM + ${LLVM_BINARY_DIR}/lib/Target/EVM + ) + +set(LLVM_LINK_COMPONENTS + EVM + EVMDesc + EVMInfo + CodeGen + Core + MC + MIRParser + SelectionDAG + Support + Target + TargetParser +) + +add_llvm_target_unittest(EVMTests + StackShuffler.cpp + StackModel.cpp + ) + +set_property(TARGET EVMTests PROPERTY FOLDER "Tests/UnitTests/TargetTests") diff --git a/llvm/unittests/Target/EVM/StackModel.cpp b/llvm/unittests/Target/EVM/StackModel.cpp new file mode 100644 index 000000000000..a6f4afb5a793 --- /dev/null +++ b/llvm/unittests/Target/EVM/StackModel.cpp @@ -0,0 +1,147 @@ +//===---------- llvm/unittests/EVM/StackSlotBuilder.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 +// +//===----------------------------------------------------------------------===// + +#include "EVMStackModel.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/CodeGenCWrappers.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +static TargetMachine *unwrap(LLVMTargetMachineRef P) { + return reinterpret_cast(P); +} + +class EVMStackModelTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + + LLVMTargetRef Target = nullptr; + const char *Triple = "evm"; + char *ErrMsg = nullptr; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + Context = std::make_unique(); + Mod = std::make_unique("TestModule", *Context); + Mod->setDataLayout(unwrap(TM)->createDataLayout()); + const auto &LLVMTM = static_cast(*unwrap(TM)); + MMIWP = std::make_unique(&LLVMTM); + + Type *const ReturnType = Type::getVoidTy(Mod->getContext()); + FunctionType *FunctionType = FunctionType::get(ReturnType, false); + Function *const F = Function::Create( + FunctionType, GlobalValue::InternalLinkage, "TestFunction", Mod.get()); + MF = &MMIWP->getMMI().getOrCreateMachineFunction(*F); + + LISWP = std::make_unique(); + StackModel = std::make_unique( + *MF, LISWP->getLIS(), + MF->getSubtarget().stackDepthLimit()); + } + + void TearDown() override { LLVMDisposeTargetMachine(TM); } + +public: + LLVMTargetMachineRef TM = nullptr; + std::unique_ptr Context; + std::unique_ptr MMIWP; + std::unique_ptr LISWP; + std::unique_ptr Mod; + std::unique_ptr StackModel; + MachineFunction *MF = nullptr; +}; + +TEST_F(EVMStackModelTest, LiteralSlot) { + APInt Int0 = APInt(32, 0); + APInt Int42 = APInt(32, 42); + + auto *LiteralSlot0 = StackModel->getLiteralSlot(Int0); + auto *LiteralSlot0Copy = StackModel->getLiteralSlot(Int0); + EXPECT_TRUE(LiteralSlot0 == LiteralSlot0Copy); + + auto *LiteralSlot42 = StackModel->getLiteralSlot(Int42); + EXPECT_TRUE(LiteralSlot0 != LiteralSlot42); + EXPECT_TRUE(LiteralSlot0->getValue() != LiteralSlot42->getValue()); +} + +TEST_F(EVMStackModelTest, VariableSlot) { + MachineRegisterInfo &MRI = MF->getRegInfo(); + Register Reg1 = MRI.createVirtualRegister(&EVM::GPRRegClass); + Register Reg2 = MRI.createVirtualRegister(&EVM::GPRRegClass); + + auto *RegSlot1 = StackModel->getRegisterSlot(Reg1); + auto *RegSlot1Copy = StackModel->getRegisterSlot(Reg1); + EXPECT_TRUE(RegSlot1 == RegSlot1Copy); + + auto *RegSlot2 = StackModel->getRegisterSlot(Reg2); + EXPECT_TRUE(RegSlot1 != RegSlot2); + EXPECT_TRUE(RegSlot1->getReg() != RegSlot2->getReg()); +} + +TEST_F(EVMStackModelTest, SymbolSlot) { + MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto *MI = MF->CreateMachineInstr(MCID, DebugLoc()); + auto MAI = MCAsmInfo(); + auto MC = std::make_unique(Triple("evm"), &MAI, nullptr, nullptr, + nullptr, nullptr, false); + MCSymbol *Sym1 = MC->createTempSymbol("sym1", false); + MCSymbol *Sym2 = MC->createTempSymbol("sym2", false); + + auto *SymSlot1 = StackModel->getSymbolSlot(Sym1, MI); + auto *SymSlot1Copy = StackModel->getSymbolSlot(Sym1, MI); + EXPECT_TRUE(SymSlot1 == SymSlot1Copy); + + auto *SymSlot2 = StackModel->getSymbolSlot(Sym2, MI); + EXPECT_TRUE(SymSlot1 != SymSlot2); + EXPECT_TRUE(SymSlot1->getSymbol() != SymSlot2->getSymbol()); +} + +TEST_F(EVMStackModelTest, CallerReturnSlot) { + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + auto *Call = MF->CreateMachineInstr(TII->get(EVM::FCALL), DebugLoc()); + auto *Call2 = MF->CreateMachineInstr(TII->get(EVM::FCALL), DebugLoc()); + + auto *RetSlot1 = StackModel->getCallerReturnSlot(Call); + auto *RetSlot1Copy = StackModel->getCallerReturnSlot(Call); + EXPECT_TRUE(RetSlot1 == RetSlot1Copy); + + auto *RetSlot2 = StackModel->getCallerReturnSlot(Call2); + EXPECT_TRUE(RetSlot1 != RetSlot2); + EXPECT_TRUE(RetSlot1->getCall() != RetSlot2->getCall()); +} + +TEST_F(EVMStackModelTest, CalleeReturnSlot) { + // Be sure the slot for callee return (the MF) is a single one. + EXPECT_TRUE(StackModel->getCalleeReturnSlot(MF) == + StackModel->getCalleeReturnSlot(MF)); +} + +TEST_F(EVMStackModelTest, UnusedSlot) { + // Be sure the UnusedSlot is a single one. + EXPECT_TRUE(EVMStackModel::getUnusedSlot() == EVMStackModel::getUnusedSlot()); +} diff --git a/llvm/unittests/Target/EVM/StackShuffler.cpp b/llvm/unittests/Target/EVM/StackShuffler.cpp new file mode 100644 index 000000000000..b37871eaadd3 --- /dev/null +++ b/llvm/unittests/Target/EVM/StackShuffler.cpp @@ -0,0 +1,199 @@ +//===---------- llvm/unittests/MC/AssemblerTest.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 +// +//===----------------------------------------------------------------------===// + +#include "EVMRegisterInfo.h" +#include "EVMStackModel.h" +#include "EVMStackShuffler.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +#include +static TargetMachine *unwrap(LLVMTargetMachineRef P) { + return reinterpret_cast(P); +} + +class LLDCTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + + LLVMTargetRef Target = nullptr; + const char *Triple = "evm"; + char *ErrMsg = nullptr; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + Context = std::make_unique(); + Mod = std::make_unique("TestModule", *Context); + Mod->setDataLayout(unwrap(TM)->createDataLayout()); + const auto &LLVMTM = static_cast(*unwrap(TM)); + MMIWP = std::make_unique(&LLVMTM); + + Type *const ReturnType = Type::getVoidTy(Mod->getContext()); + FunctionType *FunctionType = FunctionType::get(ReturnType, false); + Function *const F = Function::Create( + FunctionType, GlobalValue::InternalLinkage, "TestFunction", Mod.get()); + MF = &MMIWP->getMMI().getOrCreateMachineFunction(*F); + + LISWP = std::make_unique(); + StackModel = std::make_unique( + *MF, LISWP->getLIS(), + MF->getSubtarget().stackDepthLimit()); + } + + void TearDown() override { LLVMDisposeTargetMachine(TM); } + +public: + LLVMTargetMachineRef TM = nullptr; + MachineFunction *MF = nullptr; + std::unique_ptr Context; + std::unique_ptr Mod; + std::unique_ptr MMIWP; + std::unique_ptr LISWP; + std::unique_ptr StackModel; +}; + +TEST_F(LLDCTest, Basic) { + Stack SourceStack; + Stack TargetStack; + + MachineBasicBlock *MBB = MF->CreateMachineBasicBlock(nullptr); + MF->push_back(MBB); + + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + const MCInstrDesc &MCID = TII->get(EVM::SELFBALANCE); + MachineRegisterInfo &MRI = MF->getRegInfo(); + + auto CreateInstr = [&]() { + Register Reg = MRI.createVirtualRegister(&EVM::GPRRegClass); + MachineInstr *MI = BuildMI(MBB, DebugLoc(), MCID, Reg); + return std::pair(MI, Reg); + }; + SmallVector> Instrs; + for (unsigned I = 0; I < 17; ++I) + Instrs.emplace_back(CreateInstr()); + + // Create the source stack: + // [ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET %5 ] + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[0].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[1].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[2].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[3].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[4].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[5].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[6].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[7].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[9].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[10].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[11].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[12].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[13].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[14].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[15].second)); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[16].second)); + SourceStack.emplace_back(StackModel->getCalleeReturnSlot(MBB->getParent())); + SourceStack.emplace_back(StackModel->getCalleeReturnSlot(MBB->getParent())); + SourceStack.emplace_back(StackModel->getRegisterSlot(Instrs[5].second)); + + // [ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET Unused Unused + // ] + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[1].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[0].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[2].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[3].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[4].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[5].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[6].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[7].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[9].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[10].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[11].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[12].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[13].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[14].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[15].second)); + TargetStack.emplace_back(StackModel->getRegisterSlot(Instrs[16].second)); + TargetStack.emplace_back(StackModel->getCalleeReturnSlot(MBB->getParent())); + TargetStack.emplace_back(EVMStackModel::getUnusedSlot()); + TargetStack.emplace_back(EVMStackModel::getUnusedSlot()); + + StringRef Reference("\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET %5 ]\n\ +POP\n\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET ]\n\ +SWAP16\n\ +[ %0 RET %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET %1 ]\n\ +SWAP16\n\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET ]\n\ +POP\n\ +[ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET ]\n\ +SWAP15\n\ +[ %0 RET %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 %1 ]\n\ +SWAP16\n\ +[ %1 RET %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 %0 ]\n\ +SWAP15\n\ +[ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET ]\n\ +PUSH Unused\n\ +[ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET Unused ]\n\ +PUSH Unused\n\ +[ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET Unused Unused ]\n"); + + std::ostringstream Output; + calculateStack( + SourceStack, TargetStack, + MF->getSubtarget().stackDepthLimit(), + [&](unsigned SwapDepth) { // swap + Output << SourceStack.toString() << '\n'; + Output << "SWAP" << SwapDepth << '\n'; + }, + [&](const StackSlot *Slot) { // dupOrPush + Output << SourceStack.toString() << '\n'; + if (Slot->isRematerializable()) + Output << "PUSH " << Slot->toString() << '\n'; + else { + Stack TmpStack = SourceStack; + std::reverse(TmpStack.begin(), TmpStack.end()); + auto *It = std::find(TmpStack.begin(), TmpStack.end(), Slot); + if (It == TmpStack.end()) + FAIL() << "Invalid DUP operation."; + + auto Depth = std::distance(TmpStack.begin(), It); + Output << "DUP" << Depth + 1 << '\n'; + } + }, + [&]() { // pop + Output << SourceStack.toString() << '\n'; + Output << "POP" << '\n'; + }); + + Output << SourceStack.toString() << '\n'; + std::cerr << Output.str(); + EXPECT_TRUE(Reference == Output.str()); +} From 2f2409198e7de7392cb2803fc7b26df160c007f2 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 12 Mar 2025 17:05:30 +0100 Subject: [PATCH 087/203] [InstCombine][EVM] Disable generation of llvm.?mul.with.overflow intrinsic Its default lowering in ISel triggers an assertion: LC != RTLIB::UNKNOWN_LIBCALL && "Cannot expand this operation!" when expanding ?MULO. While we could expand MULO using MUL_LOHI, we would first need to custom lower the MUL_LOHI operation, which requires a dozen instructions. Therefore, this approach doesn't seem beneficial. --- .../InstCombine/InstCombineCompares.cpp | 20 ++++++++++- .../EVM/no-mul-fold-to-overflow-int.ll | 34 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index b0114e429904..669f8b6f5f77 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -27,6 +27,9 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Support/KnownBits.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/InstCombine/InstCombiner.h" #include @@ -5354,9 +5357,24 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I, return new ICmpInst(ICmpInst::getSignedPredicate(Pred), Constant::getNullValue(Op0->getType()), Op0); } - + // EVM local begin + // Disable generation of llvm.?mul.with.overflow intrinsic for EVM, as + // its default lowering in ISel triggers an assertion: + // + // LC != RTLIB::UNKNOWN_LIBCALL && "Cannot expand this operation!" + // + // when expanding ISD::?MULO. While we could expand MULO using MUL_LOHI, + // we would first need to custom lower the MUL_LOHI operation, which + // requires a dozen instructions. + // TODO: #794: Investigate whether it is reasonable to enable the generation. + Triple TT(I.getFunction()->getParent()->getTargetTriple()); + if (!TT.isEVM()) { + // EVM local end if (Value *V = foldMultiplicationOverflowCheck(I)) return replaceInstUsesWith(I, V); + // EVM local begin + } + // EVM local end if (Instruction *R = foldICmpAndXX(I, Q, *this)) return R; diff --git a/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll b/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll new file mode 100644 index 000000000000..aa4f4f87e988 --- /dev/null +++ b/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll @@ -0,0 +1,34 @@ +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.revert(ptr addrspace(1), i256) + +define fastcc i256 @checked_mul(i256 %0) { +; CHECK-LABEL: checked_mul +; CHECK-NOT: @llvm.smul.with.overflow + +entry: + %multiplication_result = mul i256 100, %0 + %division_signed_is_divided_int_min = icmp eq i256 %multiplication_result, -57896044618658097711785492504343953926634992332820282019728792003956564819968 + br label %division_signed_non_overflow + +division_signed_non_overflow: + %division_signed_result_non_zero = sdiv i256 %multiplication_result, 100 + br label %division_signed_join + +division_signed_join: + %comparison_result = icmp eq i256 %0, %division_signed_result_non_zero + %comparison_result_extended = zext i1 %comparison_result to i256 + %comparison_result3 = icmp eq i256 %comparison_result_extended, 0 + %comparison_result_extended4 = zext i1 %comparison_result3 to i256 + br i1 %comparison_result3, label %if_main, label %if_join + +if_main: + call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 36) + unreachable + +if_join: + ret i256 %multiplication_result +} From 079792def0c895e426e97f955a27b66bcaa18a45 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 12 Mar 2025 17:40:37 +0100 Subject: [PATCH 088/203] [EVM] SingleUseExpr: check all regs of a multi-value instruction before moving it Consider the following example bb.0: liveins: %0, %4, %6 %1:gpr, %2:gpr = FCALL @multival, %0:gpr, %3:gpr = SIGNEXTEND %4:gpr, %2:gpr %5:gpr = AND %1:gpr, %6:gpr bb.1: liveins: %5, %3 Here FCALL should not be moved after SIGNEXTEND, as this would break data flow on the register '%2'. --- .../lib/Target/EVM/EVMSingleUseExpression.cpp | 22 +++++++++++ .../CodeGen/EVM/single-use-expr-mulivalue.mir | 39 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index 7c14b057c78d..c2528e069055 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -375,6 +375,28 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, for (const MachineOperand &MO : I->operands()) if (MO.isReg() && MO.isDef() && MO.getReg() == Reg) return false; + + // Check that subsequent defs of a multi-value instruction are not + // defined/used by instructions between 'DefI' and 'Insert'. + // For example, in the code: + // + // bb.0: + // liveins: %0, %4, %6 + // + // %1:gpr, %2:gpr = FCALL @multival, %0:gpr, + // %3:gpr = SIGNEXTEND %4:gpr, %2:gpr + // %5:gpr = AND %1:gpr, %6:gpr + // + // bb.1: + // liveins: %5, %3 + // + // FCALL should not be moved after SIGNEXTEND, as this breaks + // data flow on the register '%2'. + + for (const auto &SubDef : drop_begin(DefI->defs())) + for (const MachineOperand &MO : I->operands()) + if (MO.isReg() && MO.getReg() == SubDef.getReg()) + return false; } return true; diff --git a/llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir b/llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir new file mode 100644 index 000000000000..bdb47ec7a9c8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/single-use-expr-mulivalue.mir @@ -0,0 +1,39 @@ +# RUN: llc -x mir -run-pass evm-single-use-expressions -verify-machineinstrs < %s | FileCheck %s + +--- | + + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + declare void @multival() + define void @dont_move_mulivalue() { ret void } + +... +--- +# This test ensures that we do not move FCALL instruction +# after the SIGNEXTEND one. +# +# CHECK-LABEL: bb.1: +# CHECK: %3:gpr, %4:gpr = FCALL @multival, %0 +# CHECK-NEXT: %5:gpr = SIGNEXTEND %1, %4 +# CHECK-NEXT: %6:gpr = AND %3, %2 +name: dont_move_mulivalue +tracksRegLiveness: true +registers: + - { id: 0, class: gpr } +body: | + bb.0: + + %0:gpr = SELFBALANCE implicit-def dead $arguments + %1:gpr = SELFBALANCE implicit-def dead $arguments + %2:gpr = SELFBALANCE implicit-def dead $arguments + + bb.1: + + %3:gpr, %4:gpr = FCALL @multival, %0:gpr, implicit-def dead $arguments, implicit $sp + %5:gpr = SIGNEXTEND %1:gpr, %4:gpr, implicit-def dead $arguments + %6:gpr = AND %3:gpr, %2:gpr, implicit-def dead $arguments + + bb.2: + + RET %5:gpr, %6:gpr +... From 839caf9baf8c417a53167e5b7fbdde3c6053d2a3 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 7 Mar 2025 11:27:39 +0100 Subject: [PATCH 089/203] [lld][EVM] Add support of 'evm-assemble' mode In this mode, the linker follows the pipeline for an executable file, but ignores undefined symbols and emits unresolved relocations. However, the resulting binary has a relocatable type. --- lld/ELF/Arch/EVM.cpp | 2 ++ lld/ELF/Config.h | 3 +++ lld/ELF/Driver.cpp | 8 ++++++++ lld/ELF/InputSection.cpp | 39 +++++++++++++++++++++++++++++++++++++++ lld/ELF/Options.td | 2 ++ lld/ELF/Writer.cpp | 4 ++++ 6 files changed, 58 insertions(+) diff --git a/lld/ELF/Arch/EVM.cpp b/lld/ELF/Arch/EVM.cpp index a61dab9e1be3..9c083f590069 100644 --- a/lld/ELF/Arch/EVM.cpp +++ b/lld/ELF/Arch/EVM.cpp @@ -50,6 +50,8 @@ RelExpr EVM::getRelExpr(RelType type, const Symbol &s, switch (type) { case R_EVM_DATA: return R_ABS; + case R_EVM_NONE: + return R_NONE; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index ba554f4784bc..4072bfde42eb 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -293,6 +293,9 @@ struct Config { bool relaxGP; bool relocatable; bool resolveGroups; + // EVM local begin + bool evmAssembly; + // EVM local end bool relrGlibc = false; bool relrPackDynRelocs = false; llvm::DenseSet saveTempsArgs; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 472ee8c938b3..1fb6612b3b37 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1846,6 +1846,14 @@ static void readConfigs(opt::InputArgList &args) { } else { error(Twine("cannot find version script ") + arg->getValue()); } + + // EVM local begin + if (args.hasArg(OPT_evm_assembly)) { + config->evmAssembly = true; + config->unresolvedSymbols = UnresolvedPolicy::Ignore; + config->emitRelocs = true; + } + // EVM local end } // Some Config members do not directly correspond to any particular diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index a165c813d425..aedc14a19bc9 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -446,6 +446,45 @@ void InputSection::copyRelocations(uint8_t *buf, auto *p = reinterpret_cast(buf); buf += sizeof(RelTy); + // EVM local begin + // The --evm-assembly mode implies the --emit-relocs option, which + // instructs the linker to emit both applied relocations + // (for defined symbols) and unresolved relocations + // (for undefined symbols). We do not need to retain the applied + // relocations, as their symbols will be removed from the output file. + // To remove them from the '.rela' section, we would need to create a + // new synthetic section, copy only the necessary relocations there, + // and map that section to the output file. However, a simpler approach + // seems to be to use the original relocation sections and change the type + // of unnecessary relocations to 'NONE'. Please note that we cannot simply + // omit them from the output file, as by the time 'copyRelocations' is + // called, the ELF header (which describes section sizes) has already been + // emitted. Therefore, we cannot alter the number of relocations in the + // output + // '.rela' section. + // For example, the resulting '.rela' sections might look like: + // + // Relocation section '.rela.text' at offset 0x1038 contains 9 entries: + // + // Offset Info Type Sym. Value Symbol's Name + Addend + // 00000000 00000000 R_EVM_NONE 0 + // 00000000 00000000 R_EVM_NONE 0 + // 00000000 00000000 R_EVM_NONE 0 + // 00000000 00000000 R_EVM_NONE 0 + // 0000001f 00000601 R_EVM_DATA 00000000 __linker_symbol__0 + 0 + // 00000023 00000701 R_EVM_DATA 00000000 __linker_symbol__1 + 0 + // 00000027 00000801 R_EVM_DATA 00000000 __linker_symbol__2 + 0 + // 0000002b 00000901 R_EVM_DATA 00000000 __linker_symbol__3 + 0 + // 0000002f 00000a01 R_EVM_DATA 00000000 __linker_symbol__4 + 0 + // + if (config->evmAssembly && sym.isDefined()) { + p->r_offset = 0; + p->r_addend = 0; + p->setSymbolAndType(0, 0, false); + continue; + } + // EVM local end + if (RelTy::HasAddend) p->r_addend = rel.addend; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 74733efb28ff..4a76409cf09f 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -423,6 +423,8 @@ defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">; def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; +def evm_assembly: F<"evm-assembly">, HelpText<"Create evm assembly object file">; + defm retain_symbols_file: Eq<"retain-symbols-file", "Retain only the symbols listed in the file">, MetaVarName<"">; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 7ab0aa19087a..b8c728dacd4f 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2702,6 +2702,10 @@ static uint16_t getELFType() { return ET_DYN; if (config->relocatable) return ET_REL; + // EVM local begin + if (config->evmAssembly) + return ET_REL; + // EVM local end return ET_EXEC; } From 422a00e5fa8e50dd9f10677ada71127f5fe05da6 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 14 Mar 2025 12:00:09 +0100 Subject: [PATCH 090/203] [EVM] Add dependencies support This patch adds new LLD API function: \p codeSegment, 0 means inBuffers[0] is deploy code, 1 - runtime code; \p inBuffers - relocatable ELF files to be assembled \p inBuffersIDs - their IDs \p outBuffer - resulting relocatable object file LLVMBool LLVMAssembleEVM(const LLVMMemoryBufferRef inBuffers[], const char *const inBuffersIDs[], uint64_t numInBuffers, LLVMMemoryBufferRef *outBuffer, char **errorMessage); which performs the following steps, which are based on Ethereum's Assembly::assemble() logic: - Concatenates the .text sections of input ELF files referenced by __dataoffset* symbols from the first file. - Resolves undefined __dataoffset* and __datasize* symbols. - Gathers all undefined linker symbols (library references) from all files. - Ensures that the first file does not load and set immutables simultaneously. The frontend is expected to call this API for each contract in the dependency graph. Once the root is assembled, the resulting file should be passed to the LLVMLinkEVM LLD API functions to resolve library references and generate the bytecode. --- lld/include/lld-c/LLDAsLibraryC.h | 70 +- lld/lld-c/CMakeLists.txt | 1 + lld/lld-c/LLDAsLibraryC.cpp | 682 +++++++++++------- lld/unittests/EVM/Inputs/A_deploy.ll | 20 + lld/unittests/EVM/Inputs/A_deployed.ll | 13 + lld/unittests/EVM/Inputs/D_deploy.ll | 20 + lld/unittests/EVM/Inputs/D_deployed.ll | 9 + lld/unittests/EVM/Inputs/R_deploy.ll | 34 + lld/unittests/EVM/Inputs/R_deployed.ll | 26 + lld/unittests/EVM/Inputs/deployIr.ll | 21 + lld/unittests/EVM/Inputs/deployedIr.ll | 13 + lld/unittests/EVM/Inputs/undefDeployIr.ll | 13 + lld/unittests/EVM/Inputs/undefDeployedIr.ll | 13 + lld/unittests/EVM/LLDTest.cpp | 343 +++++---- .../EVM/MCTargetDesc/EVMMCTargetDesc.cpp | 36 +- .../Target/EVM/MCTargetDesc/EVMMCTargetDesc.h | 15 +- 16 files changed, 906 insertions(+), 423 deletions(-) create mode 100644 lld/unittests/EVM/Inputs/A_deploy.ll create mode 100644 lld/unittests/EVM/Inputs/A_deployed.ll create mode 100644 lld/unittests/EVM/Inputs/D_deploy.ll create mode 100644 lld/unittests/EVM/Inputs/D_deployed.ll create mode 100644 lld/unittests/EVM/Inputs/R_deploy.ll create mode 100644 lld/unittests/EVM/Inputs/R_deployed.ll create mode 100644 lld/unittests/EVM/Inputs/deployIr.ll create mode 100644 lld/unittests/EVM/Inputs/deployedIr.ll create mode 100644 lld/unittests/EVM/Inputs/undefDeployIr.ll create mode 100644 lld/unittests/EVM/Inputs/undefDeployedIr.ll diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h index a0640b9be73c..ed8f5b3b1b4d 100644 --- a/lld/include/lld-c/LLDAsLibraryC.h +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -73,56 +73,58 @@ LLVM_C_EXTERN_C_BEGIN // only usage is to represent Ethereum addresses which are of 160 bit width. #define LINKER_SYMBOL_SIZE 20 -/** Returns true if the \p inBuffer contains an ELF object file. */ +/** Returns true if the \p inBuffer contains an EVM ELF object file. */ LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer); +/** + * Performs the following steps, which are based on Ethereum's + * Assembly::assemble() logic: + * - Concatenates the .text sections of input ELF files referenced + * by __dataoffset* symbols from the first file. + * - Resolves undefined __dataoffset* and __datasize* symbols. + * - Gathers all undefined linker symbols (library references) from + * all files. + * - Ensures that the first file does not load and set + * immutables simultaneously. + * + * \p codeSegment, 0 means the first file has a deploy code, + * 1 - runtime code; + * \p inBuffers - relocatable ELF files to be assembled + * \p inBuffersIDs - their IDs + * \p outBuffer - resulting relocatable object file */ +LLVMBool LLVMAssembleEVM(uint64_t codeSegment, + const LLVMMemoryBufferRef inBuffers[], + const char *const inBuffersIDs[], + uint64_t inBuffersNum, LLVMMemoryBufferRef *outBuffer, + char **errorMessage); + +/** Returns an array of undefined linker symbol names + * from the ELF object provided in \p inBuffer. */ void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, char ***linkerSymbols, uint64_t *numLinkerSymbols); -/** Disposes an array with symbols returned by the - * LLVMGetUndefinedReferences* functions. */ +/** Disposes of the array of symbols returned by the + * 'LLVMGetUndefinedReferences' function. */ void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols); -/** 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. - * \p linkerSymbolNames has the same meaning as for LLVMLinkEVM. - * If at least one resulting binary contains unresolved linker symbols, - * output binaries will be returned as ELF object files. See LLVMLinkEVM - * description. - * 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], +/** Resolves undefined linker symbols in the ELF object file \p inBuffer. + * Returns the ELF object file if any linker symbols remain unresolved; + * otherwise, returns the bytecode. */ +LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffer, + LLVMMemoryBufferRef *outBuffer, const char *const *linkerSymbolNames, const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], uint64_t numLinkerSymbols, char **errorMessage); -/// Returns immutables and their offsets of the ELF object -/// file passed in \p inBuffer. +/** Returns the immutable symbol names and their offsets from the ELF + * object file provided in \p inBuffer. */ uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, char ***immutableIDs, uint64_t **immutableOffsets); -/// Disposes immutable names and their offsets returned by the -/// LLVMGetImmutablesEVM. +/** Disposes of the immutable names and their offsets returned by + * 'LLVMGetImmutablesEVM'. */ void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, uint64_t numOfImmutables); LLVM_C_EXTERN_C_END diff --git a/lld/lld-c/CMakeLists.txt b/lld/lld-c/CMakeLists.txt index 3e4e20806d6f..1ab04cd41b0b 100644 --- a/lld/lld-c/CMakeLists.txt +++ b/lld/lld-c/CMakeLists.txt @@ -6,6 +6,7 @@ add_lld_library(lldC LINK_COMPONENTS Core EVMDesc + ObjCopy Object Support diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp index 393c25cc51d0..9d6e0d4633ee 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -2,10 +2,14 @@ #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "llvm-c/Core.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" #include "llvm/Object/Binary.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ObjectFile.h" @@ -25,19 +29,23 @@ using namespace llvm; using namespace object; +using namespace objcopy; LLD_HAS_DRIVER_MEM_BUF(elf) namespace llvm { namespace EVM { std::string getLinkerSymbolHash(StringRef SymName); +bool isLinkerSymbolName(StringRef Name); std::string getLinkerSymbolName(StringRef SymName); +bool isSymbolSectionName(StringRef Name); std::string getSymbolSectionName(StringRef Name); std::string getSymbolIndexedName(StringRef Name, unsigned SubIdx); std::string getDataSizeSymbol(StringRef SymbolName); -std::string getDataOffsetSymbol(StringRef SymbolName); std::string getNonIndexedSymbolName(StringRef Name); -bool isLinkerSymbolName(StringRef Name); +bool isDataOffsetSymbolName(StringRef Name); +std::string getDataOffsetSymbol(StringRef Name); +std::string extractDataOffseteName(StringRef SymbolName); bool isLoadImmutableSymbolName(StringRef Name); std::string getImmutableId(StringRef Name); } // namespace EVM @@ -213,134 +221,7 @@ void LLVMDisposeUndefinedReferences(char *symbolNames[], uint64_t numSymbols) { //----------------------------------------------------------------------------// -/// 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(); - - auto getDataOffsetName = [](StringRef name) { - return EVM::getDataOffsetSymbol(EVM::getLinkerSymbolHash(name)); - }; - auto getDataSizeName = [](StringRef name) { - return EVM::getDataSizeSymbol(EVM::getLinkerSymbolHash(name)); - }; - - // Define the script part related to the top-level contract. - std::string topName = EVM::getLinkerSymbolHash(bufIDs[0]); - std::string deployed = EVM::getLinkerSymbolHash(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; - std::string topLevelBuf; - raw_string_ostream topLevel(topLevelBuf); - topLevel << topName << "(.text);\n" - << EVM::getDataOffsetSymbol(deployed) << " = .;\n" - << deployed << "(.text);\n" - << EVM::getDataSizeSymbol(deployed) << " = . - " - << EVM::getDataOffsetSymbol(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 dataSizeBuf; - raw_string_ostream symDatasizeDeps(dataSizeBuf); - - // Contains symbols whose values are the offsets of the dependent contracts. - // For the example above, this contains: - // __dataoffset_B_40 = .; - std::string dataOffsetBuf; - raw_string_ostream symDataOffsetDeps(dataOffsetBuf); - 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 << getDataSizeName(bufIDs[idx]) << " = " - << LLVMGetBufferSize(memBufs[idx]) << ";\n"; - - symDataOffsetDeps << getDataOffsetName(bufIDs[2]) << " = .;\n"; - for (unsigned idx = 3; idx < numObjectsToLink; ++idx) - symDataOffsetDeps << getDataOffsetName(bufIDs[idx]) << " = " - << getDataOffsetName(bufIDs[idx - 1]) << " + " - << getDataSizeName(bufIDs[idx - 1]) << ";\n"; - } - - // Contains a symbol whose value is the total size of the top-level contract - // with all the dependencies. - std::string dataSizeTopBuf; - raw_string_ostream symDatasizeTop(dataSizeTopBuf); - symDatasizeTop << EVM::getDataSizeSymbol(topName) << " = "; - if (numObjectsToLink > 2) - symDatasizeTop << getDataOffsetName(bufIDs.back()) << " + " - << getDataSizeName(bufIDs.back()) << ";\n"; - 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. - std::string deploySize = - "LONG(" + EVM::getDataOffsetSymbol(deployed) + ");\n"; - - std::string script = - formatv("{0}\n\ -ENTRY(0);\n\ -SECTIONS {\n\ - . = 0;\n\ - .code : SUBALIGN(1) {\n\ -{1}\ -{2}\ -{3}\ -{4}\ - }\n\ -}\n\ -", - symDatasizeDeps.str(), topLevel.str(), symDataOffsetDeps.str(), - symDatasizeTop.str(), deploySize); - - return script; -} - -/// Create linker script as described in the createEVMRelLinkerScript. +/// Create linker script with linker symbol definitions. static std::string createEVMLinkerSymbolsDefinitions( const char *const *linkerSymbolNames, const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], @@ -349,16 +230,60 @@ static std::string createEVMLinkerSymbolsDefinitions( numLinkerSymbols); } -/// Resolve undefined linker symbols in the ELF object file \inBuffer -/// and return the obtained ELF object file. -static bool -resolveEVMLinkerSymbols(MemoryBufferRef inBuffer, - LLVMMemoryBufferRef *outBuffer, - const char *const *linkerSymbolNames, - const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], - uint64_t numLinkerSymbols, char **errorMessage) { +/// Returns true if the \p inBuffer contains an EVM ELF object file. +LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer) { + Expected> inBinaryOrErr = + ELFObjectFile::create(unwrap(inBuffer)->getMemBufferRef()); + if (!inBinaryOrErr) { + handleAllErrors(inBinaryOrErr.takeError(), [](const ErrorInfoBase &EI) {}); + return false; + } + return inBinaryOrErr.get().getArch() == Triple::evm; +} + +/// Returns an array of undefined linker symbol names +/// from the ELF object provided in \p inBuffer. +void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, + char ***linkerSymbols, + uint64_t *numLinkerSymbols) { + assert(linkerSymbols && numLinkerSymbols); + *linkerSymbols = nullptr; + *numLinkerSymbols = 0; + if (!LLVMIsELFEVM(inBuffer)) + return; + + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + const auto *oFile = static_cast(inBinary.get()); + + *linkerSymbols = LLVMGetUndefinedSymbols(oFile, numLinkerSymbols, + ReferenceSymbolType::Linker); +} + +/// Resolves undefined linker symbols in the ELF object file \p inBuffer. +/// Returns the ELF object file if any linker symbols remain unresolved; +/// otherwise, returns the bytecode. +LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffer, + LLVMMemoryBufferRef *outBuffer, + const char *const *linkerSymbolNames, + const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], + uint64_t numLinkerSymbols, char **errorMessage) { SmallVector localInMemBufRefs(2); - localInMemBufRefs[0] = inBuffer; + localInMemBufRefs[0] = *unwrap(inBuffer); + + *outBuffer = nullptr; + if (!LLVMIsELFEVM(inBuffer)) { + *errorMessage = strdup("Input binary is not an EVM ELF file"); + return true; + } + + std::unique_ptr inBinary = + cantFail(createBinary(localInMemBufRefs[0])); + assert(inBinary->isObject()); + bool shouldEmitRelocatable = + hasUndefinedReferenceSymbols(*static_cast(inBinary.get()), + linkerSymbolNames, numLinkerSymbols); + std::string linkerScript = createEVMLinkerSymbolsDefinitions( linkerSymbolNames, linkerSymbolValues, numLinkerSymbols); @@ -369,31 +294,36 @@ resolveEVMLinkerSymbols(MemoryBufferRef inBuffer, SmallVector lldArgs; lldArgs.push_back("ld.lld"); + + // Push the name of the linker script - '1'. lldArgs.push_back("-T"); lldArgs.push_back("1"); - lldArgs.push_back("--relocatable"); - // Push the name of the input object file - '0'. lldArgs.push_back("0"); + // If all symbols are resolved, strip the ELF format and emit the final + // bytecode. Otherwise, emit an ELF relocatable file. + if (shouldEmitRelocatable) + lldArgs.push_back("--relocatable"); + else + 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. + // Lld-as-a-library is not thread-safe due to its global state, + // so we need to protect it from concurrent access by multiple 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; - // For unification with other LLVM C-API functions, return 'true' in case of - // an error. - if (!Ret) { + bool ret = !s.retCode && s.canRunAgain; + if (!ret) { *errorMessage = strdup(errorString.c_str()); return true; } @@ -401,133 +331,386 @@ resolveEVMLinkerSymbols(MemoryBufferRef inBuffer, StringRef data = ostream.str(); *outBuffer = LLVMCreateMemoryBufferWithMemoryRangeCopy(data.data(), data.size(), "result"); + return false; } -/// Returns true if the \p inBuffer contains an ELF object file. -LLVMBool LLVMIsELFEVM(LLVMMemoryBufferRef inBuffer) { - Expected> inBinaryOrErr = - ELFObjectFile::create(unwrap(inBuffer)->getMemBufferRef()); - if (!inBinaryOrErr) { - handleAllErrors(inBinaryOrErr.takeError(), [](const ErrorInfoBase &EI) {}); - return false; +/// Finds symbol name sections and adds them to the \p namesMap. +/// A symbol name section has the following format: +/// +/// .symbol_name__linker_symbol__$[0-9a-f]{64}$__ +/// +static void getSymbolNameSections(const ObjectFile *oFile, const char *fileID, + StringMap> &namesMap) { + [[maybe_unused]] SmallVector sectionNames; + for (const SectionRef &sec : oFile->sections()) { + StringRef secName = cantFail(sec.getName()); + if (EVM::isSymbolSectionName(secName)) { + namesMap[secName].push_back(fileID); +#ifndef NDEBUG + sectionNames.push_back(secName); +#endif // NDEBUG + } } - return inBinaryOrErr.get().getArch() == Triple::evm; + +#ifndef NDEBUG + // Verify that undefined linker symbols exist for the name sections. + StringMap expectedSymNames; + for (StringRef secName : sectionNames) { + StringRef refName = getSectionContent(*oFile, secName); + for (unsigned idx = 0; idx < LINKER_SYMBOL_SIZE / subSymbolRelocSize; + ++idx) { + std::string symName = getLinkerSubSymbolName(refName, idx); + expectedSymNames[symName]++; + } + } + + StringMap actualSymNames; + for (const SymbolRef &sym : oFile->symbols()) { + section_iterator symSec = cantFail(sym.getSection()); + // Undefined symbol has no related section. + if (symSec != oFile->section_end()) + continue; + + StringRef symName = cantFail(sym.getName()); + if (EVM::isLinkerSymbolName(symName)) + actualSymNames[symName]++; + } + assert(actualSymNames == expectedSymNames); +#endif // NDEBUG } -/// Returns an array of undefined linker symbol names -/// of the ELF object passed in \p inBuffer. -void LLVMGetUndefinedReferencesEVM(LLVMMemoryBufferRef inBuffer, - char ***linkerSymbols, - uint64_t *numLinkerSymbols) { - if (linkerSymbols) { - *numLinkerSymbols = 0; - *linkerSymbols = nullptr; +/// Creates a linker script to generate an 'assembly' ELF object file. +/// Here is an example of the script: +/// +/// ENTRY(0); +/// SECTIONS { +/// . = 0; +/// .text : SUBALIGN(1) { +/// __datasize__$12c297efd8baf$__ = 268; +/// __dataoffset__$b5e66d52578d$__ = ABSOLUTE(.); +/// __$b5e66d52578d$__(.text); +/// __dataoffset__$12c297efd8baf$__ = ABSOLUTE(.); +/// __$12c297efd8baf$__(.text); +/// __datasize__$b5e66d52578d$__ = ABSOLUTE(.); +/// } +/// /DISCARD/ : { +/// } +/// } +/// VERSION { +/// { local: +/// __datasize__$12c297efd8baf$__; +/// __dataoffset__$b5e66d52578d$__; +/// __dataoffset__$12c297efd8baf$__; +/// __datasize__$b5e66d52578d$__; +/// }; +/// }; + +static std::string createEVMAssembeScript(ArrayRef memBufs, + ArrayRef depIDs, + const StringSet<> &depsToLink) { + assert(memBufs.size() == depIDs.size()); + + // Maps symbol name section to an array of object IDs that contain it. + StringMap> symbolNameSectionsMap; + + auto getDataSizeName = [](StringRef name) { + return EVM::getDataSizeSymbol(EVM::getLinkerSymbolHash(name)); + }; + + // A set of all the defined symbols in the script that should be removed + // from the symbol table. To achieve this, we set their visibility + // to 'local'. These symbols will later be removed using the 'objcopy' API. + std::string definedSymbolsBuf; + raw_string_ostream definedSymbols(definedSymbolsBuf); + + std::unique_ptr firstBinary = + cantFail(createBinary(unwrap(memBufs[0])->getMemBufferRef())); + const auto *firstObjFile = static_cast(firstBinary.get()); + getSymbolNameSections(firstObjFile, depIDs[0], symbolNameSectionsMap); + + std::string textSectionBuf; + raw_string_ostream textSection(textSectionBuf); + + // A set of all the defined symbols in the script that should be removed + // from the symbol table. To achieve this, we set their visibility to + // 'local'. These symbols will later be removed using the 'objcopy' API. + for (unsigned idx = 1; idx < memBufs.size(); ++idx) { + std::unique_ptr binary = + cantFail(createBinary(unwrap(memBufs[idx])->getMemBufferRef())); + const auto *objFile = static_cast(binary.get()); + std::string bufIdHash = EVM::getLinkerSymbolHash(depIDs[idx]); + if (depsToLink.count(bufIdHash)) + getSymbolNameSections(objFile, depIDs[idx], symbolNameSectionsMap); + + for (const SectionRef &sec : objFile->sections()) { + if (sec.isText()) { + assert(cantFail(sec.getName()) == ".text"); + std::string sym = getDataSizeName(depIDs[idx]); + definedSymbols << sym << ";\n"; + textSection << sym << " = " << sec.getSize() << ";\n"; + break; + } + } } - if (!LLVMIsELFEVM(inBuffer)) - return; + // __dataoffset_Id_1 = .; + // Id_1(.text); + // __dataoffset_Id_2 = .; + // Id_2(.text); + // ... + // __dataoffset_Id_N = .; + // Id_N(.text); + for (unsigned idx = 0; idx < memBufs.size(); ++idx) { + std::string bufIdHash = EVM::getLinkerSymbolHash(depIDs[idx]); + // Do not link the dependency if it's not referenced via + // __dataoffset. + if (idx != 0 && !depsToLink.count(bufIdHash)) + continue; + + std::string sym = EVM::getDataOffsetSymbol(bufIdHash); + definedSymbols << sym << ";\n"; + textSection << sym << " = ABSOLUTE(.);\n"; + textSection << bufIdHash << "(.text);\n"; + } + + // Define a symbol whose value is the total size of the output + // '.text' section. + std::string firstDataSizeSym = getDataSizeName(depIDs[0]); + definedSymbols << firstDataSizeSym << ";\n"; + textSection << firstDataSizeSym << " = ABSOLUTE(.);\n"; + + // When assembling multiple ELF files that reference the same library, + // the files will have identical (name and content) sections: + // + // .symbol_name__linker_symbol__$[0-9a-f]{64}$__ + // + // By default, the linker will concatenate the sections, which is not desired. + // We need to retain only one section with the original content in the output + // file. To achieve this, we discard all duplicate input sections and keep + // just one. + std::string discardSectionsBuf; + raw_string_ostream discardSections(discardSectionsBuf); + for (const auto &[secName, IDs] : symbolNameSectionsMap) { + // We need to remove all symbol name sections, retaining just one + // (regardless of which one). The sections to be removed should be placed + // in the /DISCARD/ output section. + assert(IDs.size() > 0); + for (StringRef id : drop_begin(IDs)) { + std::string idHash = EVM::getLinkerSymbolHash(id); + discardSections << idHash << '(' << secName << ");\n"; + } + } + std::string script = + formatv(R"(ENTRY(0); +SECTIONS { + . = 0; + .text : SUBALIGN(1) { +{0} + } +/DISCARD/ : { +{2} +} +} +VERSION { + { local: +{1} + }; +};)", + textSection.str(), definedSymbols.str(), discardSections.str()); + + return script; +} + +/// Removes all local symbols from the object file, except for undefined +/// linker symbols and immutables from the first file. +/// This is done using the 'objcopy' API. +static LLVMMemoryBufferRef removeLocalSymbols(LLVMMemoryBufferRef inBuffer, + StringSet<> firstFileImmutables) { std::unique_ptr inBinary = cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); - const auto *oFile = static_cast(inBinary.get()); + auto *oFile = static_cast(inBinary.get()); - if (linkerSymbols) - *linkerSymbols = LLVMGetUndefinedSymbols(oFile, numLinkerSymbols, - ReferenceSymbolType::Linker); + StringSet<> undefReferenceSymbols; + for (const SymbolRef &sym : oFile->symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other == ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) { + undefReferenceSymbols.insert(cantFail(sym.getName())); + } + } + + auto errCallback = [](Error E) { + report_fatal_error(StringRef(toString(std::move(E)))); + return Error::success(); + }; + + // Create an 'objcopy' configuration that removes all local + // symbols while explicitly retaining the necessary ones. + ConfigManager Config; + Config.Common.DiscardMode = DiscardType::All; + for (const StringSet<>::value_type &entry : + llvm::concat::value_type>(undefReferenceSymbols, + firstFileImmutables)) { + if (Error E = Config.Common.SymbolsToKeep.addMatcher(NameOrPattern::create( + entry.first(), MatchStyle::Literal, errCallback))) + report_fatal_error(StringRef(toString(std::move(E)))); + } + + SmallString<0> dataVector; + raw_svector_ostream outStream(dataVector); + + if (Error Err = objcopy::executeObjcopyOnBinary(Config, *oFile, outStream)) + report_fatal_error(StringRef(toString(std::move(Err)))); + + MemoryBufferRef buffer(StringRef(dataVector.data(), dataVector.size()), ""); + Expected> result = createBinary(buffer); + + // Check the copied file. + if (!result) + report_fatal_error(StringRef(toString(result.takeError()))); + + StringRef data = outStream.str(); + StringRef bufName = unwrap(inBuffer)->getBufferIdentifier(); + return LLVMCreateMemoryBufferWithMemoryRangeCopy(data.data(), data.size(), + bufName.str().c_str()); } -/// Links the deploy and runtime ELF object files using the information about -// dependencies. -LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], - const char *inBuffersIDs[], uint64_t numInBuffers, - LLVMMemoryBufferRef outBuffers[2], - const char *const *linkerSymbolNames, - const char linkerSymbolValues[][LINKER_SYMBOL_SIZE], - uint64_t numLinkerSymbols, char **errorMessage) { - assert(numInBuffers > 1); - SmallVector localInMemBufRefs(3); - SmallVector> localInMemBufs(3); +/// Checks if the ELF file contains any undefined symbols, aside from +/// those used to represent library addresses. +static void getUndefinedNonRefSymbols(LLVMMemoryBufferRef inBuffer, + SmallVectorImpl &undefSyms) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + auto *oFile = static_cast(inBinary.get()); + + for (const SymbolRef &sym : oFile->symbols()) { + uint32_t symFlags = cantFail(sym.getFlags()); + uint8_t other = ELFSymbolRef(sym).getOther(); + if ((other != ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined)) + undefSyms.push_back(cantFail(sym.getName())); + } +} - // TODO: #740. Verify that the object files contain sections with original - // inBuffersIDs, i.e. before taking hash. - for (unsigned idx = 0; idx < 2; ++idx) { +/// Performs the following steps, which are based on Ethereum's +/// Assembly::assemble() logic: +/// - Concatenates the .text sections of input ELF files referenced +/// by __dataoffset* symbols from the first file. +/// - Resolves undefined __dataoffset* and __datasize* symbols. +/// - Gathers all undefined linker symbols (library references) from +/// all files. +/// - Ensures that the first file does not load and set +/// immutables simultaneously. +/// +/// \p codeSegment, 0 means the first file has a deploy code, +/// 1 - runtime code; +/// \p inBuffers - relocatable ELF files to be assembled +/// \p inBuffersIDs - their IDs +/// \p outBuffer - resulting relocatable object file +LLVMBool LLVMAssembleEVM(uint64_t codeSegment, + const LLVMMemoryBufferRef inBuffers[], + const char *const inBuffersIDs[], + uint64_t inBuffersNum, LLVMMemoryBufferRef *outBuffer, + char **errorMessage) { + SmallVector localInMemBufRefs(inBuffersNum + 1); + SmallVector> localInMemBufs(inBuffersNum + 1); + + // TODO: #740. Verify that the object files contain sections with the original + // inBuffersIDs, i.e. before the hash is applied. + for (unsigned idx = 0; idx < inBuffersNum; ++idx) { MemoryBufferRef ref = *unwrap(inBuffers[idx]); - // We need to copy buffers to be able to change their names, as this matters - // for the linker. + // We need to copy the buffers to change their names, as the linking + // process depends on them. localInMemBufs[idx] = MemoryBuffer::getMemBufferCopy( ref.getBuffer(), EVM::getLinkerSymbolHash(inBuffersIDs[idx])); localInMemBufRefs[idx] = localInMemBufs[idx]->getMemBufferRef(); } - // We need to check 'init' and 'deploy' ELF files for the undefined reference - // symbols. They are located at the beginning of the localInMemBufRefs. - bool shouldEmitRelocatable = std::any_of( - localInMemBufRefs.begin(), std::next(localInMemBufRefs.begin(), 2), - [linkerSymbolNames, numLinkerSymbols](MemoryBufferRef bufRef) { - std::unique_ptr InBinary = cantFail(createBinary(bufRef)); - assert(InBinary->isObject()); - return hasUndefinedReferenceSymbols( - *static_cast(InBinary.get()), linkerSymbolNames, - numLinkerSymbols); - }); - - if (shouldEmitRelocatable) { - for (unsigned idx = 0; idx < 2; ++idx) { - LLVMMemoryBufferRef outBuf = nullptr; - char *errMsg = nullptr; - if (resolveEVMLinkerSymbols(localInMemBufRefs[idx], &outBuf, - linkerSymbolNames, linkerSymbolValues, - numLinkerSymbols, &errMsg)) { - *errorMessage = errMsg; - return true; - } - outBuffers[idx] = outBuf; + std::unique_ptr firstBinary = + cantFail(createBinary(localInMemBufRefs[0])); + const auto *firstObjFile = static_cast(firstBinary.get()); + + // Retrieve the object names referenced by the first file. + // Retrieve the immutable symbols from the first file. + StringSet<> firstLoadImmutables; + StringSet<> firstDataOffsetRefs; + for (const SymbolRef &sym : firstObjFile->symbols()) { + StringRef symName = cantFail(sym.getName()); + if (EVM::isLoadImmutableSymbolName(symName)) + firstLoadImmutables.insert(symName); + else if (EVM::isDataOffsetSymbolName(symName)) { + std::string objName = EVM::extractDataOffseteName(symName); + firstDataOffsetRefs.insert(objName); } - return false; } - std::string linkerScript = createEVMLinkerSymbolsDefinitions( - linkerSymbolNames, linkerSymbolValues, numLinkerSymbols); + // Check if the first (idx == 1) dependency file contains loadimmutable + // symbols. + // A 'codeSegment' value of 0 indicates that the first file contains a + // deploy code, which implies that the first dependency is the corresponding + // runtime code. + bool depHasLoadImmutable = false; + if (codeSegment == 0 && inBuffersNum > 1) { + std::unique_ptr binary = + cantFail(createBinary(localInMemBufRefs[1])); + const auto *oFile = static_cast(binary.get()); + + for (const SymbolRef &sym : oFile->symbols()) { + section_iterator symSec = cantFail(sym.getSection()); + if (symSec == oFile->section_end()) + continue; - linkerScript += creteEVMLinkerScript(ArrayRef(inBuffers, numInBuffers), - ArrayRef(inBuffersIDs, numInBuffers)); + StringRef symName = cantFail(sym.getName()); + if (EVM::isLoadImmutableSymbolName(symName)) + depHasLoadImmutable = true; + } + } + + if (firstLoadImmutables.size() > 0 && depHasLoadImmutable) + report_fatal_error("lld: assembly both sets up and loads immutables"); + + std::string linkerScript = createEVMAssembeScript( + ArrayRef(inBuffers, inBuffersNum), ArrayRef(inBuffersIDs, inBuffersNum), + firstDataOffsetRefs); std::unique_ptr scriptBuf = MemoryBuffer::getMemBuffer(linkerScript, "script.x"); - localInMemBufRefs[2] = scriptBuf->getMemBufferRef(); + localInMemBufRefs[inBuffersNum] = 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 + // Use file name remapping (a linker feature) to replace file names with // indexes in the array of memory buffers. const std::string remapStr("--remap-inputs="); - std::string topHash = EVM::getLinkerSymbolHash(inBuffersIDs[0]); - std::string deployedHash = EVM::getLinkerSymbolHash(inBuffersIDs[1]); - std::string remapDeployStr = remapStr + topHash + "=0"; - lldArgs.push_back(remapDeployStr.c_str()); + SmallVector args; + for (unsigned idx = 0; idx < inBuffersNum; ++idx) { + std::string idHash = EVM::getLinkerSymbolHash(inBuffersIDs[idx]); + if (idx > 0 && !firstDataOffsetRefs.count(idHash)) + continue; - std::string remapDeployedStr = remapStr + deployedHash + "=1"; - lldArgs.push_back(remapDeployedStr.c_str()); + args.emplace_back(remapStr + idHash + "=" + std::to_string(idx)); + args.emplace_back(std::to_string(idx)); + } + + args.emplace_back("-T"); + args.emplace_back(std::to_string(inBuffersNum)); - lldArgs.push_back("--remap-inputs=script.x=2"); + for (const std::string &arg : args) + lldArgs.push_back(arg.c_str()); - // Deploy code - lldArgs.push_back(topHash.c_str()); - // Deployed code - lldArgs.push_back(deployedHash.c_str()); - lldArgs.push_back("--oformat=binary"); + lldArgs.push_back("--evm-assembly"); 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. + // Lld-as-a-library is not thread-safe due to its global state, + // so we need to protect it from concurrent access by multiple threads. std::unique_lock lock(lldMutex); const lld::Result s = lld::lldMainMemBuf(localInMemBufRefs, &ostream, lldArgs, outs(), @@ -541,24 +724,29 @@ LLVMBool LLVMLinkEVM(LLVMMemoryBufferRef inBuffers[], } 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"); + LLVMMemoryBufferRef Tmp = LLVMCreateMemoryBufferWithMemoryRangeCopy( + data.data(), data.size(), + EVM::getLinkerSymbolHash(inBuffersIDs[0]).c_str()); + + SmallVector undefSyms; + getUndefinedNonRefSymbols(Tmp, undefSyms); + if (!undefSyms.empty()) { + std::string storage; + raw_string_ostream errMsg(storage); + for (StringRef name : undefSyms) + errMsg << "non-ref undefined symbol: " << name << '\n'; + + *errorMessage = strdup(errMsg.str().c_str()); + return true; + } + + *outBuffer = removeLocalSymbols(Tmp, firstLoadImmutables); return false; } -/// Returns immutables and their offsets of the ELF object -/// file passed in \p inBuffer. +/// Returns the immutable symbol names and their offsets from the ELF +/// object file provided in \p inBuffer. uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, char ***immutableIDs, uint64_t **immutableOffsets) { @@ -612,8 +800,8 @@ uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, return numOfImmutables; } -/// Disposes immutable names and their offsets returned by the -/// LLVMGetImmutablesEVM. +/// Disposes of the immutable names and their offsets returned by +/// 'LLVMGetImmutablesEVM'. void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, uint64_t numOfImmutables) { for (unsigned idx = 0; idx < numOfImmutables; ++idx) diff --git a/lld/unittests/EVM/Inputs/A_deploy.ll b/lld/unittests/EVM/Inputs/A_deploy.ll new file mode 100644 index 000000000000..7d045d64faba --- /dev/null +++ b/lld/unittests/EVM/Inputs/A_deploy.ll @@ -0,0 +1,20 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +define i256 @init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @args_len() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %codesize = tail call i256 @llvm.evm.codesize() + %res = sub i256 %codesize, %datasize + ret i256 %res +} +!1 = !{!"A_38_deployed"} +!2 = !{!"A_38"} diff --git a/lld/unittests/EVM/Inputs/A_deployed.ll b/lld/unittests/EVM/Inputs/A_deployed.ll new file mode 100644 index 000000000000..134e349b975b --- /dev/null +++ b/lld/unittests/EVM/Inputs/A_deployed.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.linkersymbol(metadata) +declare i256 @llvm.evm.datasize(metadata) + +define i256 @runtime() { + %lib = call i256 @llvm.evm.linkersymbol(metadata !2) + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %res = add i256 %lib, %datasize + ret i256 %res +} +!1 = !{!"A_38_deployed"} +!2 = !{!"library_id"} diff --git a/lld/unittests/EVM/Inputs/D_deploy.ll b/lld/unittests/EVM/Inputs/D_deploy.ll new file mode 100644 index 000000000000..9eb4097fd5e6 --- /dev/null +++ b/lld/unittests/EVM/Inputs/D_deploy.ll @@ -0,0 +1,20 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +define i256 @init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @args_len() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %codesize = tail call i256 @llvm.evm.codesize() + %res = sub i256 %codesize, %datasize + ret i256 %res +} +!1 = !{!"D_51_deployed"} +!2 = !{!"D_51"} diff --git a/lld/unittests/EVM/Inputs/D_deployed.ll b/lld/unittests/EVM/Inputs/D_deployed.ll new file mode 100644 index 000000000000..cc0d99edb63a --- /dev/null +++ b/lld/unittests/EVM/Inputs/D_deployed.ll @@ -0,0 +1,9 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @runtime() { + %res = call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %res +} +!1 = !{!"40"} diff --git a/lld/unittests/EVM/Inputs/R_deploy.ll b/lld/unittests/EVM/Inputs/R_deploy.ll new file mode 100644 index 000000000000..883d5146050c --- /dev/null +++ b/lld/unittests/EVM/Inputs/R_deploy.ll @@ -0,0 +1,34 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.codesize() + +define i256 @init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @A_init() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !4) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !4) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @args_len() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %codesize = tail call i256 @llvm.evm.codesize() + %res = sub i256 %codesize, %datasize + ret i256 %res +} +define i256 @A_runtime() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !3) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !3) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +!1 = !{!"R_107_deployed"} +!2 = !{!"R_107"} +!3 = !{!"A_38.A_38_deployed"} +!4 = !{!"A_38"} diff --git a/lld/unittests/EVM/Inputs/R_deployed.ll b/lld/unittests/EVM/Inputs/R_deployed.ll new file mode 100644 index 000000000000..97b4b99057c0 --- /dev/null +++ b/lld/unittests/EVM/Inputs/R_deployed.ll @@ -0,0 +1,26 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.loadimmutable(metadata) +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) + +define i256 @runtime() { + %res = tail call i256 @llvm.evm.loadimmutable(metadata !1) + ret i256 %res +} + +define i256 @get_runtimecode() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !2) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +define i256 @get_initcode() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !3) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !3) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +!1 = !{!"53"} +!2 = !{!"A_38.A_38_deployed"} +!3 = !{!"D_51"} diff --git a/lld/unittests/EVM/Inputs/deployIr.ll b/lld/unittests/EVM/Inputs/deployIr.ll new file mode 100644 index 000000000000..1ad574a4702a --- /dev/null +++ b/lld/unittests/EVM/Inputs/deployIr.ll @@ -0,0 +1,21 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.linkersymbol(metadata) + +define i256 @foo() { + %res = call i256 @llvm.evm.linkersymbol(metadata !1) + ret i256 %res +} + +define i256 @bar() { + %linkersym = call i256 @llvm.evm.linkersymbol(metadata !1) + %datasize = tail call i256 @llvm.evm.datasize(metadata !2) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !2) + %tmp = add i256 %datasize, %dataoffset + %res = add i256 %tmp, %linkersym + ret i256 %res +} +!1 = !{!"library_id"} +!2 = !{!"Test_26_deployed"} diff --git a/lld/unittests/EVM/Inputs/deployedIr.ll b/lld/unittests/EVM/Inputs/deployedIr.ll new file mode 100644 index 000000000000..e1ec57393ad1 --- /dev/null +++ b/lld/unittests/EVM/Inputs/deployedIr.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.linkersymbol(metadata) +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @foo() { + %res = call i256 @llvm.evm.linkersymbol(metadata !1) + %res2 = call i256 @llvm.evm.loadimmutable(metadata !2) + %res3 = add i256 %res, %res2 + ret i256 %res3 +} +!1 = !{!"library_id2"} +!2 = !{!"id"} diff --git a/lld/unittests/EVM/Inputs/undefDeployIr.ll b/lld/unittests/EVM/Inputs/undefDeployIr.ll new file mode 100644 index 000000000000..9b888a550783 --- /dev/null +++ b/lld/unittests/EVM/Inputs/undefDeployIr.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.datasize(metadata) +declare i256 @llvm.evm.dataoffset(metadata) +declare i256 @llvm.evm.linkersymbol(metadata) + +define i256 @bar() { + %datasize = tail call i256 @llvm.evm.datasize(metadata !1) + %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !1) + %res = add i256 %datasize, %dataoffset + ret i256 %res +} +!1 = !{!"unknown"} diff --git a/lld/unittests/EVM/Inputs/undefDeployedIr.ll b/lld/unittests/EVM/Inputs/undefDeployedIr.ll new file mode 100644 index 000000000000..e1ec57393ad1 --- /dev/null +++ b/lld/unittests/EVM/Inputs/undefDeployedIr.ll @@ -0,0 +1,13 @@ +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" +declare i256 @llvm.evm.linkersymbol(metadata) +declare i256 @llvm.evm.loadimmutable(metadata) + +define i256 @foo() { + %res = call i256 @llvm.evm.linkersymbol(metadata !1) + %res2 = call i256 @llvm.evm.loadimmutable(metadata !2) + %res3 = add i256 %res, %res2 + ret i256 %res3 +} +!1 = !{!"library_id2"} +!2 = !{!"id"} diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp index 818bb9eee8c2..fecf4fbcf5d7 100644 --- a/lld/unittests/EVM/LLDTest.cpp +++ b/lld/unittests/EVM/LLDTest.cpp @@ -11,14 +11,26 @@ #include "llvm-c/IRReader.h" #include "llvm-c/TargetMachine.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/KECCAK.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include using namespace llvm; +#include +static std::string expand(const char *Path) { + llvm::SmallString<256> ThisPath; + ThisPath.append(getenv("LLD_SRC_DIR")); + llvm::sys::path::append(ThisPath, "unittests", "EVM", "Inputs", Path); + std::cerr << "Full path: " << ThisPath.str().str() << '\n'; + return ThisPath.str().str(); +} + class LLDCTest : public testing::Test { void SetUp() override { LLVMInitializeEVMTargetInfo(); @@ -50,89 +62,67 @@ class LLDCTest : public testing::Test { } public: - LLVMTargetMachineRef TM; - LLVMContextRef Context; -}; - -TEST_F(LLDCTest, IterativeLinkage) { - StringRef DeployIr = "\ -target datalayout = \"E-p:256:256-i256:256:256-S256-a:256:256\" \n\ -target triple = \"evm\" \n\ -declare i256 @llvm.evm.datasize(metadata) \n\ -declare i256 @llvm.evm.dataoffset(metadata) \n\ -declare i256 @llvm.evm.linkersymbol(metadata) \n\ - \n\ -define i256 @foo() { \n\ - %res = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ - ret i256 %res \n\ -} \n\ - \n\ -define i256 @bar() { \n\ - %linkersym = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ - %datasize = tail call i256 @llvm.evm.datasize(metadata !2) \n\ - %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !2) \n\ - %tmp = add i256 %datasize, %dataoffset \n\ - %res = add i256 %tmp, %linkersym \n\ - ret i256 %res \n\ -} \n\ -!1 = !{!\"library_id\"} \n\ -!2 = !{!\"Test_26_deployed\"}"; - - StringRef DeployedIr = "\ -target datalayout = \"E-p:256:256-i256:256:256-S256-a:256:256\" \n\ -target triple = \"evm\" \n\ -declare i256 @llvm.evm.linkersymbol(metadata) \n\ -declare i256 @llvm.evm.loadimmutable(metadata) \n\ - \n\ -define i256 @foo() { \n\ - %res = call i256 @llvm.evm.linkersymbol(metadata !1) \n\ - %res2 = call i256 @llvm.evm.loadimmutable(metadata !2) \n\ - %res3 = add i256 %res, %res2 \n\ - ret i256 %res3 \n\ -} \n\ -!1 = !{!\"library_id2\"} \n\ -!2 = !{!\"id\"}"; - - // Wrap Source in a MemoryBuffer - LLVMMemoryBufferRef DeployIrMemBuffer = LLVMCreateMemoryBufferWithMemoryRange( - DeployIr.data(), DeployIr.size(), "deploy", 1); - char *ErrMsg = nullptr; - LLVMModuleRef DeployMod; - if (LLVMParseIRInContext(Context, DeployIrMemBuffer, &DeployMod, &ErrMsg)) { - FAIL() << "Failed to parse llvm ir:" << ErrMsg; - LLVMDisposeMessage(ErrMsg); - return; + LLVMMemoryBufferRef link(LLVMMemoryBufferRef InAssembly, const char *Name, + const char *LinkerSyms[], + const char LinkerSymVals[][20], uint64_t NumSyms) { + char *ErrMsg = nullptr; + LLVMMemoryBufferRef OutAssembly = nullptr; + if (LLVMLinkEVM(InAssembly, &OutAssembly, LinkerSyms, LinkerSymVals, + NumSyms, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + return OutAssembly; } - LLVMMemoryBufferRef DeployedIrMemBuffer = - LLVMCreateMemoryBufferWithMemoryRange(DeployedIr.data(), - DeployedIr.size(), "deploy", 1); - LLVMModuleRef DeployedMod; - if (LLVMParseIRInContext(Context, DeployedIrMemBuffer, &DeployedMod, - &ErrMsg)) { - FAIL() << "Failed to parse llvm ir:" << ErrMsg; - LLVMDisposeMessage(ErrMsg); - return; + LLVMMemoryBufferRef assemble(uint64_t codeSegment, + const std::vector &Objs, + const std::vector &IDs) { + char *ErrMsg = nullptr; + LLVMMemoryBufferRef OutAssembly = nullptr; + if (LLVMAssembleEVM(codeSegment, Objs.data(), IDs.data(), Objs.size(), + &OutAssembly, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + EXPECT_TRUE(LLVMIsELFEVM(OutAssembly)); + return OutAssembly; } - // Run CodeGen to produce the buffers. - LLVMMemoryBufferRef DeployObjMemBuffer; - LLVMMemoryBufferRef DeployedObjMemBuffer; - if (LLVMTargetMachineEmitToMemoryBuffer(TM, DeployMod, LLVMObjectFile, - &ErrMsg, &DeployObjMemBuffer)) { - FAIL() << "Failed to compile llvm ir:" << ErrMsg; - LLVMDisposeModule(DeployMod); - LLVMDisposeMessage(ErrMsg); - return; - } - if (LLVMTargetMachineEmitToMemoryBuffer(TM, DeployedMod, LLVMObjectFile, - &ErrMsg, &DeployedObjMemBuffer)) { - FAIL() << "Failed to compile llvm ir:" << ErrMsg; - LLVMDisposeModule(DeployedMod); - LLVMDisposeMessage(ErrMsg); - return; + LLVMMemoryBufferRef compileIR(const char *FileName) { + auto Buf = MemoryBuffer::getFile(expand(FileName), /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (auto EC = Buf.getError()) + exit(1); + + LLVMMemoryBufferRef IrBuf = llvm::wrap(Buf.get().release()); + char *ErrMsg = nullptr; + LLVMModuleRef Module; + if (LLVMParseIRInContext(Context, IrBuf, &Module, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + + // Run CodeGen to produce the buffers. + LLVMMemoryBufferRef Result; + if (LLVMTargetMachineEmitToMemoryBuffer(TM, Module, LLVMObjectFile, &ErrMsg, + &Result)) { + LLVMDisposeModule(Module); + LLVMDisposeMessage(ErrMsg); + exit(1); + } + LLVMDisposeModule(Module); + return Result; } + LLVMTargetMachineRef TM; + LLVMContextRef Context; +}; + +TEST_F(LLDCTest, IterativeLinkage) { + LLVMMemoryBufferRef DeployObjMemBuffer = compileIR("deployIr.ll"); + LLVMMemoryBufferRef DeployedObjMemBuffer = compileIR("deployedIr.ll"); + EXPECT_TRUE(LLVMIsELFEVM(DeployObjMemBuffer)); EXPECT_TRUE(LLVMIsELFEVM(DeployedObjMemBuffer)); @@ -144,109 +134,196 @@ define i256 @foo() { \n\ StringRef SymVal1(LinkerSymbolVal[0], 20); StringRef SymVal2(LinkerSymbolVal[1], 20); - std::array InMemBuf = {DeployObjMemBuffer, - DeployedObjMemBuffer}; - std::array OutMemBuf = {nullptr, nullptr}; const char *InIDs[] = {"Test_26", "Test_26_deployed"}; + std::array InData = {DeployObjMemBuffer, + DeployedObjMemBuffer}; + LLVMMemoryBufferRef InMemBuf = nullptr; + LLVMMemoryBufferRef OutMemBuf = nullptr; + char *ErrMsg = nullptr; + + // Assemble deploy with deployed. + { + if (LLVMAssembleEVM(/*codeSegment=*/0, InData.data(), InIDs, 2, &OutMemBuf, + &ErrMsg)) { + FAIL() << "Failed to assemble:" << ErrMsg; + LLVMDisposeMessage(ErrMsg); + return; + } + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf)); + std::swap(OutMemBuf, InMemBuf); + } - // Check load immutable references + // Check load immutable references. { char **ImmutableIDs = nullptr; uint64_t *ImmutableOffsets = nullptr; uint64_t ImmCount = - LLVMGetImmutablesEVM(InMemBuf[1], &ImmutableIDs, &ImmutableOffsets); + LLVMGetImmutablesEVM(InData[1], &ImmutableIDs, &ImmutableOffsets); EXPECT_TRUE(ImmCount == 1); EXPECT_TRUE(std::strcmp(ImmutableIDs[0], "id") == 0); LLVMDisposeImmutablesEVM(ImmutableIDs, ImmutableOffsets, ImmCount); } - // No linker symbol definitions are provided, so we have to receive two ELF - // object files. - if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), nullptr, nullptr, - 0, &ErrMsg)) { + // No linker symbol definitions are provided, so we have to receive ELF + // object file. + if (LLVMLinkEVM(InMemBuf, &OutMemBuf, nullptr, nullptr, 0, &ErrMsg)) { FAIL() << "Failed to link:" << ErrMsg; LLVMDisposeMessage(ErrMsg); return; } - EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[0])); - EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[1])); + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf)); char **UndefLinkerSyms = nullptr; uint64_t NumLinkerUndefs = 0; - LLVMGetUndefinedReferencesEVM(OutMemBuf[0], &UndefLinkerSyms, - &NumLinkerUndefs); - EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[0]) == 0)); - LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); + LLVMGetUndefinedReferencesEVM(OutMemBuf, &UndefLinkerSyms, &NumLinkerUndefs); - LLVMGetUndefinedReferencesEVM(OutMemBuf[1], &UndefLinkerSyms, - &NumLinkerUndefs); - EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[1]) == 0)); + EXPECT_TRUE(NumLinkerUndefs == 2); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[0]) == 0)); + EXPECT_TRUE((std::strcmp(UndefLinkerSyms[1], LinkerSymbol[1]) == 0)); LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); - InMemBuf.swap(OutMemBuf); - LLVMDisposeMemoryBuffer(OutMemBuf[0]); - LLVMDisposeMemoryBuffer(OutMemBuf[1]); + std::swap(OutMemBuf, InMemBuf); + LLVMDisposeMemoryBuffer(OutMemBuf); - // The first linker symbol definitions is provided, so we still have to - // receive two ELF object files, because of the undefined second reference. - if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), LinkerSymbol, - LinkerSymbolVal, 1, &ErrMsg)) { + // The first linker symbol definitions is provided, so we still have + // to receive an ELF object file + if (LLVMLinkEVM(InMemBuf, &OutMemBuf, LinkerSymbol, LinkerSymbolVal, 1, + &ErrMsg)) { FAIL() << "Failed to link:" << ErrMsg; LLVMDisposeMessage(ErrMsg); return; } - EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[0])); - EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf[1])); + EXPECT_TRUE(LLVMIsELFEVM(OutMemBuf)); - LLVMGetUndefinedReferencesEVM(OutMemBuf[0], &UndefLinkerSyms, - &NumLinkerUndefs); - EXPECT_TRUE(NumLinkerUndefs == 0); - LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); - - LLVMGetUndefinedReferencesEVM(OutMemBuf[1], &UndefLinkerSyms, - &NumLinkerUndefs); + LLVMGetUndefinedReferencesEVM(OutMemBuf, &UndefLinkerSyms, &NumLinkerUndefs); + EXPECT_TRUE(NumLinkerUndefs == 1); EXPECT_TRUE((std::strcmp(UndefLinkerSyms[0], LinkerSymbol[1]) == 0)); LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); - InMemBuf.swap(OutMemBuf); - LLVMDisposeMemoryBuffer(OutMemBuf[0]); - LLVMDisposeMemoryBuffer(OutMemBuf[1]); + std::swap(OutMemBuf, InMemBuf); + LLVMDisposeMemoryBuffer(OutMemBuf); // Both linker symbol definitions are provided, so we have to receive - // bytecodes files. - if (LLVMLinkEVM(InMemBuf.data(), InIDs, 2, OutMemBuf.data(), LinkerSymbol, - LinkerSymbolVal, 2, &ErrMsg)) { + // a bytecode. + if (LLVMLinkEVM(InMemBuf, &OutMemBuf, LinkerSymbol, LinkerSymbolVal, 2, + &ErrMsg)) { FAIL() << "Failed to link:" << ErrMsg; LLVMDisposeMessage(ErrMsg); return; } - EXPECT_TRUE(!LLVMIsELFEVM(OutMemBuf[0])); - EXPECT_TRUE(!LLVMIsELFEVM(OutMemBuf[1])); + EXPECT_TRUE(!LLVMIsELFEVM(OutMemBuf)); - LLVMGetUndefinedReferencesEVM(OutMemBuf[0], &UndefLinkerSyms, - &NumLinkerUndefs); + LLVMGetUndefinedReferencesEVM(OutMemBuf, &UndefLinkerSyms, &NumLinkerUndefs); EXPECT_TRUE(NumLinkerUndefs == 0); LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); - LLVMGetUndefinedReferencesEVM(OutMemBuf[1], &UndefLinkerSyms, - &NumLinkerUndefs); + StringRef DeployBin(LLVMGetBufferStart(OutMemBuf), + LLVMGetBufferSize(OutMemBuf)); + + EXPECT_TRUE(DeployBin.find(SymVal1) != StringRef::npos); + EXPECT_TRUE(DeployBin.find(SymVal2) != StringRef::npos); + + LLVMDisposeMemoryBuffer(OutMemBuf); + LLVMDisposeMemoryBuffer(InMemBuf); +} + +TEST_F(LLDCTest, Assembly) { + const char *LinkerSymbol[2] = {"unused_library_id", "library_id"}; + const char LinkerSymbolVal[2][20] = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9}}; + + LLVMMemoryBufferRef A_deploy_obj = compileIR("A_deploy.ll"); + LLVMMemoryBufferRef A_deployed_obj = compileIR("A_deployed.ll"); + LLVMMemoryBufferRef D_deploy_obj = compileIR("D_deploy.ll"); + LLVMMemoryBufferRef D_deployed_obj = compileIR("D_deployed.ll"); + LLVMMemoryBufferRef R_deploy_obj = compileIR("R_deploy.ll"); + LLVMMemoryBufferRef R_deployed_obj = compileIR("R_deployed.ll"); + + // A assemble. + LLVMMemoryBufferRef A_assembly_deployed = + assemble(/*codeSegment=*/1, {A_deployed_obj}, {"A_38_deployed"}); + LLVMMemoryBufferRef A_assembly = + assemble(/*codeSegment=*/0, {A_deploy_obj, A_deployed_obj}, + {"A_38", "A_38_deployed"}); + + // D assemble. + LLVMMemoryBufferRef D_assembly = + assemble(/*codeSegment=*/0, {D_deploy_obj, D_deployed_obj}, + {"D_51", "D_51_deployed"}); + + // R_deployed assemble. + // A_assembly is not required here, but we add it intentionaly to check + // that it will be ignored (the total number of library reference is 3). + LLVMMemoryBufferRef R_deployed_assemble = + assemble(/*codeSegment=*/1, + {R_deployed_obj, D_assembly, A_assembly, A_assembly_deployed}, + {"R_107_deployed", "D_51", "A_38", "A_38.A_38_deployed"}); + + // R assemble. + LLVMMemoryBufferRef R_assembly = assemble( + /*codeSegment=*/0, + {R_deploy_obj, R_deployed_assemble, A_assembly, A_assembly_deployed}, + {"R_107", "R_107_deployed", "A_38", "A_38.A_38_deployed"}); + + // Linking with no linker symbols. + LLVMMemoryBufferRef TmpAssembly = link(R_assembly, "R", nullptr, nullptr, 0); + EXPECT_TRUE(LLVMIsELFEVM(TmpAssembly)); + + // Linking with unused linker symbol. It has no effect. + LLVMMemoryBufferRef TmpAssembly2 = + link(TmpAssembly, "R", LinkerSymbol, LinkerSymbolVal, 1); + EXPECT_TRUE(LLVMIsELFEVM(TmpAssembly2)); + + // Linking with both linker symbols. The library reference should be resolved + // and resulting object is a final bytecode. + LLVMMemoryBufferRef Bytecode = + link(TmpAssembly2, "R", LinkerSymbol, LinkerSymbolVal, 2); + EXPECT_TRUE(!LLVMIsELFEVM(Bytecode)); + + char **UndefLinkerSyms = nullptr; + uint64_t NumLinkerUndefs = 0; + + LLVMGetUndefinedReferencesEVM(Bytecode, &UndefLinkerSyms, &NumLinkerUndefs); EXPECT_TRUE(NumLinkerUndefs == 0); LLVMDisposeUndefinedReferences(UndefLinkerSyms, NumLinkerUndefs); - StringRef DeployBin(LLVMGetBufferStart(OutMemBuf[0]), - LLVMGetBufferSize(OutMemBuf[0])); - StringRef DeployedBin(LLVMGetBufferStart(OutMemBuf[1]), - LLVMGetBufferSize(OutMemBuf[1])); + StringRef Binary(LLVMGetBufferStart(Bytecode), LLVMGetBufferSize(Bytecode)); - EXPECT_TRUE(DeployBin.find(SymVal1) != StringRef::npos); - EXPECT_TRUE(DeployedBin.find(SymVal2) != StringRef::npos); + StringRef LibAddr(LinkerSymbolVal[1], 20); + EXPECT_TRUE(Binary.count(LibAddr) == 3); - for (unsigned I = 0; I < 2; ++I) { - LLVMDisposeMemoryBuffer(OutMemBuf[I]); - LLVMDisposeMemoryBuffer(InMemBuf[I]); - } + LLVMDisposeMemoryBuffer(A_deploy_obj); + LLVMDisposeMemoryBuffer(A_deployed_obj); + LLVMDisposeMemoryBuffer(D_deploy_obj); + LLVMDisposeMemoryBuffer(D_deployed_obj); + LLVMDisposeMemoryBuffer(R_deploy_obj); + LLVMDisposeMemoryBuffer(R_deployed_obj); + LLVMDisposeMemoryBuffer(TmpAssembly); + LLVMDisposeMemoryBuffer(TmpAssembly2); + LLVMDisposeMemoryBuffer(Bytecode); +} + +TEST_F(LLDCTest, UndefNonRefSymbols) { + LLVMMemoryBufferRef DeployObj = compileIR("undefDeployIr.ll"); + LLVMMemoryBufferRef DeployedObj = compileIR("undefDeployedIr.ll"); + + EXPECT_TRUE(LLVMIsELFEVM(DeployObj)); + EXPECT_TRUE(LLVMIsELFEVM(DeployedObj)); + + const std::array InObjs = {DeployObj, DeployedObj}; + const std::array IDs = {"Test_26", "Test_26_deployed"}; + LLVMMemoryBufferRef OutObj = nullptr; + char *ErrMsg = nullptr; + EXPECT_TRUE(LLVMAssembleEVM(/*codeSegment=*/0, InObjs.data(), IDs.data(), + IDs.size(), &OutObj, &ErrMsg)); + EXPECT_TRUE(StringRef(ErrMsg).contains("non-ref undefined symbol:")); + LLVMDisposeMessage(ErrMsg); + + LLVMDisposeMemoryBuffer(DeployObj); + LLVMDisposeMemoryBuffer(DeployedObj); } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp index a721a9eb03bc..0700022c5d30 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.cpp @@ -136,6 +136,12 @@ std::string EVM::getSymbolSectionName(StringRef Name) { return (Twine(".symbol_name") + Name).str(); } + +// Returns true if the \p Name starts with '.symbol_name'. +bool EVM::isSymbolSectionName(StringRef Name) { + return Name.find(".symbol_name") == 0; +} + // Strips index from the \p Name. std::string EVM::getNonIndexedSymbolName(StringRef Name) { Regex suffixRegex(R"(.*[0-4]$)"); @@ -153,12 +159,34 @@ bool EVM::isLinkerSymbolName(StringRef Name) { return Name.find("__linker_symbol") == 0; } -std::string EVM::getDataSizeSymbol(StringRef SymbolName) { - return (Twine("__datasize") + SymbolName).str(); +std::string EVM::getDataSizeSymbol(StringRef Name) { + return (Twine("__datasize") + Name).str(); } -std::string EVM::getDataOffsetSymbol(StringRef SymbolName) { - return (Twine("__dataoffset") + SymbolName).str(); +bool EVM::isDataSizeSymbolName(StringRef SymbolName) { + return SymbolName.find("__datasize") == 0; +} + +std::string EVM::extractDataSizeName(StringRef SymbolName) { + if (!SymbolName.consume_front("__datasize")) + report_fatal_error("Unexpected datasize symbol format"); + + return SymbolName.str(); +} + +std::string EVM::getDataOffsetSymbol(StringRef Name) { + return (Twine("__dataoffset") + Name).str(); +} + +bool EVM::isDataOffsetSymbolName(StringRef Name) { + return Name.find("__dataoffset") == 0; +} + +std::string EVM::extractDataOffseteName(StringRef SymbolName) { + if (!SymbolName.consume_front("__dataoffset")) + report_fatal_error("Unexpected dataoffset symbol format"); + + return SymbolName.str(); } std::string EVM::getLoadImmutableSymbol(StringRef Name, unsigned Idx) { diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h index d72cbd61a189..cbdb9bb29333 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCTargetDesc.h @@ -40,14 +40,19 @@ std::unique_ptr createEVMELFObjectWriter(uint8_t OSABI); namespace EVM { std::string getLinkerSymbolHash(StringRef SymName); std::string getSymbolIndexedName(StringRef Name, unsigned SubIdx); -std::string getSymbolSectionName(StringRef Name); std::string getNonIndexedSymbolName(StringRef Name); -std::string getLinkerSymbolName(StringRef Name); +bool isSymbolSectionName(StringRef Name); +std::string getSymbolSectionName(StringRef Name); bool isLinkerSymbolName(StringRef Name); -std::string getDataSizeSymbol(StringRef SymbolName); -std::string getDataOffsetSymbol(StringRef SymbolName); -std::string getLoadImmutableSymbol(StringRef Name, unsigned Idx); +std::string getLinkerSymbolName(StringRef Name); +bool isDataSizeSymbolName(StringRef SymbolName); +std::string getDataSizeSymbol(StringRef Name); +std::string extractDataSizeName(StringRef SymbolName); +bool isDataOffsetSymbolName(StringRef Name); +std::string getDataOffsetSymbol(StringRef Name); +std::string extractDataOffseteName(StringRef SymbolName); bool isLoadImmutableSymbolName(StringRef Name); +std::string getLoadImmutableSymbol(StringRef Name, unsigned Idx); std::string getImmutableId(StringRef Name); } // namespace EVM } // namespace llvm From 74d4b2861426b763bb70adb351b07c000478ec2c Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 6 Feb 2025 18:12:58 +0100 Subject: [PATCH 091/203] [EVM] Support evmla pushdeployaddress instruction This implements 'Call Protection For Libraries' mechanism in EVMLA mode. The runtime code of a library always starts with a push instruction, which is a zero of 20 bytes at compilation time. When the deploy code runs, this constant is replaced in memory by the current address and this modified code is stored in the contract. The front-end handles the deploy-related part. At runtime, this causes the deploy time address to be the first constant to be pushed onto the stack and the dispatcher code compares the current address against this constant for any non-view and non-pure function. TODO: #778. The correctness of this patch is based on the empirical observation that the function containing PUSHDEPLOYADDRESS always appears first in the module layout. Theoretically, this may not hold, so we need to position this function at the start. --- llvm/include/llvm/IR/IntrinsicsEVM.td | 8 ++++ llvm/lib/Target/EVM/EVMArgumentMove.cpp | 26 +++++++++++-- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 34 +++++++++++++++++ llvm/lib/Target/EVM/EVMInstrInfo.td | 7 +++- llvm/lib/Target/EVM/EVMMachineFunctionInfo.h | 7 ++++ .../lib/Target/EVM/EVMSingleUseExpression.cpp | 3 +- llvm/lib/Target/EVM/EVMStackModel.h | 2 +- llvm/lib/Target/EVM/EVMStackSolver.cpp | 24 ++++++++++-- .../MC/EVM/push-deploy-address-not-used.ll | 31 ++++++++++++++++ llvm/test/MC/EVM/push-deploy-address.ll | 37 +++++++++++++++++++ 10 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 llvm/test/MC/EVM/push-deploy-address-not-used.ll create mode 100644 llvm/test/MC/EVM/push-deploy-address.ll diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index fbf81c3bcbf1..633b014140e3 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -272,6 +272,14 @@ def int_evm_linkersymbol : DefaultAttrsIntrinsic< [IntrNoMem, IntrWillReturn] >; +// The intrinsic call should not be optimized out, even though +// its result is not used. For this, IntrInaccessibleMemOnly is +// used. +def int_evm_pushdeployaddress : DefaultAttrsIntrinsic< + [llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn, + IntrNoDuplicate] +>; + // Immutables support. // Loads immutable value. Should be used only in runtime code. diff --git a/llvm/lib/Target/EVM/EVMArgumentMove.cpp b/llvm/lib/Target/EVM/EVMArgumentMove.cpp index 206add7946ec..c60b497d6087 100644 --- a/llvm/lib/Target/EVM/EVMArgumentMove.cpp +++ b/llvm/lib/Target/EVM/EVMArgumentMove.cpp @@ -26,11 +26,15 @@ //===----------------------------------------------------------------------===// #include "EVM.h" +#include "EVMMachineFunctionInfo.h" #include "EVMSubtarget.h" #include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/Support/raw_ostream.h" + +#include + using namespace llvm; #define DEBUG_TYPE "evm-argument-move" @@ -68,10 +72,17 @@ bool EVMArgumentMove::runOnMachineFunction(MachineFunction &MF) { bool Changed = false; MachineBasicBlock &EntryMBB = MF.front(); - SmallVector Args; - for (MachineInstr &MI : EntryMBB) { - if (EVM::ARGUMENT == MI.getOpcode()) - Args.push_back(&MI); + std::deque Args; + MachineInstr *PushDeployAddress = nullptr; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (EVM::ARGUMENT == MI.getOpcode()) + Args.push_back(&MI); + else if (EVM::PUSHDEPLOYADDRESS == MI.getOpcode()) { + assert(!PushDeployAddress && "Multiple PUSHDEPLOYADDRESS instructions"); + PushDeployAddress = &MI; + } + } } // Sort ARGUMENT instructions in ascending order of their arguments. @@ -82,6 +93,13 @@ bool EVMArgumentMove::runOnMachineFunction(MachineFunction &MF) { return Arg1Idx < Arg2Idx; }); + // Should be the first instruction at the MF's entry. + if (PushDeployAddress) { + auto *MFI = MF.getInfo(); + MFI->setHasPushDeployAddress(); + Args.push_front(PushDeployAddress); + } + for (MachineInstr *MI : reverse(Args)) { MachineInstr *Arg = MI->removeFromParent(); EntryMBB.insert(EntryMBB.begin(), Arg); diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index 9328c5802bef..d6fcb596a519 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "EVMMCInstLower.h" +#include "EVMMachineFunctionInfo.h" #include "EVMTargetMachine.h" #include "MCTargetDesc/EVMMCTargetDesc.h" #include "MCTargetDesc/EVMTargetStreamer.h" @@ -48,6 +49,11 @@ class EVMAsmPrinter : public AsmPrinter { StringSet<> WideRelocSymbolsSet; StringMap ImmutablesMap; + // True if there is a function that pushes deploy address. + bool ModuleHasPushDeployAddress = false; + + bool FirstFunctIsHandled = false; + public: EVMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : AsmPrinter(TM, std::move(Streamer)) {} @@ -62,6 +68,9 @@ class EVMAsmPrinter : public AsmPrinter { void emitEndOfAsmFile(Module &) override; + void emitFunctionBodyStart() override; + void emitFunctionBodyEnd() override; + private: void emitAssemblySymbol(const MachineInstr *MI); void emitWideRelocatableSymbol(const MachineInstr *MI); @@ -92,6 +101,29 @@ void EVMAsmPrinter::emitFunctionEntryLabel() { } } +void EVMAsmPrinter::emitFunctionBodyStart() { + if (const auto *MFI = MF->getInfo(); + MFI->getHasPushDeployAddress()) { + // TODO: #778. Move the function with PUSHDEPLOYADDRESS to the + // beginning of the module layout. + if (FirstFunctIsHandled) + report_fatal_error("Function with PUSHDEPLOYADDRESS isn't first."); + + assert(!ModuleHasPushDeployAddress && + "Multiple functions with PUSHDEPLOYADDRESS."); + + // Deploy address is represented by a PUSH20 instruction at the + // start of the bytecode. + MCInst MCI; + MCI.setOpcode(EVM::PUSH20_S); + MCI.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, MCI); + ModuleHasPushDeployAddress = true; + } +} + +void EVMAsmPrinter::emitFunctionBodyEnd() { FirstFunctIsHandled = true; } + void EVMAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { AsmPrinter::emitBasicBlockStart(MBB); @@ -285,6 +317,8 @@ void EVMAsmPrinter::emitWideRelocatableSymbol(const MachineInstr *MI) { void EVMAsmPrinter::emitEndOfAsmFile(Module &) { WideRelocSymbolsSet.clear(); ImmutablesMap.clear(); + ModuleHasPushDeployAddress = false; + FirstFunctIsHandled = false; } void EVMAsmPrinter::emitJumpDest() { diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 347b49449c97..ecab375fe435 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -200,7 +200,7 @@ def ADJCALLSTACKUP [(EVMcallseq_end timm:$amt1, timm:$amt2)]>; } -let hasSideEffects = 1, Defs = [], +let hasSideEffects = 1, isNotDuplicable = 1, Defs = [], Uses = [ARGUMENTS] in def ARGUMENT : NRI<(outs GPR:$res), (ins i256imm:$argno), [(set GPR:$res, (EVMargument timm:$argno))], @@ -1146,3 +1146,8 @@ let isAsCheapAsAMove = 1, isReMaterializable = 1, isCodeGenOnly = 1, hasSideEffe def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>; } } + +let isCodeGenOnly = 1, hasSideEffects = 1, isNotDuplicable = 1 in +def PUSHDEPLOYADDRESS + : NRI<(outs GPR:$res), (ins), [(set GPR:$res, (int_evm_pushdeployaddress))], + "PUSHDEPLOYADDRESS $res">; diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h index 0d49c32cebe8..260f3a08ab4b 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h @@ -57,6 +57,9 @@ class EVMMachineFunctionInfo final : public MachineFunctionInfo { /// If the MF's instructions are in 'stack' form. bool IsStackified = false; + /// True if MF has PushDeployAddress instruction. + bool HasPushDeployAddress = false; + public: explicit EVMMachineFunctionInfo(MachineFunction &MF) {} EVMMachineFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {} @@ -101,6 +104,10 @@ class EVMMachineFunctionInfo final : public MachineFunctionInfo { void setIsStackified(bool Val = true) { IsStackified = Val; } bool getIsStackified() const { return IsStackified; } + + void setHasPushDeployAddress(bool Val = true) { HasPushDeployAddress = Val; } + + bool getHasPushDeployAddress() const { return HasPushDeployAddress; } }; } // end namespace llvm diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index c2528e069055..99cd82bd643a 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -310,7 +310,8 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, return true; // Don't move ARGUMENT instructions, as stackification pass relies on this. - if (DefI->getOpcode() == EVM::ARGUMENT) + if (DefI->getOpcode() == EVM::ARGUMENT || + DefI->getOpcode() == EVM::PUSHDEPLOYADDRESS) return false; // Check for register dependencies. diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h index ba6cad8860a7..055785e50d98 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.h +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -321,7 +321,7 @@ class EVMStackModel { LIS.getInterval(MI.getOperand(0).getReg()).containsOneValue()) return true; return Opc == EVM::ARGUMENT || Opc == EVM::RET || Opc == EVM::JUMP || - Opc == EVM::JUMPI; + Opc == EVM::JUMPI || Opc == EVM::PUSHDEPLOYADDRESS; } auto instructionsToProcess(const MachineBasicBlock *MBB) const { return make_filter_range( diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index a34b7a4aa706..b477ed624991 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -10,6 +10,7 @@ #include "EVM.h" #include "EVMInstrInfo.h" +#include "EVMMachineFunctionInfo.h" #include "EVMRegisterInfo.h" #include "EVMStackShuffler.h" #include "llvm/ADT/DepthFirstIterator.h" @@ -426,15 +427,32 @@ void EVMStackSolver::runPropagation() { } } + Stack EntryStack; + bool IsNoReturn = MF.getFunction().hasFnAttribute(Attribute::NoReturn); + Stack FunctionParameters = StackModel.getFunctionParameters(); + + // We assume the function containing PUSHDEPLOYADDRESS instruction has the + // following properties: + // - It is unique (verified in AsmPrinter) + // - It appears first in the module layout. TODO: #778 + // - It does not return and has no arguments + if (const auto *MFI = MF.getInfo(); + MFI->getHasPushDeployAddress()) { + MachineBasicBlock::const_iterator I = MF.front().getFirstNonDebugInstr(); + assert(I != MF.front().end() && I->getOpcode() == EVM::PUSHDEPLOYADDRESS && + "MF has no PUSHDEPLOYADDRESS"); + assert(IsNoReturn && FunctionParameters.empty() && + "PUSHDEPLOYADDRESS in a non - void/nullary function"); + EntryStack.push_back(StackModel.getRegisterSlot(I->getOperand(0).getReg())); + } // The entry MBB's stack contains the function parameters, which cannot be // inferred; put them to the stack. - Stack EntryStack; - if (!MF.getFunction().hasFnAttribute(Attribute::NoReturn)) + if (!IsNoReturn) EntryStack.push_back(StackModel.getCalleeReturnSlot(&MF)); // Calling convention: input arguments are passed in stack such that the // first one specified in the function declaration is passed on the stack TOP. - append_range(EntryStack, reverse(StackModel.getFunctionParameters())); + append_range(EntryStack, reverse(FunctionParameters)); insertMBBEntryStack(&MF.front(), EntryStack); } diff --git a/llvm/test/MC/EVM/push-deploy-address-not-used.ll b/llvm/test/MC/EVM/push-deploy-address-not-used.ll new file mode 100644 index 000000000000..44d2154e0e68 --- /dev/null +++ b/llvm/test/MC/EVM/push-deploy-address-not-used.ll @@ -0,0 +1,31 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --no-leading-addr --disassemble - | FileCheck %s + +; CHECK-LABEL: : +; CHECK-NEXT: 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 PUSH20 0 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.pushdeployaddress() +declare i256 @llvm.evm.address() +declare void @llvm.evm.revert(ptr addrspace(1), i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @test() noreturn { +entry: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %deploy_addr = tail call i256 @llvm.evm.pushdeployaddress() + %curr_addr = tail call i256 @llvm.evm.address() + %cmp = icmp eq i256 %curr_addr, 0 + br i1 %cmp, label %exit, label %error + +error: + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +exit: + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} + +!1 = !{!"imm_id"} diff --git a/llvm/test/MC/EVM/push-deploy-address.ll b/llvm/test/MC/EVM/push-deploy-address.ll new file mode 100644 index 000000000000..5694faf6a68c --- /dev/null +++ b/llvm/test/MC/EVM/push-deploy-address.ll @@ -0,0 +1,37 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --no-leading-addr --disassemble - | FileCheck %s + +; CHECK-LABEL: : +; CHECK-NEXT: 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 PUSH20 0 + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.pushdeployaddress() +declare i256 @llvm.evm.address() +declare void @llvm.evm.revert(ptr addrspace(1), i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @test() noreturn { +entry: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %curr_addr = tail call i256 @llvm.evm.address() + %calldatasize = tail call i256 @llvm.evm.calldatasize() + %cmp_calldata = icmp ult i256 %calldatasize, 4 + br i1 %cmp_calldata, label %error, label %check_deploy + +check_deploy: + %deploy_addr = tail call i256 @llvm.evm.pushdeployaddress() + %cmp = icmp eq i256 %deploy_addr, %curr_addr + br i1 %cmp, label %exit, label %error + +error: + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +exit: + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} + +!1 = !{!"imm_id"} From 5af55263a6a66c1b05671adf20162dcbaf0129be Mon Sep 17 00:00:00 2001 From: Kevin McAfee Date: Fri, 16 Aug 2024 11:48:57 -0700 Subject: [PATCH 092/203] [SDAG] Read-only intrinsics must have WillReturn and !Throws attributes to be treated as loads (#99999) This change avoids deleting `!willReturn` intrinsics for which the return value is unused when building the SDAG. Currently, calls to read-only intrinsics not marked with `IntrWillReturn` cannot be deleted at the LLVM IR level but may be deleted when building the SDAG. These calls are unsafe to remove from the IR because the functions are `!willReturn` and should also be unsafe to remove fromthe SDAG for the same reason. This change aligns the behavior of the SDAG to that of LLVM IR. This change also requires that intrinsics not have the `Throws` attribute to be treated as loads for the same reason. --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index a58af7e2b73c..199c864512e3 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5230,7 +5230,8 @@ void SelectionDAGBuilder::visitTargetIntrinsic(const CallInst &I, // definition. const Function *F = I.getCalledFunction(); bool HasChain = !F->doesNotAccessMemory(); - bool OnlyLoad = HasChain && F->onlyReadsMemory(); + bool OnlyLoad = + HasChain && F->onlyReadsMemory() && F->willReturn() && F->doesNotThrow(); // Build the operand list. SmallVector Ops; From 7d513dc56c795e19fb424c45ebde87a697042c49 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 6 Nov 2024 15:19:01 +0100 Subject: [PATCH 093/203] [EVM] Add pre-commit test for Update intrinsic attributes Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/cse-intrinsic.ll | 607 +++++++++++++++++++++++++ 1 file changed, 607 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/cse-intrinsic.ll diff --git a/llvm/test/CodeGen/EVM/cse-intrinsic.ll b/llvm/test/CodeGen/EVM/cse-intrinsic.ll new file mode 100644 index 000000000000..cdab966ee4a1 --- /dev/null +++ b/llvm/test/CodeGen/EVM/cse-intrinsic.ll @@ -0,0 +1,607 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @addmod_dce(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: define noundef i256 @addmod_dce +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.addmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + ret i256 0 +} + +define i256 @sha3_dce(ptr addrspace(1) %offset, i256 %size) nounwind { +; CHECK-LABEL: define noundef i256 @sha3_dce +; CHECK-SAME: (ptr addrspace(1) readonly [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[OFFSET]], i256 [[SIZE]]) +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + ret i256 0 +} + +define i256 @pc_dce() nounwind { +; CHECK-LABEL: define noundef i256 @pc_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.pc() +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.pc() + ret i256 0 +} + +define i256 @gas_dce() nounwind { +; CHECK-LABEL: define noundef i256 @gas_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.gas() + ret i256 0 +} + +define i256 @msize_dce() nounwind { +; CHECK-LABEL: define noundef i256 @msize_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.msize() +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.msize() + ret i256 0 +} + +define i256 @balance_dce(i256 %rs1) nounwind { +; CHECK-LABEL: define noundef i256 @balance_dce +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.balance(i256 %rs1) + ret i256 0 +} + +define i256 @selfbalance_dce() nounwind { +; CHECK-LABEL: define noundef i256 @selfbalance_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.selfbalance() + ret i256 0 +} + +define i256 @returndatasize_dce() nounwind { +; CHECK-LABEL: define noundef i256 @returndatasize_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.returndatasize() +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.returndatasize() + ret i256 0 +} + +define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: define i256 @addmod +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.addmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + %res2 = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { +; CHECK-LABEL: define i256 @mulmod +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.mulmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) + %res2 = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @exp(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: define i256 @exp +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.exp(i256 [[RS1]], i256 [[RS2]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) + %res2 = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { +; CHECK-LABEL: define i256 @sha3 +; CHECK-SAME: (ptr addrspace(1) [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[OFFSET]], i256 [[SIZE]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + %res2 = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @signextend(i256 %bytesize, i256 %val) nounwind { +; CHECK-LABEL: define i256 @signextend +; CHECK-SAME: (i256 [[BYTESIZE:%.*]], i256 [[VAL:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.signextend(i256 [[BYTESIZE]], i256 [[VAL]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + %res2 = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @byte(i256 %rs1, i256 %rs2) nounwind { +; CHECK-LABEL: define i256 @byte +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.byte(i256 [[RS1]], i256 [[RS2]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) + %res2 = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @pc() nounwind { +; CHECK-LABEL: define i256 @pc +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.pc() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.pc() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.pc() + %res2 = call i256 @llvm.evm.pc() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @gas() nounwind { +; CHECK-LABEL: define i256 @gas +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.gas() + %res2 = call i256 @llvm.evm.gas() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @msize() nounwind { +; CHECK-LABEL: define i256 @msize +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.msize() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.msize() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.msize() + %res2 = call i256 @llvm.evm.msize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @address() nounwind { +; CHECK-LABEL: define i256 @address +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.address() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.address() + %res2 = call i256 @llvm.evm.address() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @origin() nounwind { +; CHECK-LABEL: define i256 @origin +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.origin() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.origin() + %res2 = call i256 @llvm.evm.origin() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @caller() nounwind { +; CHECK-LABEL: define i256 @caller +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.caller() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.caller() + %res2 = call i256 @llvm.evm.caller() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @balance(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @balance +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.balance(i256 %rs1) + %res2 = call i256 @llvm.evm.balance(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @calldatasize() nounwind { +; CHECK-LABEL: define i256 @calldatasize +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldatasize() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.calldatasize() + %res2 = call i256 @llvm.evm.calldatasize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { +; CHECK-LABEL: define i256 @calldataload +; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) + %res2 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @callvalue() nounwind { +; CHECK-LABEL: define i256 @callvalue +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.callvalue() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.callvalue() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.callvalue() + %res2 = call i256 @llvm.evm.callvalue() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @codesize() nounwind { +; CHECK-LABEL: define i256 @codesize +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.codesize() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.codesize() + %res2 = call i256 @llvm.evm.codesize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @gasprice() nounwind { +; CHECK-LABEL: define i256 @gasprice +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gasprice() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.gasprice() + %res2 = call i256 @llvm.evm.gasprice() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @extcodesize(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @extcodesize +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.extcodesize(i256 %rs1) + %res2 = call i256 @llvm.evm.extcodesize(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @extcodehash(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @extcodehash +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.extcodehash(i256 %rs1) + %res2 = call i256 @llvm.evm.extcodehash(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @returndatasize() nounwind { +; CHECK-LABEL: define i256 @returndatasize +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.returndatasize() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.returndatasize() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.returndatasize() + %res2 = call i256 @llvm.evm.returndatasize() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @blockhash(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @blockhash +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.blockhash(i256 %rs1) + %res2 = call i256 @llvm.evm.blockhash(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @blobhash(i256 %rs1) nounwind { +; CHECK-LABEL: define i256 @blobhash +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[RS1]]) +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.blobhash(i256 %rs1) + %res2 = call i256 @llvm.evm.blobhash(i256 %rs1) + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @coinbase() nounwind { +; CHECK-LABEL: define i256 @coinbase +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.coinbase() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.coinbase() + %res2 = call i256 @llvm.evm.coinbase() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @timestamp() nounwind { +; CHECK-LABEL: define i256 @timestamp +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.timestamp() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.timestamp() + %res2 = call i256 @llvm.evm.timestamp() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @number() nounwind { +; CHECK-LABEL: define i256 @number +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.number() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.number() + %res2 = call i256 @llvm.evm.number() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @difficulty() nounwind { +; CHECK-LABEL: define i256 @difficulty +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.difficulty() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.difficulty() + %res2 = call i256 @llvm.evm.difficulty() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @gaslimit() nounwind { +; CHECK-LABEL: define i256 @gaslimit +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gaslimit() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.gaslimit() + %res2 = call i256 @llvm.evm.gaslimit() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @chainid() nounwind { +; CHECK-LABEL: define i256 @chainid +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.chainid() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.chainid() + %res2 = call i256 @llvm.evm.chainid() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @selfbalance() nounwind { +; CHECK-LABEL: define i256 @selfbalance +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.selfbalance() + %res2 = call i256 @llvm.evm.selfbalance() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @basefee() nounwind { +; CHECK-LABEL: define i256 @basefee +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.basefee() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.basefee() + %res2 = call i256 @llvm.evm.basefee() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define i256 @blobbasefee() nounwind { +; CHECK-LABEL: define i256 @blobbasefee +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobbasefee() +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 +; CHECK-NEXT: ret i256 [[RET]] +; + %res1 = call i256 @llvm.evm.blobbasefee() + %res2 = call i256 @llvm.evm.blobbasefee() + %ret = add i256 %res1, %res2 + ret i256 %ret +} + +define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { +; CHECK-LABEL: define void @log0 +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + ret void +} + +define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { +; CHECK-LABEL: define void @log1 +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) +; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) + call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) + ret void +} + +define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { +; CHECK-LABEL: define void @log2 +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) +; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) + call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) + ret void +} + +define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { +; CHECK-LABEL: define void @log3 +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) +; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) + call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) + ret void +} + +define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { +; CHECK-LABEL: define void @log4 +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) +; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) +; CHECK-NEXT: ret void +; + call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) + call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) + ret void +} + +declare i256 @llvm.evm.addmod(i256, i256, i256) +declare i256 @llvm.evm.mulmod(i256, i256, i256) +declare i256 @llvm.evm.exp(i256, i256) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) +declare i256 @llvm.evm.signextend(i256, i256) +declare i256 @llvm.evm.byte(i256, i256) +declare i256 @llvm.evm.pc() +declare i256 @llvm.evm.gas() +declare i256 @llvm.evm.msize() +declare i256 @llvm.evm.address() +declare i256 @llvm.evm.origin() +declare i256 @llvm.evm.caller() +declare i256 @llvm.evm.balance(i256) +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.calldataload(ptr addrspace(2)) +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.codesize() +declare i256 @llvm.evm.gasprice() +declare i256 @llvm.evm.extcodesize(i256) +declare i256 @llvm.evm.extcodehash(i256) +declare i256 @llvm.evm.blockhash(i256) +declare i256 @llvm.evm.blobhash(i256) +declare i256 @llvm.evm.returndatasize() +declare i256 @llvm.evm.coinbase() +declare i256 @llvm.evm.timestamp() +declare i256 @llvm.evm.number() +declare i256 @llvm.evm.difficulty() +declare i256 @llvm.evm.gaslimit() +declare i256 @llvm.evm.chainid() +declare i256 @llvm.evm.selfbalance() +declare i256 @llvm.evm.basefee() +declare i256 @llvm.evm.blobbasefee() +declare void @llvm.evm.log0(ptr addrspace(1), i256) +declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) +declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) +declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) +declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) From c8369e57b5a831d27bde322fddb4fde8860c5284 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 31 Oct 2024 16:25:11 +0100 Subject: [PATCH 094/203] [EVM] Update intrinsic attributes Signed-off-by: Vladimir Radosavljevic --- llvm/include/llvm/IR/IntrinsicsEVM.td | 90 +++++++++++------------ llvm/lib/Transforms/Utils/Local.cpp | 14 ++++ llvm/test/CodeGen/EVM/cse-intrinsic.ll | 99 ++++++++++++-------------- 3 files changed, 104 insertions(+), 99 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index 633b014140e3..3dccff349131 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -25,47 +25,47 @@ let TargetPrefix = "evm" in { // Arithmetical operations. def int_evm_div - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_sdiv - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_mod - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_smod - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_shl - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_shr - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_sar - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_addmod : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], - [IntrNoMem]>; + [IntrNoMem, IntrWillReturn]>; def int_evm_mulmod : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], - [IntrNoMem]>; + [IntrNoMem, IntrWillReturn]>; def int_evm_exp - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_sha3 : Intrinsic<[llvm_i256_ty], - [LLVMQualPointerType, llvm_i256_ty], [IntrReadMem]>; + [LLVMQualPointerType, llvm_i256_ty], [IntrReadMem, IntrWillReturn]>; def int_evm_signextend - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_byte : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], - [IntrNoMem]>; + [IntrNoMem, IntrWillReturn]>; // Memory operations. def int_evm_mstore8 @@ -73,86 +73,86 @@ def int_evm_mstore8 // TODO: CPR-1555. // Review system intrinsics attributes taking into account EVM. -def int_evm_msize : Intrinsic<[llvm_i256_ty], [], []>; +def int_evm_msize : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_pc : Intrinsic<[llvm_i256_ty], [], []>; +def int_evm_pc : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_gas : Intrinsic<[llvm_i256_ty], [], []>; +def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; // Getting values from the context. -def int_evm_address : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_address : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_origin : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_origin : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_caller : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_caller : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], []>; +def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_callvalue : Intrinsic<[llvm_i256_ty], [], []>; +def int_evm_callvalue : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_calldatasize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_calldatasize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_calldataload : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], []>; -def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; // TODO: Ensure the gasprice is fixed while a function execution. -def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_extcodesize - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; def int_evm_extcodecopy : Intrinsic<[], [llvm_i256_ty, LLVMQualPointerType, LLVMQualPointerType, llvm_i256_ty], [IntrWriteMem]>; -def int_evm_returndatasize : Intrinsic<[llvm_i256_ty], [], []>; +def int_evm_returndatasize : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; def int_evm_extcodehash - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; -def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; +def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; -def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem]>; +def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; -def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_number : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_number : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], []>; +def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly]>; -def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem]>; +def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; // Logging. def int_evm_log0 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], []>; + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], [IntrReadMem]>; def int_evm_log1 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], []>; + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], [IntrReadMem]>; def int_evm_log2 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty], []>; + llvm_i256_ty], [IntrReadMem]>; def int_evm_log3 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty], []>; + llvm_i256_ty, llvm_i256_ty], [IntrReadMem]>; def int_evm_log4 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], []>; + llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], [IntrReadMem]>; // System calls def int_evm_create @@ -252,14 +252,14 @@ def int_evm_memcpyas1as4 // passed in the metadata. def int_evm_datasize : DefaultAttrsIntrinsic< [llvm_i256_ty], [llvm_metadata_ty], - [IntrNoMem, IntrSpeculatable] + [IntrNoMem, IntrWillReturn, 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] + [IntrNoMem, IntrWillReturn, IntrSpeculatable] >; diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index f68cbf62b982..3d447c713410 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -56,6 +56,9 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +// EVM local end #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" @@ -436,6 +439,17 @@ bool llvm::wouldInstructionBeTriviallyDead(const Instruction *I, return true; } + // EVM local begin + if (auto *II = dyn_cast(I)) + if (II->getIntrinsicID() == Intrinsic::evm_msize || + II->getIntrinsicID() == Intrinsic::evm_pc || + II->getIntrinsicID() == Intrinsic::evm_gas || + II->getIntrinsicID() == Intrinsic::evm_balance || + II->getIntrinsicID() == Intrinsic::evm_returndatasize || + II->getIntrinsicID() == Intrinsic::evm_selfbalance) + return true; + // EVM local end + if (auto *CB = dyn_cast(I)) if (isRemovableAlloc(CB, TLI)) return true; diff --git a/llvm/test/CodeGen/EVM/cse-intrinsic.ll b/llvm/test/CodeGen/EVM/cse-intrinsic.ll index cdab966ee4a1..b833d99b5ba8 100644 --- a/llvm/test/CodeGen/EVM/cse-intrinsic.ll +++ b/llvm/test/CodeGen/EVM/cse-intrinsic.ll @@ -7,7 +7,6 @@ target triple = "evm" define i256 @addmod_dce(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { ; CHECK-LABEL: define noundef i256 @addmod_dce ; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.addmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) @@ -16,8 +15,7 @@ define i256 @addmod_dce(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { define i256 @sha3_dce(ptr addrspace(1) %offset, i256 %size) nounwind { ; CHECK-LABEL: define noundef i256 @sha3_dce -; CHECK-SAME: (ptr addrspace(1) readonly [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[OFFSET]], i256 [[SIZE]]) +; CHECK-SAME: (ptr addrspace(1) nocapture readnone [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) @@ -26,8 +24,7 @@ define i256 @sha3_dce(ptr addrspace(1) %offset, i256 %size) nounwind { define i256 @pc_dce() nounwind { ; CHECK-LABEL: define noundef i256 @pc_dce -; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.pc() +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.pc() @@ -36,8 +33,7 @@ define i256 @pc_dce() nounwind { define i256 @gas_dce() nounwind { ; CHECK-LABEL: define noundef i256 @gas_dce -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.gas() @@ -46,8 +42,7 @@ define i256 @gas_dce() nounwind { define i256 @msize_dce() nounwind { ; CHECK-LABEL: define noundef i256 @msize_dce -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.msize() +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.msize() @@ -56,8 +51,7 @@ define i256 @msize_dce() nounwind { define i256 @balance_dce(i256 %rs1) nounwind { ; CHECK-LABEL: define noundef i256 @balance_dce -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.balance(i256 %rs1) @@ -66,8 +60,7 @@ define i256 @balance_dce(i256 %rs1) nounwind { define i256 @selfbalance_dce() nounwind { ; CHECK-LABEL: define noundef i256 @selfbalance_dce -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.selfbalance() @@ -76,8 +69,7 @@ define i256 @selfbalance_dce() nounwind { define i256 @returndatasize_dce() nounwind { ; CHECK-LABEL: define noundef i256 @returndatasize_dce -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { -; CHECK-NEXT: [[RES:%.*]] = tail call i256 @llvm.evm.returndatasize() +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.returndatasize() @@ -86,7 +78,7 @@ define i256 @returndatasize_dce() nounwind { define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { ; CHECK-LABEL: define i256 @addmod -; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.addmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -99,7 +91,7 @@ define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { ; CHECK-LABEL: define i256 @mulmod -; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.mulmod(i256 [[RS1]], i256 [[RS2]], i256 [[RS3]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -112,7 +104,7 @@ define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { define i256 @exp(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: define i256 @exp -; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.exp(i256 [[RS1]], i256 [[RS2]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -125,7 +117,7 @@ define i256 @exp(i256 %rs1, i256 %rs2) nounwind { define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { ; CHECK-LABEL: define i256 @sha3 -; CHECK-SAME: (ptr addrspace(1) [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: (ptr addrspace(1) [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[OFFSET]], i256 [[SIZE]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -138,7 +130,7 @@ define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { define i256 @signextend(i256 %bytesize, i256 %val) nounwind { ; CHECK-LABEL: define i256 @signextend -; CHECK-SAME: (i256 [[BYTESIZE:%.*]], i256 [[VAL:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[BYTESIZE:%.*]], i256 [[VAL:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.signextend(i256 [[BYTESIZE]], i256 [[VAL]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -151,7 +143,7 @@ define i256 @signextend(i256 %bytesize, i256 %val) nounwind { define i256 @byte(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: define i256 @byte -; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.byte(i256 [[RS1]], i256 [[RS2]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -164,7 +156,7 @@ define i256 @byte(i256 %rs1, i256 %rs2) nounwind { define i256 @pc() nounwind { ; CHECK-LABEL: define i256 @pc -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.pc() ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.pc() ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -178,7 +170,7 @@ define i256 @pc() nounwind { define i256 @gas() nounwind { ; CHECK-LABEL: define i256 @gas -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gas() ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.gas() ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -192,7 +184,7 @@ define i256 @gas() nounwind { define i256 @msize() nounwind { ; CHECK-LABEL: define i256 @msize -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.msize() ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.msize() ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -206,7 +198,7 @@ define i256 @msize() nounwind { define i256 @address() nounwind { ; CHECK-LABEL: define i256 @address -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.address() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -219,7 +211,7 @@ define i256 @address() nounwind { define i256 @origin() nounwind { ; CHECK-LABEL: define i256 @origin -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.origin() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -232,7 +224,7 @@ define i256 @origin() nounwind { define i256 @caller() nounwind { ; CHECK-LABEL: define i256 @caller -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.caller() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -245,7 +237,7 @@ define i256 @caller() nounwind { define i256 @balance(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @balance -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -259,7 +251,7 @@ define i256 @balance(i256 %rs1) nounwind { define i256 @calldatasize() nounwind { ; CHECK-LABEL: define i256 @calldatasize -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldatasize() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -272,7 +264,7 @@ define i256 @calldatasize() nounwind { define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { ; CHECK-LABEL: define i256 @calldataload -; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -286,10 +278,9 @@ define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { define i256 @callvalue() nounwind { ; CHECK-LABEL: define i256 @callvalue -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.callvalue() -; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.callvalue() -; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] ; %res1 = call i256 @llvm.evm.callvalue() @@ -300,7 +291,7 @@ define i256 @callvalue() nounwind { define i256 @codesize() nounwind { ; CHECK-LABEL: define i256 @codesize -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.codesize() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -313,7 +304,7 @@ define i256 @codesize() nounwind { define i256 @gasprice() nounwind { ; CHECK-LABEL: define i256 @gasprice -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gasprice() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -326,7 +317,7 @@ define i256 @gasprice() nounwind { define i256 @extcodesize(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @extcodesize -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -339,7 +330,7 @@ define i256 @extcodesize(i256 %rs1) nounwind { define i256 @extcodehash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @extcodehash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -352,7 +343,7 @@ define i256 @extcodehash(i256 %rs1) nounwind { define i256 @returndatasize() nounwind { ; CHECK-LABEL: define i256 @returndatasize -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.returndatasize() ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.returndatasize() ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -366,7 +357,7 @@ define i256 @returndatasize() nounwind { define i256 @blockhash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @blockhash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -379,7 +370,7 @@ define i256 @blockhash(i256 %rs1) nounwind { define i256 @blobhash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @blobhash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -392,7 +383,7 @@ define i256 @blobhash(i256 %rs1) nounwind { define i256 @coinbase() nounwind { ; CHECK-LABEL: define i256 @coinbase -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.coinbase() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -405,7 +396,7 @@ define i256 @coinbase() nounwind { define i256 @timestamp() nounwind { ; CHECK-LABEL: define i256 @timestamp -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.timestamp() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -418,7 +409,7 @@ define i256 @timestamp() nounwind { define i256 @number() nounwind { ; CHECK-LABEL: define i256 @number -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.number() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -431,7 +422,7 @@ define i256 @number() nounwind { define i256 @difficulty() nounwind { ; CHECK-LABEL: define i256 @difficulty -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.difficulty() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -444,7 +435,7 @@ define i256 @difficulty() nounwind { define i256 @gaslimit() nounwind { ; CHECK-LABEL: define i256 @gaslimit -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gaslimit() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -457,7 +448,7 @@ define i256 @gaslimit() nounwind { define i256 @chainid() nounwind { ; CHECK-LABEL: define i256 @chainid -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.chainid() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -470,7 +461,7 @@ define i256 @chainid() nounwind { define i256 @selfbalance() nounwind { ; CHECK-LABEL: define i256 @selfbalance -; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.selfbalance() ; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.selfbalance() ; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] @@ -484,7 +475,7 @@ define i256 @selfbalance() nounwind { define i256 @basefee() nounwind { ; CHECK-LABEL: define i256 @basefee -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.basefee() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -497,7 +488,7 @@ define i256 @basefee() nounwind { define i256 @blobbasefee() nounwind { ; CHECK-LABEL: define i256 @blobbasefee -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobbasefee() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -510,7 +501,7 @@ define i256 @blobbasefee() nounwind { define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { ; CHECK-LABEL: define void @log0 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: ret void @@ -522,7 +513,7 @@ define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { ; CHECK-LABEL: define void @log1 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) ; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) ; CHECK-NEXT: ret void @@ -534,7 +525,7 @@ define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { ; CHECK-LABEL: define void @log2 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) ; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) ; CHECK-NEXT: ret void @@ -546,7 +537,7 @@ define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwin define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { ; CHECK-LABEL: define void @log3 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) ; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) ; CHECK-NEXT: ret void @@ -558,7 +549,7 @@ define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { ; CHECK-LABEL: define void @log4 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) ; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) ; CHECK-NEXT: ret void From 6d80fb5436a6a9ab0eeba5107f60e0acde718fde Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 31 Oct 2024 14:01:50 +0100 Subject: [PATCH 095/203] Introduce VMAAResult Signed-off-by: Vladimir Radosavljevic --- llvm/include/llvm/Analysis/VMAliasAnalysis.h | 50 ++++++ llvm/lib/Analysis/CMakeLists.txt | 3 + llvm/lib/Analysis/VMAliasAnalysis.cpp | 173 +++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 llvm/include/llvm/Analysis/VMAliasAnalysis.h create mode 100644 llvm/lib/Analysis/VMAliasAnalysis.cpp diff --git a/llvm/include/llvm/Analysis/VMAliasAnalysis.h b/llvm/include/llvm/Analysis/VMAliasAnalysis.h new file mode 100644 index 000000000000..bedf209a1719 --- /dev/null +++ b/llvm/include/llvm/Analysis/VMAliasAnalysis.h @@ -0,0 +1,50 @@ +//===-- VMAliasAnalysis.h - VM alias analysis -------------------*- 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 is the address space based alias analysis pass, that is shared between +// EVM and EVM backends. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_VMALIASANALYSIS_H +#define LLVM_ANALYSIS_VMALIASANALYSIS_H + +#include "llvm/Analysis/AliasAnalysis.h" + +namespace llvm { + +class DataLayout; +class MemoryLocation; + +/// A simple AA result that uses address space info to answer queries. +class VMAAResult : public AAResultBase { + const DataLayout &DL; + SmallDenseSet StorageAS; + SmallDenseSet HeapAS; + unsigned MaxAS; + +public: + explicit VMAAResult(const DataLayout &DL, + const SmallDenseSet &StorageAS, + const SmallDenseSet &HeapAS, unsigned MaxAS) + : DL(DL), StorageAS(StorageAS), HeapAS(HeapAS), MaxAS(MaxAS) {} + + /// Handle invalidation events from the new pass manager. + /// + /// By definition, this result is stateless and so remains valid. + bool invalidate(Function &, const PreservedAnalyses &, + FunctionAnalysisManager::Invalidator &) { + return false; + } + + AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, + AAQueryInfo &AAQI, const Instruction *I); +}; +} // end namespace llvm + +#endif // LLVM_ANALYSIS_VMALIASANALYSIS_H diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index 74476cb5440c..bccdaef24be5 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -139,6 +139,9 @@ add_llvm_component_library(LLVMAnalysis ValueLatticeUtils.cpp ValueTracking.cpp VectorUtils.cpp + # EVM local begin + VMAliasAnalysis.cpp + # EVM local end ${GeneratedMLSources} ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/Analysis/VMAliasAnalysis.cpp b/llvm/lib/Analysis/VMAliasAnalysis.cpp new file mode 100644 index 000000000000..510e64c72f66 --- /dev/null +++ b/llvm/lib/Analysis/VMAliasAnalysis.cpp @@ -0,0 +1,173 @@ +//===-- VMAliasAnalysis.cpp - VM alias analysis -----------------*- 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 is the address space based alias analysis pass, that is shared between +// EVM and EVM backends. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/VMAliasAnalysis.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Instructions.h" +#include + +using namespace llvm; + +// Get the base pointer and the offset by looking through the +// ptrtoint+arithmetic+inttoptr sequence. +static std::pair +getBaseWithOffset(const Value *V, unsigned BitWidth, unsigned MaxLookup = 6) { + auto Offset = APInt::getZero(BitWidth); + + // Bail out if this is not an inttoptr instruction. + if (!isa(V)) + return {nullptr, Offset}; + + V = cast(V)->getOperand(0); + + for (unsigned I = 0; I < MaxLookup; ++I) { + // If this is a ptrtoint, get the operand and stop the lookup. + if (const auto *PtrToInt = dyn_cast(V)) { + V = PtrToInt->getOperand(0); + break; + } + + // We only handle binary operations. + const auto *BOp = dyn_cast(V); + if (!BOp) + break; + + // With constant operand. + const auto *CI = dyn_cast(BOp->getOperand(1)); + if (!CI) + break; + + auto Val = CI->getValue(); + + // If the value is larger than the current bitwidth, extend the offset + // and remember the new bitwidth. + if (Val.getBitWidth() > BitWidth) { + BitWidth = Val.getBitWidth(); + Offset = Offset.sext(BitWidth); + } else { + // Otherwise, extend the value to the current bitwidth. + Val = Val.sext(BitWidth); + } + + // TODO: CPR-1652 Support more instructions. + if (BOp->getOpcode() == Instruction::Add) + Offset += Val; + else if (BOp->getOpcode() == Instruction::Sub) + Offset -= Val; + else + break; + + V = BOp->getOperand(0); + } + return {V, Offset}; +} + +static std::optional getConstStartLoc(const MemoryLocation &Loc, + unsigned BitWidth) { + if (isa(Loc.Ptr)) + return APInt::getZero(BitWidth); + + if (const auto *CE = dyn_cast(Loc.Ptr)) { + if (CE->getOpcode() == Instruction::IntToPtr) { + if (auto *CI = dyn_cast(CE->getOperand(0))) + return CI->getValue(); + } + } + + if (const auto *IntToPtr = dyn_cast(Loc.Ptr)) { + if (auto *CI = dyn_cast(IntToPtr->getOperand(0))) + return CI->getValue(); + } + + return std::nullopt; +} + +AliasResult VMAAResult::alias(const MemoryLocation &LocA, + const MemoryLocation &LocB, AAQueryInfo &AAQI, + const Instruction *I) { + const unsigned ASA = LocA.Ptr->getType()->getPointerAddressSpace(); + const unsigned ASB = LocB.Ptr->getType()->getPointerAddressSpace(); + + // If we don't know what this is, bail out. + if (ASA > MaxAS || ASB > MaxAS) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + // Pointers can't alias if they are not in the same address space. + if (ASA != ASB) + return AliasResult::NoAlias; + + // Since pointers are in the same address space, handle only cases that are + // interesting to us. + if (!StorageAS.count(ASA) && !HeapAS.count(ASA)) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + // Don't check unknown memory locations. + if (!LocA.Size.isPrecise() || !LocB.Size.isPrecise()) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + // Only 256-bit keys are valid for storage. + if (StorageAS.count(ASA)) { + constexpr unsigned KeyByteWidth = 32; + if (LocA.Size != KeyByteWidth || LocB.Size != KeyByteWidth) + return AAResultBase::alias(LocA, LocB, AAQI, I); + } + + unsigned BitWidth = DL.getPointerSizeInBits(ASA); + auto StartA = getConstStartLoc(LocA, BitWidth); + auto StartB = getConstStartLoc(LocB, BitWidth); + + // If we don't have constant start locations, try to get the base pointer and + // the offset. In case we managed to find them and pointers have the same + // base, we can compare offsets to prove aliasing. Otherwise, forward the + // query to the next alias analysis. + if (!StartA || !StartB) { + auto [BaseA, OffsetA] = getBaseWithOffset(LocA.Ptr, BitWidth); + auto [BaseB, OffsetB] = getBaseWithOffset(LocB.Ptr, BitWidth); + if (!BaseA || !BaseB || BaseA != BaseB) + return AAResultBase::alias(LocA, LocB, AAQI, I); + + StartA = OffsetA; + StartB = OffsetB; + } + + // Extend start locations to the same bitwidth and not less than pointer size. + unsigned MaxBitWidth = std::max(StartA->getBitWidth(), StartB->getBitWidth()); + MaxBitWidth = std::max(MaxBitWidth, BitWidth); + const APInt StartAVal = StartA->sext(MaxBitWidth); + const APInt StartBVal = StartB->sext(MaxBitWidth); + + // Keys in storage can't overlap. + if (StorageAS.count(ASA)) { + if (StartAVal == StartBVal) + return AliasResult::MustAlias; + return AliasResult::NoAlias; + } + + // If heap locations are the same, they either must or partially alias based + // on the size of locations. + if (StartAVal == StartBVal) { + if (LocA.Size == LocB.Size) + return AliasResult::MustAlias; + return AliasResult::PartialAlias; + } + + auto DoesOverlap = [](const APInt &X, const APInt &XEnd, const APInt &Y) { + return Y.sge(X) && Y.slt(XEnd); + }; + + // For heap accesses, if locations don't overlap, they are not aliasing. + if (!DoesOverlap(StartAVal, StartAVal + LocA.Size.getValue(), StartBVal) && + !DoesOverlap(StartBVal, StartBVal + LocB.Size.getValue(), StartAVal)) + return AliasResult::NoAlias; + return AliasResult::PartialAlias; +} From 5a1ade33dd2e986e328fc8e679834117bce15f55 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 6 Nov 2024 17:38:08 +0100 Subject: [PATCH 096/203] [EVM] Add pre-commit tests for Add implementation of AliasAnalysis Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/aa-dse.ll | 20 ++ llvm/test/CodeGen/EVM/aa-eval.ll | 257 +++++++++++++++++++++++++ llvm/test/CodeGen/EVM/aa-memory-opt.ll | 231 ++++++++++++++++++++++ 3 files changed, 508 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/aa-dse.ll create mode 100644 llvm/test/CodeGen/EVM/aa-eval.ll create mode 100644 llvm/test/CodeGen/EVM/aa-memory-opt.ll diff --git a/llvm/test/CodeGen/EVM/aa-dse.ll b/llvm/test/CodeGen/EVM/aa-dse.ll new file mode 100644 index 000000000000..5f0af2ddfdd7 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aa-dse.ll @@ -0,0 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -passes=dse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define void @test() { +; CHECK-LABEL: define void @test() { +; CHECK-NEXT: [[PTR1:%.*]] = inttoptr i256 32 to ptr addrspace(1) +; CHECK-NEXT: [[PTR2:%.*]] = inttoptr i256 32 to ptr addrspace(1) +; CHECK-NEXT: store i256 2, ptr addrspace(1) [[PTR1]], align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) [[PTR2]], align 64 +; CHECK-NEXT: ret void +; + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} diff --git a/llvm/test/CodeGen/EVM/aa-eval.ll b/llvm/test/CodeGen/EVM/aa-eval.ll new file mode 100644 index 000000000000..61a51f755ea2 --- /dev/null +++ b/llvm/test/CodeGen/EVM/aa-eval.ll @@ -0,0 +1,257 @@ +; RUN: opt -passes=aa-eval -aa-pipeline=basic-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK-LABEL: Function: test_offset_i8_noalias +; CHECK: MayAlias: i8 addrspace(1)* %inttoptr1, i8 addrspace(1)* %inttoptr2 +define void @test_offset_i8_noalias(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i8 + %add1 = add i8 %ptrtoint, 32 + %inttoptr1 = inttoptr i8 %add1 to ptr addrspace(1) + store i8 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i8 %ptrtoint, 33 + %inttoptr2 = inttoptr i8 %add2 to ptr addrspace(1) + store i8 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_i512_noalias +; CHECK: MayAlias: i512 addrspace(1)* %inttoptr1, i512 addrspace(1)* %inttoptr2 +define void @test_offset_i512_noalias(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i512 + %add1 = add i512 %ptrtoint, 32 + %inttoptr1 = inttoptr i512 %add1 to ptr addrspace(1) + store i512 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i512 %ptrtoint, 96 + %inttoptr2 = inttoptr i512 %add2 to ptr addrspace(1) + store i512 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_mustalias +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_mustalias(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %inttoptr1 = inttoptr i256 %ptrtoint to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %inttoptr2 = inttoptr i256 %ptrtoint to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_noalias1 +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_noalias1(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, 64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_noalias2 +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_noalias2(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, -32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, -64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_noalias3 +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_noalias3(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = sub i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = sub i256 %ptrtoint, 64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_partialalias1 +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_partialalias1(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, 48 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_partialalias2 +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_partialalias2(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, -32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, -48 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_offset_partialalias3 +; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +define void @test_offset_partialalias3(ptr addrspace(1) %addr) { + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = sub i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = sub i256 %ptrtoint, 48 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + ret void +} + +; CHECK-LABEL: Function: test_as1_i8_noalias +; CHECK: MayAlias: i8 addrspace(1)* %ptr1, i8 addrspace(1)* %ptr2 +define void @test_as1_i8_noalias() { + %ptr1 = inttoptr i8 32 to ptr addrspace(1) + %ptr2 = inttoptr i8 64 to ptr addrspace(1) + store i8 2, ptr addrspace(1) %ptr1, align 64 + store i8 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_unknown_as +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(10)* %ptr2 +define void @test_unknown_as() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 64 to ptr addrspace(10) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(10) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_mayalias +; CHECK: MayAlias: i256* %0, i256* %1 +define void @test_mayalias(ptr %0, ptr %1) { + store i256 2, ptr %0, align 64 + store i256 1, ptr %1, align 64 + ret void +} + +; CHECK-LABEL: Function: test_noalias_fallback +; CHECK: NoAlias: i256* %0, i256* %1 +define void @test_noalias_fallback(ptr noalias %0, ptr noalias %1) { + store i256 2, ptr %0, align 64 + store i256 1, ptr %1, align 64 + ret void +} + +; CHECK-LABEL: Function: test_noalias +; CHECK: MayAlias: i256* %ptr0, i256 addrspace(1)* %ptr1 +; CHECK: MayAlias: i256* %ptr0, i256 addrspace(2)* %ptr2 +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(2)* %ptr2 +; CHECK: MayAlias: i256* %ptr0, i256 addrspace(4)* %ptr4 +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(4)* %ptr4 +; CHECK: MayAlias: i256 addrspace(2)* %ptr2, i256 addrspace(4)* %ptr4 +; CHECK: MayAlias: i256* %ptr0, i256 addrspace(5)* %ptr5 +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(5)* %ptr5 +; CHECK: MayAlias: i256 addrspace(2)* %ptr2, i256 addrspace(5)* %ptr5 +; CHECK: MayAlias: i256 addrspace(4)* %ptr4, i256 addrspace(5)* %ptr5 +; CHECK: MayAlias: i256* %ptr0, i256 addrspace(6)* %ptr6 +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(6)* %ptr6 +; CHECK: MayAlias: i256 addrspace(2)* %ptr2, i256 addrspace(6)* %ptr6 +; CHECK: MayAlias: i256 addrspace(4)* %ptr4, i256 addrspace(6)* %ptr6 +; CHECK: MayAlias: i256 addrspace(5)* %ptr5, i256 addrspace(6)* %ptr6 +define void @test_noalias() { + %ptr0 = inttoptr i256 32 to ptr + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 32 to ptr addrspace(2) + %ptr4 = inttoptr i256 32 to ptr addrspace(4) + %ptr5 = inttoptr i256 32 to ptr addrspace(5) + %ptr6 = inttoptr i256 32 to ptr addrspace(6) + store i256 1, ptr %ptr0, align 64 + store i256 1, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(2) %ptr2, align 64 + store i256 1, ptr addrspace(4) %ptr4, align 64 + store i256 1, ptr addrspace(5) %ptr5, align 64 + store i256 1, ptr addrspace(6) %ptr6, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as1_noalias +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +define void @test_as1_noalias() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 64 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as1_mustalias +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +define void @test_as1_mustalias() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as1_partialalias +; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +define void @test_as1_partialalias() { + %ptr1 = inttoptr i256 32 to ptr addrspace(1) + %ptr2 = inttoptr i256 48 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr1, align 64 + store i256 1, ptr addrspace(1) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as5_noalias +; CHECK: MayAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 +define void @test_as5_noalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(5) + %ptr2 = inttoptr i256 1 to ptr addrspace(5) + store i256 2, ptr addrspace(5) %ptr1, align 64 + store i256 1, ptr addrspace(5) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as5_mustalias +; CHECK: MayAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 +define void @test_as5_mustalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(5) + %ptr2 = inttoptr i256 0 to ptr addrspace(5) + store i256 2, ptr addrspace(5) %ptr1, align 64 + store i256 1, ptr addrspace(5) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as6_noalias +; CHECK: MayAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 +define void @test_as6_noalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(6) + %ptr2 = inttoptr i256 1 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr1, align 64 + store i256 1, ptr addrspace(6) %ptr2, align 64 + ret void +} + +; CHECK-LABEL: Function: test_as6_mustalias +; CHECK: MayAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 +define void @test_as6_mustalias() { + %ptr1 = inttoptr i256 0 to ptr addrspace(6) + %ptr2 = inttoptr i256 0 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr1, align 64 + store i256 1, ptr addrspace(6) %ptr2, align 64 + ret void +} diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll new file mode 100644 index 000000000000..2e108cdfe5ff --- /dev/null +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -0,0 +1,231 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test_offset(ptr addrspace(1) %addr) { +; CHECK-LABEL: define i256 @test_offset +; CHECK-SAME: (ptr addrspace(1) [[ADDR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[PTRTOINT:%.*]] = ptrtoint ptr addrspace(1) [[ADDR]] to i256 +; CHECK-NEXT: [[ADD1:%.*]] = add i256 [[PTRTOINT]], 32 +; CHECK-NEXT: [[INTTOPTR1:%.*]] = inttoptr i256 [[ADD1]] to ptr addrspace(1) +; CHECK-NEXT: store i256 3, ptr addrspace(1) [[INTTOPTR1]], align 1 +; CHECK-NEXT: [[ADD2:%.*]] = add i256 [[PTRTOINT]], 64 +; CHECK-NEXT: [[INTTOPTR2:%.*]] = inttoptr i256 [[ADD2]] to ptr addrspace(1) +; CHECK-NEXT: store i256 58, ptr addrspace(1) [[INTTOPTR2]], align 1 +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR1]], align 1 +; CHECK-NEXT: ret i256 [[LOAD]] +; + %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 + %add1 = add i256 %ptrtoint, 32 + %inttoptr1 = inttoptr i256 %add1 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %inttoptr1, align 1 + %add2 = add i256 %ptrtoint, 64 + %inttoptr2 = inttoptr i256 %add2 to ptr addrspace(1) + store i256 58, ptr addrspace(1) %inttoptr2, align 1 + %load = load i256, ptr addrspace(1) %inttoptr1, align 1 + ret i256 %load +} + +define i256 @test_memcpy() { +; CHECK-LABEL: define i256 @test_memcpy +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: tail call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noundef nonnull align 32 dereferenceable(53) inttoptr (i256 96 to ptr addrspace(1)), ptr addrspace(1) noundef nonnull align 256 dereferenceable(53) inttoptr (i256 256 to ptr addrspace(1)), i256 53, i1 false) +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + tail call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) inttoptr (i256 96 to ptr addrspace(1)), ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 53, i1 false) + %ret = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_different_locsizes() { +; CHECK-LABEL: define i256 @test_different_locsizes +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: store i8 1, ptr addrspace(1) inttoptr (i8 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + store i8 1, ptr addrspace(1) inttoptr (i8 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_gas() { +; CHECK-LABEL: define i256 @test_gas +; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: store i256 [[GAS]], ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + %gas = call i256 @llvm.evm.gas() + store i256 %gas, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_log0(ptr addrspace(1) %off, i256 %size) { +; CHECK-LABEL: define noundef i256 @test_log0 +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) +; CHECK-NEXT: ret i256 2 +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) + %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as() { +; CHECK-LABEL: define i256 @test_as +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as1_overlap() { +; CHECK-LABEL: define i256 @test_as1_overlap +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 31 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) null, align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 31 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) null, align 64 + ret i256 %ret +} + +define i256 @test_as1_null() { +; CHECK-LABEL: define i256 @test_as1_null +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) null, align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) null, align 64 + ret i256 %ret +} + +define i256 @test_as1_small() { +; CHECK-LABEL: define i256 @test_as1_small +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_as1_large() { +; CHECK-LABEL: define i256 @test_as1_large +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(1)), align 4294967296 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 + store i256 1, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(1)), align 64 + %ret = load i256, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 + ret i256 %ret +} + +define i256 @test_as5_null() { +; CHECK-LABEL: define i256 @test_as5_null +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(5) null, align 64 + store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 + %ret = load i256, ptr addrspace(5) null, align 64 + ret i256 %ret +} + +define i256 @test_as5_small() { +; CHECK-LABEL: define i256 @test_as5_small +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as5_large() { +; CHECK-LABEL: define i256 @test_as5_large +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 4294967296 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 64 + store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 + %ret = load i256, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 64 + ret i256 %ret +} + +define i256 @test_as6_small() { +; CHECK-LABEL: define i256 @test_as6_small +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 +; CHECK-NEXT: store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 + %ret = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + ret i256 %ret +} + +define i256 @test_as6_large() { +; CHECK-LABEL: define i256 @test_as6_large +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 4294967296 +; CHECK-NEXT: store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 4294967296 +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 64 + store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 + %ret = load i256, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 64 + ret i256 %ret +} + +declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1), ptr addrspace(1), i256, i1 immarg) +declare i256 @llvm.evm.gas() +declare void @llvm.evm.log0(ptr addrspace(1), i256) + From 8483ae4a920738dd62e764aed2071d641aa17f90 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 31 Oct 2024 15:19:32 +0100 Subject: [PATCH 097/203] [EVM] Add implementation of AliasAnalysis Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/CMakeLists.txt | 1 + llvm/lib/Target/EVM/EVM.h | 9 ++- llvm/lib/Target/EVM/EVMAliasAnalysis.cpp | 60 +++++++++++++++++++ llvm/lib/Target/EVM/EVMAliasAnalysis.h | 66 +++++++++++++++++++++ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 27 +++++++++ llvm/lib/Target/EVM/EVMTargetMachine.h | 1 + llvm/test/CodeGen/EVM/aa-dse.ll | 2 - llvm/test/CodeGen/EVM/aa-eval.ll | 66 ++++++++++----------- llvm/test/CodeGen/EVM/aa-memory-opt.ll | 73 ++++++++++-------------- 9 files changed, 226 insertions(+), 79 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMAliasAnalysis.cpp create mode 100644 llvm/lib/Target/EVM/EVMAliasAnalysis.h diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 5ed6d7406daa..5a1d09479bb0 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -36,6 +36,7 @@ add_custom_command( add_custom_target(EVMStdLib DEPENDS "${EVM_STDLIB_PATH_OUT}") add_llvm_target(EVMCodeGen + EVMAliasAnalysis.cpp EVMAllocaHoisting.cpp EVMArgumentMove.cpp EVMAsmPrinter.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 432fd869460d..95e5160c410b 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -16,6 +16,7 @@ #include "llvm/IR/PassManager.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Pass.h" namespace llvm { class EVMTargetMachine; @@ -31,7 +32,9 @@ enum AddressSpaces { AS_CALL_DATA = 2, AS_RETURN_DATA = 3, AS_CODE = 4, - AS_STORAGE = 5 + AS_STORAGE = 5, + AS_TSTORAGE = 6, + MAX_ADDRESS = AS_TSTORAGE }; } // namespace EVMAS @@ -45,6 +48,8 @@ unsigned constexpr PUSH = 3; // LLVM IR passes. ModulePass *createEVMLowerIntrinsicsPass(); FunctionPass *createEVMCodegenPreparePass(); +ImmutablePass *createEVMAAWrapperPass(); +ImmutablePass *createEVMExternalAAWrapperPass(); // ISel and immediate followup passes. FunctionPass *createEVMISelDag(EVMTargetMachine &TM, @@ -73,6 +78,8 @@ void initializeEVMSingleUseExpressionPass(PassRegistry &); void initializeEVMSplitCriticalEdgesPass(PassRegistry &); void initializeEVMStackifyPass(PassRegistry &); void initializeEVMBPStackificationPass(PassRegistry &); +void initializeEVMAAWrapperPassPass(PassRegistry &); +void initializeEVMExternalAAWrapperPass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { EVMLinkRuntimePass() = default; diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp new file mode 100644 index 000000000000..4170c96a6fe2 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp @@ -0,0 +1,60 @@ +//===-- EVMAliasAnalysis.cpp - EVM alias analysis ---------------*- 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 is the EVM address space based alias analysis pass. +// +//===----------------------------------------------------------------------===// + +#include "EVMAliasAnalysis.h" +#include "EVM.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-aa" + +AnalysisKey EVMAA::Key; + +// Register this pass... +char EVMAAWrapperPass::ID = 0; +char EVMExternalAAWrapper::ID = 0; + +INITIALIZE_PASS(EVMAAWrapperPass, "evm-aa", + "EVM Address space based Alias Analysis", false, true) + +INITIALIZE_PASS(EVMExternalAAWrapper, "evm-aa-wrapper", + "EVM Address space based Alias Analysis Wrapper", false, true) + +ImmutablePass *llvm::createEVMAAWrapperPass() { return new EVMAAWrapperPass(); } + +ImmutablePass *llvm::createEVMExternalAAWrapperPass() { + return new EVMExternalAAWrapper(); +} + +EVMAAWrapperPass::EVMAAWrapperPass() : ImmutablePass(ID) { + initializeEVMAAWrapperPassPass(*PassRegistry::getPassRegistry()); +} + +void EVMAAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); +} + +bool EVMAAWrapperPass::doInitialization(Module &M) { + SmallDenseSet StorageAS = {EVMAS::AS_STORAGE, EVMAS::AS_TSTORAGE}; + SmallDenseSet HeapAS = {EVMAS::AS_HEAP}; + Result = std::make_unique(M.getDataLayout(), StorageAS, HeapAS, + EVMAS::MAX_ADDRESS); + return false; +} + +VMAAResult EVMAA::run(Function &F, AnalysisManager &AM) { + SmallDenseSet StorageAS = {EVMAS::AS_STORAGE, EVMAS::AS_TSTORAGE}; + SmallDenseSet HeapAS = {EVMAS::AS_HEAP}; + return VMAAResult(F.getParent()->getDataLayout(), StorageAS, HeapAS, + EVMAS::MAX_ADDRESS); +} diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.h b/llvm/lib/Target/EVM/EVMAliasAnalysis.h new file mode 100644 index 000000000000..1f65703037ad --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.h @@ -0,0 +1,66 @@ +//===-- EVMAliasAnalysis.h - EVM alias analysis -----------------*- 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 is the EVM address space based alias analysis pass. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMALIASANALYSIS_H +#define LLVM_LIB_TARGET_EVM_EVMALIASANALYSIS_H + +#include "llvm/Analysis/VMAliasAnalysis.h" + +namespace llvm { +/// Analysis pass providing a never-invalidated alias analysis result. +class EVMAA : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + + static AnalysisKey Key; + +public: + using Result = VMAAResult; + VMAAResult run(Function &F, AnalysisManager &AM); +}; + +/// Legacy wrapper pass to provide the VMAAResult object. +class EVMAAWrapperPass : public ImmutablePass { + std::unique_ptr Result; + +public: + static char ID; + + EVMAAWrapperPass(); + + VMAAResult &getResult() { return *Result; } + const VMAAResult &getResult() const { return *Result; } + + bool doFinalization(Module &M) override { + Result.reset(); + return false; + } + + bool doInitialization(Module &M) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +// Wrapper around ExternalAAWrapperPass so that the default constructor gets the +// callback. +class EVMExternalAAWrapper : public ExternalAAWrapperPass { +public: + static char ID; + + EVMExternalAAWrapper() + : ExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { + if (auto *WrapperPass = P.getAnalysisIfAvailable()) + AAR.addAAResult(WrapperPass->getResult()); + }) {} +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMALIASANALYSIS_H diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 5db238303281..e8de2fd59c46 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -12,6 +12,7 @@ #include "EVM.h" +#include "EVMAliasAnalysis.h" #include "EVMMachineFunctionInfo.h" #include "EVMTargetMachine.h" #include "EVMTargetObjectFile.h" @@ -57,6 +58,8 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMSplitCriticalEdgesPass(PR); initializeEVMStackifyPass(PR); initializeEVMBPStackificationPass(PR); + initializeEVMAAWrapperPassPass(PR); + initializeEVMExternalAAWrapperPass(PR); } static std::string computeDataLayout() { @@ -113,6 +116,10 @@ bool EVMTargetMachine::parseMachineFunctionInfo( return false; } +void EVMTargetMachine::registerDefaultAliasAnalyses(AAManager &AAM) { + AAM.registerFunctionAnalysis(); +} + void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PB.registerPipelineStartEPCallback( [](ModulePassManager &PM, OptimizationLevel Level) { @@ -125,6 +132,18 @@ void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { if (Level.getSizeLevel() || Level.getSpeedupLevel() > 1) PM.addPass(MergeIdenticalBBPass()); }); + + PB.registerAnalysisRegistrationCallback([](FunctionAnalysisManager &FAM) { + FAM.registerPass([] { return EVMAA(); }); + }); + + PB.registerParseAACallback([](StringRef AAName, AAManager &AAM) { + if (AAName == "evm-aa") { + AAM.registerFunctionAnalysis(); + return true; + } + return false; + }); } namespace { @@ -159,6 +178,14 @@ class EVMPassConfig final : public TargetPassConfig { void EVMPassConfig::addIRPasses() { addPass(createEVMLowerIntrinsicsPass()); + if (TM->getOptLevel() != CodeGenOptLevel::None) { + addPass(createEVMAAWrapperPass()); + addPass( + createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { + if (auto *WrapperPass = P.getAnalysisIfAvailable()) + AAR.addAAResult(WrapperPass->getResult()); + })); + } TargetPassConfig::addIRPasses(); } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.h b/llvm/lib/Target/EVM/EVMTargetMachine.h index 8f01067f8ef3..390398449afe 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.h +++ b/llvm/lib/Target/EVM/EVMTargetMachine.h @@ -61,6 +61,7 @@ class EVMTargetMachine final : public LLVMTargetMachine { bool usesPhysRegsForValues() const override { return false; } void registerPassBuilderCallbacks(PassBuilder &PB) override; + void registerDefaultAliasAnalyses(AAManager &AAM) override; }; // EVMTargetMachine. } // end namespace llvm diff --git a/llvm/test/CodeGen/EVM/aa-dse.ll b/llvm/test/CodeGen/EVM/aa-dse.ll index 5f0af2ddfdd7..c2b639fd88b4 100644 --- a/llvm/test/CodeGen/EVM/aa-dse.ll +++ b/llvm/test/CodeGen/EVM/aa-dse.ll @@ -6,9 +6,7 @@ target triple = "evm" define void @test() { ; CHECK-LABEL: define void @test() { -; CHECK-NEXT: [[PTR1:%.*]] = inttoptr i256 32 to ptr addrspace(1) ; CHECK-NEXT: [[PTR2:%.*]] = inttoptr i256 32 to ptr addrspace(1) -; CHECK-NEXT: store i256 2, ptr addrspace(1) [[PTR1]], align 64 ; CHECK-NEXT: store i256 1, ptr addrspace(1) [[PTR2]], align 64 ; CHECK-NEXT: ret void ; diff --git a/llvm/test/CodeGen/EVM/aa-eval.ll b/llvm/test/CodeGen/EVM/aa-eval.ll index 61a51f755ea2..a59e89e91306 100644 --- a/llvm/test/CodeGen/EVM/aa-eval.ll +++ b/llvm/test/CodeGen/EVM/aa-eval.ll @@ -1,10 +1,10 @@ -; RUN: opt -passes=aa-eval -aa-pipeline=basic-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s +; RUN: opt -passes=aa-eval -aa-pipeline=basic-aa,evm-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" ; CHECK-LABEL: Function: test_offset_i8_noalias -; CHECK: MayAlias: i8 addrspace(1)* %inttoptr1, i8 addrspace(1)* %inttoptr2 +; CHECK: NoAlias: i8 addrspace(1)* %inttoptr1, i8 addrspace(1)* %inttoptr2 define void @test_offset_i8_noalias(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i8 %add1 = add i8 %ptrtoint, 32 @@ -17,7 +17,7 @@ define void @test_offset_i8_noalias(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_i512_noalias -; CHECK: MayAlias: i512 addrspace(1)* %inttoptr1, i512 addrspace(1)* %inttoptr2 +; CHECK: NoAlias: i512 addrspace(1)* %inttoptr1, i512 addrspace(1)* %inttoptr2 define void @test_offset_i512_noalias(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i512 %add1 = add i512 %ptrtoint, 32 @@ -30,7 +30,7 @@ define void @test_offset_i512_noalias(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_mustalias -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: MustAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_mustalias(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %inttoptr1 = inttoptr i256 %ptrtoint to ptr addrspace(1) @@ -41,7 +41,7 @@ define void @test_offset_mustalias(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_noalias1 -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: NoAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_noalias1(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = add i256 %ptrtoint, 32 @@ -54,7 +54,7 @@ define void @test_offset_noalias1(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_noalias2 -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: NoAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_noalias2(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = add i256 %ptrtoint, -32 @@ -67,7 +67,7 @@ define void @test_offset_noalias2(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_noalias3 -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: NoAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_noalias3(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = sub i256 %ptrtoint, 32 @@ -80,7 +80,7 @@ define void @test_offset_noalias3(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_partialalias1 -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: PartialAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_partialalias1(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = add i256 %ptrtoint, 32 @@ -93,7 +93,7 @@ define void @test_offset_partialalias1(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_partialalias2 -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: PartialAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_partialalias2(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = add i256 %ptrtoint, -32 @@ -106,7 +106,7 @@ define void @test_offset_partialalias2(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_offset_partialalias3 -; CHECK: MayAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 +; CHECK: PartialAlias: i256 addrspace(1)* %inttoptr1, i256 addrspace(1)* %inttoptr2 define void @test_offset_partialalias3(ptr addrspace(1) %addr) { %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = sub i256 %ptrtoint, 32 @@ -119,7 +119,7 @@ define void @test_offset_partialalias3(ptr addrspace(1) %addr) { } ; CHECK-LABEL: Function: test_as1_i8_noalias -; CHECK: MayAlias: i8 addrspace(1)* %ptr1, i8 addrspace(1)* %ptr2 +; CHECK: NoAlias: i8 addrspace(1)* %ptr1, i8 addrspace(1)* %ptr2 define void @test_as1_i8_noalias() { %ptr1 = inttoptr i8 32 to ptr addrspace(1) %ptr2 = inttoptr i8 64 to ptr addrspace(1) @@ -155,21 +155,21 @@ define void @test_noalias_fallback(ptr noalias %0, ptr noalias %1) { } ; CHECK-LABEL: Function: test_noalias -; CHECK: MayAlias: i256* %ptr0, i256 addrspace(1)* %ptr1 -; CHECK: MayAlias: i256* %ptr0, i256 addrspace(2)* %ptr2 -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(2)* %ptr2 -; CHECK: MayAlias: i256* %ptr0, i256 addrspace(4)* %ptr4 -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(4)* %ptr4 -; CHECK: MayAlias: i256 addrspace(2)* %ptr2, i256 addrspace(4)* %ptr4 -; CHECK: MayAlias: i256* %ptr0, i256 addrspace(5)* %ptr5 -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(5)* %ptr5 -; CHECK: MayAlias: i256 addrspace(2)* %ptr2, i256 addrspace(5)* %ptr5 -; CHECK: MayAlias: i256 addrspace(4)* %ptr4, i256 addrspace(5)* %ptr5 -; CHECK: MayAlias: i256* %ptr0, i256 addrspace(6)* %ptr6 -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(6)* %ptr6 -; CHECK: MayAlias: i256 addrspace(2)* %ptr2, i256 addrspace(6)* %ptr6 -; CHECK: MayAlias: i256 addrspace(4)* %ptr4, i256 addrspace(6)* %ptr6 -; CHECK: MayAlias: i256 addrspace(5)* %ptr5, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(1)* %ptr1 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(2)* %ptr2 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(2)* %ptr2 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(4)* %ptr4 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(4)* %ptr4 +; CHECK: NoAlias: i256 addrspace(2)* %ptr2, i256 addrspace(4)* %ptr4 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256 addrspace(2)* %ptr2, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256 addrspace(4)* %ptr4, i256 addrspace(5)* %ptr5 +; CHECK: NoAlias: i256* %ptr0, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(2)* %ptr2, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(4)* %ptr4, i256 addrspace(6)* %ptr6 +; CHECK: NoAlias: i256 addrspace(5)* %ptr5, i256 addrspace(6)* %ptr6 define void @test_noalias() { %ptr0 = inttoptr i256 32 to ptr %ptr1 = inttoptr i256 32 to ptr addrspace(1) @@ -187,7 +187,7 @@ define void @test_noalias() { } ; CHECK-LABEL: Function: test_as1_noalias -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +; CHECK: NoAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 define void @test_as1_noalias() { %ptr1 = inttoptr i256 32 to ptr addrspace(1) %ptr2 = inttoptr i256 64 to ptr addrspace(1) @@ -197,7 +197,7 @@ define void @test_as1_noalias() { } ; CHECK-LABEL: Function: test_as1_mustalias -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +; CHECK: MustAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 define void @test_as1_mustalias() { %ptr1 = inttoptr i256 32 to ptr addrspace(1) %ptr2 = inttoptr i256 32 to ptr addrspace(1) @@ -207,7 +207,7 @@ define void @test_as1_mustalias() { } ; CHECK-LABEL: Function: test_as1_partialalias -; CHECK: MayAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 +; CHECK: PartialAlias: i256 addrspace(1)* %ptr1, i256 addrspace(1)* %ptr2 define void @test_as1_partialalias() { %ptr1 = inttoptr i256 32 to ptr addrspace(1) %ptr2 = inttoptr i256 48 to ptr addrspace(1) @@ -217,7 +217,7 @@ define void @test_as1_partialalias() { } ; CHECK-LABEL: Function: test_as5_noalias -; CHECK: MayAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 +; CHECK: NoAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 define void @test_as5_noalias() { %ptr1 = inttoptr i256 0 to ptr addrspace(5) %ptr2 = inttoptr i256 1 to ptr addrspace(5) @@ -227,7 +227,7 @@ define void @test_as5_noalias() { } ; CHECK-LABEL: Function: test_as5_mustalias -; CHECK: MayAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 +; CHECK: MustAlias: i256 addrspace(5)* %ptr1, i256 addrspace(5)* %ptr2 define void @test_as5_mustalias() { %ptr1 = inttoptr i256 0 to ptr addrspace(5) %ptr2 = inttoptr i256 0 to ptr addrspace(5) @@ -237,7 +237,7 @@ define void @test_as5_mustalias() { } ; CHECK-LABEL: Function: test_as6_noalias -; CHECK: MayAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 +; CHECK: NoAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 define void @test_as6_noalias() { %ptr1 = inttoptr i256 0 to ptr addrspace(6) %ptr2 = inttoptr i256 1 to ptr addrspace(6) @@ -247,7 +247,7 @@ define void @test_as6_noalias() { } ; CHECK-LABEL: Function: test_as6_mustalias -; CHECK: MayAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 +; CHECK: MustAlias: i256 addrspace(6)* %ptr1, i256 addrspace(6)* %ptr2 define void @test_as6_mustalias() { %ptr1 = inttoptr i256 0 to ptr addrspace(6) %ptr2 = inttoptr i256 0 to ptr addrspace(6) diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll index 2e108cdfe5ff..8fdbf224f60e 100644 --- a/llvm/test/CodeGen/EVM/aa-memory-opt.ll +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -5,7 +5,7 @@ target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @test_offset(ptr addrspace(1) %addr) { -; CHECK-LABEL: define i256 @test_offset +; CHECK-LABEL: define noundef i256 @test_offset ; CHECK-SAME: (ptr addrspace(1) [[ADDR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[PTRTOINT:%.*]] = ptrtoint ptr addrspace(1) [[ADDR]] to i256 ; CHECK-NEXT: [[ADD1:%.*]] = add i256 [[PTRTOINT]], 32 @@ -14,8 +14,7 @@ define i256 @test_offset(ptr addrspace(1) %addr) { ; CHECK-NEXT: [[ADD2:%.*]] = add i256 [[PTRTOINT]], 64 ; CHECK-NEXT: [[INTTOPTR2:%.*]] = inttoptr i256 [[ADD2]] to ptr addrspace(1) ; CHECK-NEXT: store i256 58, ptr addrspace(1) [[INTTOPTR2]], align 1 -; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR1]], align 1 -; CHECK-NEXT: ret i256 [[LOAD]] +; CHECK-NEXT: ret i256 3 ; %ptrtoint = ptrtoint ptr addrspace(1) %addr to i256 %add1 = add i256 %ptrtoint, 32 @@ -29,12 +28,11 @@ define i256 @test_offset(ptr addrspace(1) %addr) { } define i256 @test_memcpy() { -; CHECK-LABEL: define i256 @test_memcpy -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-LABEL: define noundef i256 @test_memcpy +; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 ; CHECK-NEXT: tail call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noundef nonnull align 32 dereferenceable(53) inttoptr (i256 96 to ptr addrspace(1)), ptr addrspace(1) noundef nonnull align 256 dereferenceable(53) inttoptr (i256 256 to ptr addrspace(1)), i256 53, i1 false) -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 tail call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) inttoptr (i256 96 to ptr addrspace(1)), ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 53, i1 false) @@ -43,12 +41,11 @@ define i256 @test_memcpy() { } define i256 @test_different_locsizes() { -; CHECK-LABEL: define i256 @test_different_locsizes +; CHECK-LABEL: define noundef i256 @test_different_locsizes ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 ; CHECK-NEXT: store i8 1, ptr addrspace(1) inttoptr (i8 1 to ptr addrspace(1)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 store i8 1, ptr addrspace(1) inttoptr (i8 1 to ptr addrspace(1)), align 64 @@ -57,13 +54,12 @@ define i256 @test_different_locsizes() { } define i256 @test_gas() { -; CHECK-LABEL: define i256 @test_gas -; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-LABEL: define noundef i256 @test_gas +; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() ; CHECK-NEXT: store i256 [[GAS]], ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 %gas = call i256 @llvm.evm.gas() @@ -74,7 +70,7 @@ define i256 @test_gas() { define i256 @test_log0(ptr addrspace(1) %off, i256 %size) { ; CHECK-LABEL: define noundef i256 @test_log0 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: ret i256 2 @@ -86,12 +82,11 @@ define i256 @test_log0(ptr addrspace(1) %off, i256 %size) { } define i256 @test_as() { -; CHECK-LABEL: define i256 @test_as +; CHECK-LABEL: define noundef i256 @test_as ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 @@ -101,7 +96,7 @@ define i256 @test_as() { define i256 @test_as1_overlap() { ; CHECK-LABEL: define i256 @test_as1_overlap -; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { ; CHECK-NEXT: store i256 2, ptr addrspace(1) null, align 4294967296 ; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 31 to ptr addrspace(1)), align 64 ; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) null, align 4294967296 @@ -114,12 +109,11 @@ define i256 @test_as1_overlap() { } define i256 @test_as1_null() { -; CHECK-LABEL: define i256 @test_as1_null +; CHECK-LABEL: define noundef i256 @test_as1_null ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(1) null, align 4294967296 ; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) null, align 4294967296 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(1) null, align 64 store i256 1, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 64 @@ -128,12 +122,11 @@ define i256 @test_as1_null() { } define i256 @test_as1_small() { -; CHECK-LABEL: define i256 @test_as1_small +; CHECK-LABEL: define noundef i256 @test_as1_small ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 ; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(1) inttoptr (i256 33 to ptr addrspace(1)), align 64 store i256 1, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 @@ -142,12 +135,11 @@ define i256 @test_as1_small() { } define i256 @test_as1_large() { -; CHECK-LABEL: define i256 @test_as1_large +; CHECK-LABEL: define noundef i256 @test_as1_large ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 ; CHECK-NEXT: store i256 1, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(1)), align 4294967296 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533664 to ptr addrspace(1)), align 64 store i256 1, ptr addrspace(1) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(1)), align 64 @@ -156,12 +148,11 @@ define i256 @test_as1_large() { } define i256 @test_as5_null() { -; CHECK-LABEL: define i256 @test_as5_null +; CHECK-LABEL: define noundef i256 @test_as5_null ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 ; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) null, align 4294967296 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(5) null, align 64 store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 @@ -170,12 +161,11 @@ define i256 @test_as5_null() { } define i256 @test_as5_small() { -; CHECK-LABEL: define i256 @test_as5_small +; CHECK-LABEL: define noundef i256 @test_as5_small ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 @@ -184,12 +174,11 @@ define i256 @test_as5_small() { } define i256 @test_as5_large() { -; CHECK-LABEL: define i256 @test_as5_large +; CHECK-LABEL: define noundef i256 @test_as5_large ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 4294967296 ; CHECK-NEXT: store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 4294967296 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(5) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(5)), align 64 store i256 1, ptr addrspace(5) inttoptr (i256 1 to ptr addrspace(5)), align 64 @@ -198,12 +187,11 @@ define i256 @test_as5_large() { } define i256 @test_as6_small() { -; CHECK-LABEL: define i256 @test_as6_small +; CHECK-LABEL: define noundef i256 @test_as6_small ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 ; CHECK-NEXT: store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 @@ -212,12 +200,11 @@ define i256 @test_as6_small() { } define i256 @test_as6_large() { -; CHECK-LABEL: define i256 @test_as6_large +; CHECK-LABEL: define noundef i256 @test_as6_large ; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 4294967296 ; CHECK-NEXT: store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 4294967296 -; CHECK-NEXT: ret i256 [[RET]] +; CHECK-NEXT: ret i256 2 ; store i256 2, ptr addrspace(6) inttoptr (i256 53919893334301279589334030174039261352344891250716429051063678533632 to ptr addrspace(6)), align 64 store i256 1, ptr addrspace(6) inttoptr (i256 1 to ptr addrspace(6)), align 64 From fdf8b2eff8ba93e39e150f5c50286c463cb34d2a Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 4 Nov 2024 15:39:27 +0100 Subject: [PATCH 098/203] Introduce VMSHA3ConstFolding Signed-off-by: Vladimir Radosavljevic --- .../llvm/Transforms/Scalar/SHA3ConstFolding.h | 36 + llvm/lib/Transforms/Scalar/CMakeLists.txt | 3 + .../Transforms/Scalar/SHA3ConstFolding.cpp | 742 ++++++++++++++++++ 3 files changed, 781 insertions(+) create mode 100644 llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h create mode 100644 llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp diff --git a/llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h b/llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h new file mode 100644 index 000000000000..be77e6b2251b --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/SHA3ConstFolding.h @@ -0,0 +1,36 @@ +//===-- SHA3ConstFolding.h - Const fold calls to sha3 -----------*- 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 provides the interface for the SHA3 const folding pass. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_SHA3CONSTFOLDING_H +#define LLVM_TRANSFORMS_SCALAR_SHA3CONSTFOLDING_H + +#include "llvm/Analysis/AliasAnalysis.h" + +namespace llvm { + +class Function; +class AssumptionCache; +class MemorySSA; +class DominatorTree; +class TargetLibraryInfo; +class LoopInfo; +class Instruction; + +bool runSHA3ConstFolding( + Function &F, AliasAnalysis &AA, AssumptionCache &AC, MemorySSA &MSSA, + DominatorTree &DT, const TargetLibraryInfo &TLI, const LoopInfo &LI, + const std::function &IsSha3Call, + unsigned HeapAS); + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_SHA3CONSTFOLDING_H diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index dfa27013f0eb..43e0f4a43666 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -73,6 +73,9 @@ add_llvm_component_library(LLVMScalarOpts Scalarizer.cpp ScalarizeMaskedMemIntrin.cpp SeparateConstOffsetFromGEP.cpp + # EVM local begin + SHA3ConstFolding.cpp + # EVM local end SimpleLoopUnswitch.cpp SimplifyCFGPass.cpp Sink.cpp diff --git a/llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp b/llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp new file mode 100644 index 000000000000..1275610ad735 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/SHA3ConstFolding.cpp @@ -0,0 +1,742 @@ +//===-- SHA3ConstFolding.cpp - Const fold calls to sha3 ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// The code below tries to replace sha3 (that calucates keccak256 hash) calls +// with the calculated hash values. +// +// It uses the following general approach: given a sha3 call (MemoryUse), +// walk upwards to find store instructions (clobbers) with +// constant values that fully define data (in memory) for which we compute hash. +// Only sha3 calls with the constant 'size' argument are checked. +// +// For example: +// +// store i256 1, ptr addrspace(1) null +// store i256 2, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)) +// %hash = tail call i256 @sha3(ptr addrspace(1) null, i256 64, i1 true) +// ret i256 %hash +// +// is transformed into: +// +// store i256 1, ptr addrspace(1) null +// store i256 2, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)) +// ret i256 -536754096339594166047489462334966539640... +// +// A bit more concretely: +// +// For all sha3 calls: +// 1. Collect potentially dominating clobbering MemoryDefs by walking upwards. +// Check that clobbering values are constants, otherwise bail out. +// +// 2. Check that +// 1. Each clobber is withing the sha3 memory location: +// |--clobber--| +// |------MemUse-------| +// 2. Clobbers are not intersected with each other: +// |--cl1--| +// |cl2| +// |--cl3--| +// |------MemUse-------| +// 3.Collect clobber values +// +// 3. Create a memory array from the collected values and calculate +// the Keccak256 hash. +// +// 4. Run simplification for each instruction in the function, as sha3 folding +// can provide new opportunities for this. +// +// 5. If the simplification has changed the function, run one more iteration +// of the whole process starting from p.1. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/SHA3ConstFolding.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/Analysis/MemoryLocation.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/MustExecute.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/KECCAK.h" +#include "llvm/Transforms/Utils/AssumeBundleBuilder.h" +#include "llvm/Transforms/Utils/Local.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "sha3-constant-folding" + +STATISTIC(NumSHA3Folded, "Number of sha3 calls folded"); + +DEBUG_COUNTER(SHA3Counter, "sha3-constant-folding", + "Controls which instructions are removed"); + +namespace { +/// This structure holds an information about a single memory clobber. +/// While walking upwards starting at a sha3 call (which is a MemUse), +/// we create a MemClobber instance for each dominating memory clobber +/// (in current implementation - a store instruction) and put it in a +/// list which then gets sorted (by the 'Start' value). As result, we get +/// representation of a continuous memory region the sha3 computes a hash for. +struct MemClobber { + // Byte offset relatively to the beginning of the sha3 memory location. + uint64_t Start; + + // Size in bytes of the clobber. + uint64_t Size; + + // Value to be stored in the memory. + APInt Clobber; +}; + +/// This class holds an information required for folding sha3 calls +/// in the function F. +/// +/// The workflow with the class is as follows: +/// 1. Create an FoldingState instance. +/// The constructor collects the MemorySSA uses corresponding to +/// sha3 calls in the function F. +/// 2. Iterate through the collected memory uses calling the runFolding(), +/// which, on success, returns a computed keccak256 +/// hash value. +/// Replace sha3 values with the calculated hash values, +/// removing the calls and invoking removeFromMSSA() to keep up to date +/// the MemorySSA representation. +/// 3. Run simplifyInstructions() to simplify/clean up the F. +/// +class FoldingState { + /// Types of the relative placement of the two memory locations. + enum class OverlapType { + // Loc1 is completely within the Loc2 + // |-Loc1-| + // |-----Loc2----| + OL_Complete, + + // Loc1 is partially with the Loc2 + // and they both refer to the same underlying object + // |-Loc1-| + // |-----Loc2----| + OL_MaybePartial, + + // Memory locations don't intersect + // |--Loc1--| + // |---Loc2--| + OL_None, + + // Nothing can be determined + OL_Unknown + }; + + /// Describes how the memory clobber is placed with respect + /// to the memory use (sha3 call). + struct OverlapResult { + OverlapType Type; + + // Clobber offset relative to the beginning of the memory use. + // Unless the type is 'OL_Complete' actual offset value doesn't matter. + uint64_t ClobberOffset; + }; + + // Whether the function contains any irreducible control flow, useful for + // being accurately able to detect loops. + const bool ContainsIrreducibleLoops; + + // The sha3 calls to be analyzed. + SmallVector SHA3MemUses; + + Function &F; + AliasAnalysis &AA; + AssumptionCache &AC; + MemorySSA &MSSA; + std::unique_ptr MSSAUpdater; + const TargetLibraryInfo &TLI; + const DataLayout &DL; + const SimplifyQuery SQ; + const LoopInfo &LI; + unsigned HeapAS; + +public: + FoldingState(const FoldingState &) = delete; + FoldingState &operator=(const FoldingState &) = delete; + FoldingState(FoldingState &&) = delete; + FoldingState &&operator=(FoldingState &&) = delete; + ~FoldingState() = default; + + FoldingState(Function &F, AliasAnalysis &AA, AssumptionCache &AC, + MemorySSA &MSSA, DominatorTree &DT, const TargetLibraryInfo &TLI, + const LoopInfo &LI, + const std::function &IsSha3Call, + unsigned HeapAS); + + /// Collect all the potential clobbering memory accesses for the + /// given sha3 call (\p Call). + SmallVector collectSHA3Clobbers(const CallInst *Call); + + /// For the given sha3 call (\p Call), walk through the collected MemorySSA + /// definitions (\p MemDefs) and try to build a continuous memory segment with + /// the data for which we compute the keccak256 hash. On success return + /// the computed hash value. + Value *runFolding(const CallInst *Call, + SmallVectorImpl &MemDefs) const; + + /// Try to simplify instructions in the function F. The candidates for + /// simplification may appear after replacing sha3 calls with the + /// calculated hash values. + bool simplifyInstructions(); + + /// Exclude the instruction from MSSA if it's going to be removed from a BB. + void removeFromMSSA(Instruction *Inst) { + if (VerifyMemorySSA) + MSSA.verifyMemorySSA(); + + MSSAUpdater->removeMemoryAccess(Inst, true); + } + + /// Return collected MemorySSa uses corresponding to sha3 calls. + const SmallVectorImpl &getSHA3MemUses() const { + return SHA3MemUses; + } + +private: + /// Return true if a dependency between \p Clobber and \p MemUse is + /// guaranteed to be loop invariant for the loops that they are in. + bool isGuaranteedLoopIndependent(const Instruction *Clobber, + const Instruction *MemUse, + const MemoryLocation &ClobberLoc) const; + + /// Return true if \p Ptr is guaranteed to be loop invariant for any possible + /// loop. In particular, this guarantees that it only references a single + /// MemoryLocation during execution of the containing function. + bool isGuaranteedLoopInvariant(const Value *Ptr) const; + + /// Check how the two memory locations (\p MemUseLoc and \p ClobberLoc) + /// are located with respect to each other. + OverlapResult isOverlap(const Instruction *MemUse, const Instruction *Clobber, + const MemoryLocation &MemUseLoc, + const MemoryLocation &ClobberLoc) const; + + /// Try to cast the constant pointer value \p Ptr to the integer offset. + /// Consider moving it to a common code. TODO: CPR-1380. + std::optional tryToCastPtrToInt(const Value *Ptr) const; + + /// Ensure the sorted array of clobbers forms a continuous memory region. + /// On success return the size the memory region. + std::optional + checkMemoryClobbers(const SmallVector &MemClobbers) const; + + /// Compute keccak256 hash for the memory segment, formed by the sorted list + /// of memory clobbers passed in \p MemClobbers. + std::array + computeKeccak256Hash(const SmallVectorImpl &MemClobbers) const; + + /// Check if we can ignore non store clobber instruction that doesn't actually + /// clobber heap memory. For example, a memcpy to AS other than heap. + /// Probably we should check more cases here. TODO: CPR-1370. + bool shouldSkipClobber(const Instruction *MemInst) const { + if (!MemInst->mayWriteToMemory()) + return true; + + if (const auto *Intr = dyn_cast(MemInst)) + return Intr->getDestAddressSpace() != HeapAS; + + return false; + } + + /// Return MemoryLocation corresponding to the pointer argument of + /// sha3 call. + MemoryLocation getLocForSHA3Call(const CallInst *I) const { + return MemoryLocation::getForArgument(I, 0, TLI); + } +}; // end FoldingState struct + +} // end anonymous namespace + +static uint64_t getPointerSize(const Value *V, const DataLayout &DL, + const TargetLibraryInfo &TLI, + const Function *F) { + uint64_t Size = 0; + ObjectSizeOpts Opts; + Opts.NullIsUnknownSize = NullPointerIsDefined(F); + + if (getObjectSize(V, Size, DL, &TLI, Opts)) + return Size; + return MemoryLocation::UnknownSize; +} + +FoldingState::FoldingState( + Function &F, AliasAnalysis &AA, AssumptionCache &AC, MemorySSA &MSSA, + DominatorTree &DT, const TargetLibraryInfo &TLI, const LoopInfo &LI, + const std::function &IsSha3Call, unsigned HeapAS) + : ContainsIrreducibleLoops(mayContainIrreducibleControl(F, &LI)), F(F), + AA(AA), AC(AC), MSSA(MSSA), + MSSAUpdater(std::make_unique(&MSSA)), TLI(TLI), + DL(F.getParent()->getDataLayout()), SQ(DL, &TLI, &DT, &AC), LI(LI), + HeapAS(HeapAS) { + MSSA.ensureOptimizedUses(); + + const ReversePostOrderTraversal RPOT(&*F.begin()); + for (BasicBlock *BB : RPOT) { + for (Instruction &I : *BB) { + if (!IsSha3Call(&I)) + continue; + + const auto *Call = cast(&I); + auto *MA = MSSA.getMemoryAccess(Call); + auto *MU = dyn_cast_or_null(MA); + // sha3 is passed a pointer in the first argument + // and the memory size in the second one. It's expected that the + // memory size to be a constant expression. In this case + // the memory location should be precise. + if (MU && getLocForSHA3Call(Call).Size.isPrecise()) + SHA3MemUses.push_back(MU); + } + } +} + +SmallVector +FoldingState::collectSHA3Clobbers(const CallInst *Call) { + SmallVector Clobbers; + const MemoryLocation Loc = getLocForSHA3Call(Call); + MemorySSAWalker *Walker = MSSA.getWalker(); + + // For the given sha3 call (which is MemoryUse in terms of MemorySSA) we + // need to collect all memory clobbering accesses. Usually these are just + // 'store' instructions. Other cases are not handled by the current + // implementation. For this we employ MemorySSA representation that maps + // memory clobbering Instructions to three access types: + // - live on entry (nothing to do, sha3 is not clobbered), + // - MemoryDef, + // - MemoryPhi + // + // We start with a nearest to the sha3 call dominating clobbering access. + // Then we do the same for the just found clobber and so on until we find the + // 'live on entry'. + // For simplicity, in case of a MemoryPhi we also stop the search. This + // constraint can be relaxed. TODO: CPR-1370. + + MemoryAccess *MA = + Walker->getClobberingMemoryAccess(MSSA.getMemoryAccess(Call)); + for (; !MSSA.isLiveOnEntryDef(MA) && !isa(MA);) { + if (auto *Def = dyn_cast(MA)) { + Clobbers.push_back(Def); + MA = Walker->getClobberingMemoryAccess(Def->getDefiningAccess(), Loc); + } + } + + return Clobbers; +} + +bool FoldingState::simplifyInstructions() { + bool Changed = false; + for (auto &Inst : make_early_inc_range(instructions(F))) { + if (Value *V = simplifyInstruction(&Inst, SQ)) { + LLVM_DEBUG(dbgs() << " SHA3ConstFolding simplify: " << Inst + << " to: " << *V << '\n'); + if (!DebugCounter::shouldExecute(SHA3Counter)) { + LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); + continue; + } + + if (!Inst.use_empty()) { + Inst.replaceAllUsesWith(V); + Changed = true; + } + if (isInstructionTriviallyDead(&Inst, &TLI)) { + removeFromMSSA(&Inst); + salvageKnowledge(&Inst, &AC); + Inst.eraseFromParent(); + Changed = true; + } + } + } + return Changed; +} + +bool FoldingState::isGuaranteedLoopIndependent( + const Instruction *Clobber, const Instruction *MemUse, + const MemoryLocation &ClobberLoc) const { + // If the dependency is within the same block or loop level (being careful + // of irreducible loops), we know that AA will return a valid result for the + // memory dependency. + if (MemUse->getParent() == Clobber->getParent()) + return true; + + // Check if both Clobber and MemUse are known to be in the same loop level. + const Loop *CurrentLI = LI.getLoopFor(MemUse->getParent()); + if (!ContainsIrreducibleLoops && CurrentLI && + CurrentLI == LI.getLoopFor(Clobber->getParent())) + return true; + + // Otherwise check the memory location is invariant to any loops. + return isGuaranteedLoopInvariant(ClobberLoc.Ptr); +} + +bool FoldingState::isGuaranteedLoopInvariant(const Value *Ptr) const { + Ptr = Ptr->stripPointerCasts(); + if (const auto *GEP = dyn_cast(Ptr)) + if (GEP->hasAllConstantIndices()) + Ptr = GEP->getPointerOperand()->stripPointerCasts(); + + if (const auto *I = dyn_cast(Ptr)) + return I->getParent()->isEntryBlock(); + + return true; +} + +static std::optional getNullOrInt(const APInt &APVal) { + if (APVal.getActiveBits() <= 64) + return APVal.getZExtValue(); + + return std::nullopt; +} + +std::optional +FoldingState::tryToCastPtrToInt(const Value *Ptr) const { + if (isa(Ptr)) + return UINT64_C(0); + + if (const auto *CE = dyn_cast(Ptr)) { + if (CE->getOpcode() == Instruction::IntToPtr) { + if (auto *CI = dyn_cast(CE->getOperand(0))) { + // Give up in case of a huge offsset, as this shouldn't happen + // in a real life. + return getNullOrInt(CI->getValue()); + } + } + } + + if (const auto *IntToPtr = dyn_cast(Ptr)) { + if (auto *CI = dyn_cast(IntToPtr->getOperand(0))) { + return getNullOrInt(CI->getValue()); + } + } + + return std::nullopt; +} + +FoldingState::OverlapResult +FoldingState::isOverlap(const Instruction *MemUse, const Instruction *Clobber, + const MemoryLocation &MemUseLoc, + const MemoryLocation &ClobberLoc) const { + // AliasAnalysis does not always account for loops. Limit overlap checks + // to dependencies for which we can guarantee they are independent of any + // loops they are in. + if (!isGuaranteedLoopIndependent(Clobber, MemUse, ClobberLoc)) + return {OverlapType::OL_Unknown, 0}; + + const Value *ClobberPtr = ClobberLoc.Ptr->stripPointerCasts(); + const Value *MemUsePtr = MemUseLoc.Ptr->stripPointerCasts(); + const Value *ClobberUndObj = getUnderlyingObject(ClobberPtr); + const Value *MemUseUndObj = getUnderlyingObject(MemUsePtr); + const uint64_t MemUseSize = MemUseLoc.Size.getValue(); + const uint64_t ClobberSize = ClobberLoc.Size.getValue(); + + // If both the Clobber and MemUse pointers are constant we expect two cases: + // - pointer is just a nullptr + // - pointer is a cast of an integer constant + + if (isa(ClobberPtr) && isa(MemUsePtr)) { + if (!tryToCastPtrToInt(ClobberPtr) || !tryToCastPtrToInt(MemUsePtr)) + return {OverlapType::OL_Unknown, 0}; + + const uint64_t ClobberPtrInt = *tryToCastPtrToInt(ClobberPtr); + const uint64_t MemUsePtrInt = *tryToCastPtrToInt(MemUsePtr); + if (MemUsePtrInt <= ClobberPtrInt && + (ClobberPtrInt + ClobberSize) <= (MemUsePtrInt + MemUseSize)) { + return {OverlapType::OL_Complete, ClobberPtrInt - MemUsePtrInt}; + } + } + + // Check whether the Clobber overwrites the MemUse object. + if (ClobberUndObj == MemUseUndObj) { + const uint64_t MemUseUndObjSize = getPointerSize(MemUseUndObj, DL, TLI, &F); + if (MemUseUndObjSize != MemoryLocation::UnknownSize && + MemUseUndObjSize == MemUseSize && MemUseSize == ClobberSize) + return {OverlapType::OL_Complete, 0}; + } + + // Query the alias information + const AliasResult AAR = AA.alias(MemUseLoc, ClobberLoc); + + // If the start pointers are the same, we just have to compare sizes to see + // if the MemUse was larger than the Clobber. + if (AAR == AliasResult::MustAlias) { + // Make sure that the MemUseSize size is >= the ClobberSize size. + if (MemUseSize >= ClobberSize) + return {OverlapType::OL_Complete, 0}; + } + + // If we hit a partial alias we may have a full overwrite + if (AAR == AliasResult::PartialAlias && AAR.hasOffset()) { + const int32_t Offset = AAR.getOffset(); + if (Offset >= 0 && + static_cast(Offset) + ClobberSize <= MemUseSize) { + return {OverlapType::OL_Complete, static_cast(Offset)}; + } + } + + // If we can't resolve the same pointers to the same object, then we can't + // analyze them at all. + if (MemUseUndObj != ClobberUndObj) { + if (AAR == AliasResult::NoAlias) + return {OverlapType::OL_None, 0}; + return {OverlapType::OL_Unknown, 0}; + } + + // Okay, we have stores to two completely different pointers. Try to + // decompose the pointer into a "base + constant_offset" form. If the base + // pointers are equal, then we can reason about the MemUse and the Clobber. + int64_t ClobberOffset = 0, MemUseOffset = 0; + const Value *ClobberBasePtr = + GetPointerBaseWithConstantOffset(ClobberPtr, ClobberOffset, DL); + const Value *MemUseBasePtr = + GetPointerBaseWithConstantOffset(MemUsePtr, MemUseOffset, DL); + + // If the base pointers still differ, we have two completely different + // stores. + if (ClobberBasePtr != MemUseBasePtr) + return {OverlapType::OL_Unknown, 0}; + + // The MemUse completely overlaps the clobber if both the start and the end + // of the Clobber are inside the MemUse: + // | |--Clobber-| | + // |------MemUse------| + // We have to be careful here as *Off is signed while *.Size is unsigned. + + // Check if the Clobber access starts not before the MemUse one. + if (ClobberOffset >= MemUseOffset) { + // If the Clobber access ends not after the MemUse access then the + // clobber one is completely overlapped by the MemUse one. + if (static_cast(ClobberOffset - MemUseOffset) + ClobberSize <= + MemUseSize) + return {OverlapType::OL_Complete, + static_cast(ClobberOffset - MemUseOffset)}; + + // If start of the Clobber access is before end of the MemUse access + // then accesses overlap. + if (static_cast(ClobberOffset - MemUseOffset) < MemUseSize) + return {OverlapType::OL_MaybePartial, 0}; + } + // If start of the MemUse access is before end of the Clobber access then + // accesses overlap. + else if (static_cast(MemUseOffset - ClobberOffset) < ClobberSize) { + return {OverlapType::OL_MaybePartial, 0}; + } + + // Can reach here only if accesses are known not to overlap. + return {OverlapType::OL_None, 0}; +} + +std::optional FoldingState::checkMemoryClobbers( + const SmallVector &MemClobbers) const { + auto Begin = MemClobbers.begin(), End = MemClobbers.end(); + assert(Begin != End); + + uint64_t TotalSize = Begin->Size; + for (const auto *It = std::next(Begin); It != End; ++It) { + TotalSize += It->Size; + const auto *ItPrev = std::prev(It); + if ((ItPrev->Start + ItPrev->Size) != It->Start) { + LLVM_DEBUG(dbgs() << "\tclobbers do alias, or there is a gap: [" + << ItPrev->Start << ", " << ItPrev->Start + ItPrev->Size + << "] -> [" << It->Start << ", " << It->Start + It->Size + << "]" << '\n'); + return std::nullopt; + } + } + + return TotalSize; +} + +std::array FoldingState::computeKeccak256Hash( + const SmallVectorImpl &MemClobbers) const { + SmallVector MemBuf; + raw_svector_ostream OS(MemBuf); + // Put all the clobber values to the buffer in the BE order. + for (const auto &MemClobber : MemClobbers) { + // Words of the APInt are in the LE order, so we need to + // iterate them starting from the end. + const auto *ValRawData = MemClobber.Clobber.getRawData(); + for (unsigned I = 0, E = MemClobber.Clobber.getNumWords(); I != E; ++I) + support::endian::write(OS, ValRawData[E - I - 1], + llvm::endianness::big); + } + LLVM_DEBUG(dbgs() << "\tinput sha3 data: " << toHex(OS.str()) << '\n'); + + return KECCAK::KECCAK_256(OS.str()); +} + +Value *FoldingState::runFolding(const CallInst *Call, + SmallVectorImpl &MemDefs) const { + uint64_t TotalSizeOfClobbers = 0; + SmallVector MemClobbers; + auto UseMemLoc = getLocForSHA3Call(Call); + const uint64_t UseMemSize = UseMemLoc.Size.getValue(); + + for (MemoryDef *MemDef : MemDefs) { + const auto *MemInstr = MemDef->getMemoryInst(); + if (shouldSkipClobber(MemInstr)) + continue; + + const auto *SI = dyn_cast(MemInstr); + if (!SI) { + LLVM_DEBUG(dbgs() << "\tunknown clobber: " << *MemInstr << '\n'); + return nullptr; + } + + LLVM_DEBUG(dbgs() << "\tfound clobber: " << *SI << '\n'); + + const MemoryLocation ClobberLoc = MemoryLocation::get(SI); + if (!ClobberLoc.Size.isPrecise()) + return nullptr; + + auto OverlapRes = isOverlap(Call, SI, UseMemLoc, ClobberLoc); + + // This clobber doesn't write to the memory sha3 is reading from. + // Just skip it and come to the next clobber. + if (OverlapRes.Type == OverlapType::OL_None) { + LLVM_DEBUG(dbgs() << "\t\twrites out of sha3 memory, offset: " + << OverlapRes.ClobberOffset << '\n'); + continue; + } + + // We give up in these cases, as it's difficult or impossible + // to determine the full memory data for the sha3. + // If required, we could try to support some cases. TODO: CPR-1370. + if (OverlapRes.Type == OverlapType::OL_MaybePartial || + OverlapRes.Type == OverlapType::OL_Unknown) { + LLVM_DEBUG(dbgs() << "\t\tpartially or unknow overlap" << '\n'); + return nullptr; + } + + // Handle OL_Complete. Try to add new clobber to the memory clobbers. + + // We cannot perform constant folding, if the stored value is not + // a constant expression. + const auto *ClobberVal = dyn_cast(SI->getValueOperand()); + if (!ClobberVal) { + LLVM_DEBUG(dbgs() << "\t\tstored value isn't constant" << '\n'); + return nullptr; + } + + const uint64_t ClobberSize = ClobberLoc.Size.getValue(); + // If we have already seen a clobber with the same start position + // and the size, we can ignore the current clobber, as it's killed + // by existing one. In fact this is similar to what DSE pass does. + if (std::any_of(MemClobbers.begin(), MemClobbers.end(), + [OverlapRes, ClobberSize](const auto &C) { + return C.Start == OverlapRes.ClobberOffset && + C.Size == ClobberSize; + })) { + LLVM_DEBUG(dbgs() << "\t\tstored value is killed by the" + "consequent clobber" + << '\n'); + continue; + } + + TotalSizeOfClobbers += ClobberSize; + assert(ClobberSize * 8 == ClobberVal->getBitWidth()); + + MemClobbers.push_back( + {OverlapRes.ClobberOffset, ClobberSize, ClobberVal->getValue()}); + + // If we have collected clobbers that fully cover the sha3 memory + // location, sort them in the ascending order of their starting addresses + // and perform some checks. + if (TotalSizeOfClobbers < UseMemSize) + continue; + + // Sort memory clobbers in the ascending order of their starting + // positions. + std::sort( + MemClobbers.begin(), MemClobbers.end(), + [](const auto &Lhs, const auto &Rhs) { return Lhs.Start < Rhs.Start; }); + + // Ensure the sorted array of clobbers forms a continuous memory region. + auto TotalSize = checkMemoryClobbers(MemClobbers); + if (!TotalSize) + return nullptr; + + assert(TotalSize == UseMemSize); + auto Hash = computeKeccak256Hash(MemClobbers); + + LLVM_DEBUG(dbgs() << "\tkeccak256 hash: " << toHex(Hash) << '\n'); + assert(Call->getType()->getIntegerBitWidth() == 256); + + Value *HashVal = + ConstantInt::get(Call->getType(), APInt(256, toHex(Hash), 16)); + return HashVal; + } + + LLVM_DEBUG(dbgs() << "\tcouldn't find enough clobbers that would fully" + "cover sha3 memory" + << '\n'); + return nullptr; +} + +bool llvm::runSHA3ConstFolding( + Function &F, AliasAnalysis &AA, AssumptionCache &AC, MemorySSA &MSSA, + DominatorTree &DT, const TargetLibraryInfo &TLI, const LoopInfo &LI, + const std::function &IsSha3Call, + unsigned HeapAS) { + LLVM_DEBUG(dbgs() << "********** SHA3 constant folding **********\n" + << "********** Function: " << F.getName() << '\n'); + + FoldingState State(F, AA, AC, MSSA, DT, TLI, LI, IsSha3Call, HeapAS); + SmallSet RemovedSHA3; + + bool Changed = false, ChangedOnIter = false; + do { + LLVM_DEBUG(dbgs() << "Running new iteration of sha3 constant folding...\n"); + + for (MemoryUse *SHA3MemUse : State.getSHA3MemUses()) { + if (RemovedSHA3.count(SHA3MemUse)) + continue; + + auto *SHA3Call = dyn_cast(SHA3MemUse->getMemoryInst()); + assert(SHA3Call != nullptr); + + LLVM_DEBUG(dbgs() << "Analyzing: " << *SHA3Call << '\n'); + SmallVector Clobbers = + State.collectSHA3Clobbers(SHA3Call); + + if (Value *HashVal = State.runFolding(SHA3Call, Clobbers)) { + if (!DebugCounter::shouldExecute(SHA3Counter)) { + LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); + return Changed; + } + + SHA3Call->replaceAllUsesWith(HashVal); + State.removeFromMSSA(SHA3Call); + SHA3Call->eraseFromParent(); + RemovedSHA3.insert(SHA3MemUse); + Changed = ChangedOnIter = true; + NumSHA3Folded++; + + LLVM_DEBUG(dbgs() << "\treplacing with the value: " << *HashVal + << '\n'); + } + } + // If we simplified some instructions after folding sha3 calls, + // run the folding again, as there may be new opportunities for this. + } while (ChangedOnIter && State.simplifyInstructions()); + + return Changed; +} From 7e3955abeec62d3d00658e46a13c76c03b6b3b74 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 8 Nov 2024 12:47:22 +0100 Subject: [PATCH 099/203] [EVM] Add pre-commit test for Add support for constant folding SHA3 calls Signed-off-by: Vladimir Radosavljevic --- .../test/CodeGen/EVM/sha3-constant-folding.ll | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/sha3-constant-folding.ll diff --git a/llvm/test/CodeGen/EVM/sha3-constant-folding.ll b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll new file mode 100644 index 000000000000..567d4176ae11 --- /dev/null +++ b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll @@ -0,0 +1,240 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -O3 -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Check that we don't fold the sha3 call if optimizing for size. +define i256 @sha3_test_optsize() minsize { +; CHECK-LABEL: define i256 @sha3_test_optsize +; CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) null, align 4294967296 + store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) + ret i256 %hash +} + + +; Both the store instructions and the sha3 call has constexpr addresses. +define i256 @sha3_test_1() nounwind { +; CHECK-LABEL: define i256 @sha3_test_1 +; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) null, align 4294967296 + store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) + ret i256 %hash +} + +; Both the store instructions and the sha3 call has runtime addresses. +define i256 @sha3_test_2(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_2 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; Store instructions don't cover sha3 memory location, so no constant folding. +define i256 @sha3_test_3(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_3 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 96 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR2]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 96) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 304594385234, ptr addrspace(1) %next_addr, align 1 + %next_addr2 = getelementptr i256, ptr addrspace(1) %addr, i256 3 + store i256 56457598675863654, ptr addrspace(1) %next_addr2, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 96) + ret i256 %hash +} + +; The second store partially overlaps sha3 memory location, +; so no constant folding. +define i256 @sha3_test_4(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_4 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i512 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i512 304594385234, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; Store instructions have different store sizes. +define i256 @sha3_test_5(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_5 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i128 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 16 +; CHECK-NEXT: store i128 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR2]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i128 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i8, ptr addrspace(1) %addr, i256 16 + store i128 304594385234, ptr addrspace(1) %next_addr, align 1 + %next_addr2 = getelementptr i8, ptr addrspace(1) %addr, i256 32 + store i256 56457598675863654, ptr addrspace(1) %next_addr2, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; Only the first store is used for the constant folding. +define i256 @sha3_test_6(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_6 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 32) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 32) + ret i256 %hash +} + +; The second sha3 call gets folded, but not the first one because there is +; non-analyzable clobber. +define i256 @sha3_test_7(ptr addrspace(1) nocapture %addr, ptr addrspace(1) nocapture %addr2) nounwind { +; CHECK-LABEL: define i256 @sha3_test_7 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]], ptr addrspace(1) nocapture writeonly [[ADDR2:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 111, ptr addrspace(1) [[ADDR2]], align 1 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: [[HASH2:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[NEXT_ADDR]], i256 32) +; CHECK-NEXT: [[HASH:%.*]] = add i256 [[HASH2]], [[HASH1]] +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 111, ptr addrspace(1) %addr2, align 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash1 = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + %hash2 = call i256 @llvm.evm.sha3(ptr addrspace(1) %next_addr, i256 32) + %hash = add i256 %hash1, %hash2 + ret i256 %hash +} + +; Memory locations of store instructions do alias with each other, so no +; constant folding. Theoretically we can support this case. TODO: CPR-1370. +define i256 @sha3_test_8(ptr addrspace(1) nocapture %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_8 +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 31 +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 63 +; CHECK-NEXT: store i8 17, ptr addrspace(1) [[NEXT_ADDR2]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 0, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i8, ptr addrspace(1) %addr, i256 31 + store i256 304594385234, ptr addrspace(1) %next_addr, align 1 + %next_addr2 = getelementptr i8, ptr addrspace(1) %addr, i256 63 + store i8 17, ptr addrspace(1) %next_addr2, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash +} + +; We have two sha3 calls where the second call gets folded on the second iteration. +define i256 @sha3_test_9(ptr addrspace(1) %addr) nounwind { +; CHECK-LABEL: define i256 @sha3_test_9 +; CHECK-SAME: (ptr addrspace(1) [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: [[SUM:%.*]] = add i256 [[HASH]], 10 +; CHECK-NEXT: store i256 [[SUM]], ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: store i256 111111111111, ptr addrspace(1) [[ADDR]], align 1 +; CHECK-NEXT: [[HASH2:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) +; CHECK-NEXT: ret i256 [[HASH2]] +; +entry: + store i256 304594385234, ptr addrspace(1) %addr, align 1 + %next_addr = getelementptr i256, ptr addrspace(1) %addr, i256 1 + store i256 56457598675863654, ptr addrspace(1) %next_addr, align 1 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + %sum = add i256 %hash, 10 + store i256 %sum, ptr addrspace(1) %next_addr, align 1 + store i256 111111111111, ptr addrspace(1) %addr, align 1 + %hash2 = call i256 @llvm.evm.sha3(ptr addrspace(1) %addr, i256 64) + ret i256 %hash2 +} + +; Offset of the second store is too big (requires > 64 bits), so no constant folding. +define i256 @sha3_test_10() nounwind { +; CHECK-LABEL: define i256 @sha3_test_10 +; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 18446744073709551616 to ptr addrspace(1)), align 4294967296 +; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) +; CHECK-NEXT: ret i256 [[HASH]] +; +entry: + store i256 304594385234, ptr addrspace(1) null, align 4294967296 + store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 18446744073709551616 to ptr addrspace(1)), align 32 + %hash = call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) + ret i256 %hash +} + +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) From e2556220d48850b86172e4fe5f59af8efe1f9fa2 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 6 Nov 2024 16:03:52 +0100 Subject: [PATCH 100/203] [EVM] Add support for constant folding SHA3 calls Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Analysis/MemoryLocation.cpp | 12 +++++ llvm/lib/Target/EVM/CMakeLists.txt | 1 + llvm/lib/Target/EVM/EVM.h | 5 ++ llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp | 48 +++++++++++++++++ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 12 +++++ .../test/CodeGen/EVM/sha3-constant-folding.ll | 51 ++++++++----------- 6 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp diff --git a/llvm/lib/Analysis/MemoryLocation.cpp b/llvm/lib/Analysis/MemoryLocation.cpp index 72dd5b98ac13..99cbd02a2365 100644 --- a/llvm/lib/Analysis/MemoryLocation.cpp +++ b/llvm/lib/Analysis/MemoryLocation.cpp @@ -14,6 +14,10 @@ #include "llvm/IR/IntrinsicsARM.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/TargetParser/Triple.h" +// EVM local end #include using namespace llvm; @@ -187,6 +191,14 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, case Intrinsic::lifetime_end: // it is okay to have lifetime intrinsic break; + case Intrinsic::evm_sha3: { + assert((ArgIdx == 0) && "Invalid argument index for sha3"); + const auto *LenCI = dyn_cast(Call->getArgOperand(1)); + if (LenCI && LenCI->getValue().getActiveBits() <= 64) + return MemoryLocation( + Arg, LocationSize::precise(LenCI->getZExtValue()), AATags); + return MemoryLocation::getAfter(Arg, AATags); + } default: llvm_unreachable("Unexpected intrinsic for EVM target"); break; diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 5a1d09479bb0..70db127e45cc 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -53,6 +53,7 @@ add_llvm_target(EVMCodeGen EVMOptimizeLiveIntervals.cpp EVMRegColoring.cpp EVMRegisterInfo.cpp + EVMSHA3ConstFolding.cpp EVMSingleUseExpression.cpp EVMSplitCriticalEdges.cpp EVMStackSolver.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 95e5160c410b..6cc2073988d9 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -91,5 +91,10 @@ struct EVMAllocaHoistingPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; +struct EVMSHA3ConstFoldingPass : PassInfoMixin { + EVMSHA3ConstFoldingPass() = default; + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + } // namespace llvm #endif // LLVM_LIB_TARGET_EVM_EVM_H diff --git a/llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp b/llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp new file mode 100644 index 000000000000..062e527b5d57 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMSHA3ConstFolding.cpp @@ -0,0 +1,48 @@ +//===-- EVMSHA3ConstFolding.cpp - Const fold calls to sha3 ------*- 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 is the EVM SHA3 const folding pass. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/Transforms/Scalar/SHA3ConstFolding.h" + +using namespace llvm; + +PreservedAnalyses EVMSHA3ConstFoldingPass::run(Function &F, + FunctionAnalysisManager &AM) { + // Don't run this pass if optimizing for size, since result of SHA3 + // calls will be replaced with a 32-byte constant, thus PUSH32 will + // be emitted. This will increase code size. + if (F.hasOptSize()) + return PreservedAnalyses::all(); + + auto &AC = AM.getResult(F); + auto &AA = AM.getResult(F); + const auto &TLI = AM.getResult(F); + auto &DT = AM.getResult(F); + auto &MSSA = AM.getResult(F).getMSSA(); + auto &LI = AM.getResult(F); + auto IsSha3Call = [](const Instruction *I) { + const auto *II = dyn_cast(I); + return II && II->getIntrinsicID() == Intrinsic::evm_sha3; + }; + + return llvm::runSHA3ConstFolding(F, AA, AC, MSSA, DT, TLI, LI, IsSha3Call, + EVMAS::AS_HEAP) + ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index e8de2fd59c46..bc49cf7b79be 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -131,12 +131,24 @@ void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { [](FunctionPassManager &PM, OptimizationLevel Level) { if (Level.getSizeLevel() || Level.getSpeedupLevel() > 1) PM.addPass(MergeIdenticalBBPass()); + if (Level.isOptimizingForSpeed()) + PM.addPass(EVMSHA3ConstFoldingPass()); }); PB.registerAnalysisRegistrationCallback([](FunctionAnalysisManager &FAM) { FAM.registerPass([] { return EVMAA(); }); }); + PB.registerPipelineParsingCallback( + [](StringRef PassName, FunctionPassManager &PM, + ArrayRef) { + if (PassName == "evm-sha3-constant-folding") { + PM.addPass(EVMSHA3ConstFoldingPass()); + return true; + } + return false; + }); + PB.registerParseAACallback([](StringRef AAName, AAManager &AAM) { if (AAName == "evm-aa") { AAM.registerFunctionAnalysis(); diff --git a/llvm/test/CodeGen/EVM/sha3-constant-folding.ll b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll index 567d4176ae11..6662883763a9 100644 --- a/llvm/test/CodeGen/EVM/sha3-constant-folding.ll +++ b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll @@ -24,13 +24,12 @@ entry: ; Both the store instructions and the sha3 call has constexpr addresses. define i256 @sha3_test_1() nounwind { -; CHECK-LABEL: define i256 @sha3_test_1 +; CHECK-LABEL: define noundef i256 @sha3_test_1 ; CHECK-SAME: () local_unnamed_addr #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 ; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 32 to ptr addrspace(1)), align 32 -; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) null, i256 64) -; CHECK-NEXT: ret i256 [[HASH]] +; CHECK-NEXT: ret i256 -53675409633959416604748946233496653964072736789863655143901645101595015023086 ; entry: store i256 304594385234, ptr addrspace(1) null, align 4294967296 @@ -41,14 +40,13 @@ entry: ; Both the store instructions and the sha3 call has runtime addresses. define i256 @sha3_test_2(ptr addrspace(1) nocapture %addr) nounwind { -; CHECK-LABEL: define i256 @sha3_test_2 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-LABEL: define noundef i256 @sha3_test_2 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 ; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 -; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) -; CHECK-NEXT: ret i256 [[HASH]] +; CHECK-NEXT: ret i256 -53675409633959416604748946233496653964072736789863655143901645101595015023086 ; entry: store i256 304594385234, ptr addrspace(1) %addr, align 1 @@ -61,7 +59,7 @@ entry: ; Store instructions don't cover sha3 memory location, so no constant folding. define i256 @sha3_test_3(ptr addrspace(1) nocapture %addr) nounwind { ; CHECK-LABEL: define i256 @sha3_test_3 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 @@ -85,7 +83,7 @@ entry: ; so no constant folding. define i256 @sha3_test_4(ptr addrspace(1) nocapture %addr) nounwind { ; CHECK-LABEL: define i256 @sha3_test_4 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 @@ -103,16 +101,15 @@ entry: ; Store instructions have different store sizes. define i256 @sha3_test_5(ptr addrspace(1) nocapture %addr) nounwind { -; CHECK-LABEL: define i256 @sha3_test_5 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define noundef i256 @sha3_test_5 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i128 0, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 16 ; CHECK-NEXT: store i128 304594385234, ptr addrspace(1) [[NEXT_ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR2:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 ; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR2]], align 1 -; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) -; CHECK-NEXT: ret i256 [[HASH]] +; CHECK-NEXT: ret i256 -53675409633959416604748946233496653964072736789863655143901645101595015023086 ; entry: store i128 0, ptr addrspace(1) %addr, align 1 @@ -126,14 +123,13 @@ entry: ; Only the first store is used for the constant folding. define i256 @sha3_test_6(ptr addrspace(1) nocapture %addr) nounwind { -; CHECK-LABEL: define i256 @sha3_test_6 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define noundef i256 @sha3_test_6 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 ; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 -; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 32) -; CHECK-NEXT: ret i256 [[HASH]] +; CHECK-NEXT: ret i256 -1651279235167815098054286291856006982035426946965232889084721396369881222887 ; entry: store i256 304594385234, ptr addrspace(1) %addr, align 1 @@ -147,15 +143,14 @@ entry: ; non-analyzable clobber. define i256 @sha3_test_7(ptr addrspace(1) nocapture %addr, ptr addrspace(1) nocapture %addr2) nounwind { ; CHECK-LABEL: define i256 @sha3_test_7 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]], ptr addrspace(1) nocapture writeonly [[ADDR2:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]], ptr addrspace(1) nocapture writeonly [[ADDR2:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 ; CHECK-NEXT: store i256 111, ptr addrspace(1) [[ADDR2]], align 1 ; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 ; CHECK-NEXT: [[HASH1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) -; CHECK-NEXT: [[HASH2:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[NEXT_ADDR]], i256 32) -; CHECK-NEXT: [[HASH:%.*]] = add i256 [[HASH2]], [[HASH1]] +; CHECK-NEXT: [[HASH:%.*]] = add i256 [[HASH1]], 28454950007360609575222453380260700122861180288886985272557645317297017637223 ; CHECK-NEXT: ret i256 [[HASH]] ; entry: @@ -173,7 +168,7 @@ entry: ; constant folding. Theoretically we can support this case. TODO: CPR-1370. define i256 @sha3_test_8(ptr addrspace(1) nocapture %addr) nounwind { ; CHECK-LABEL: define i256 @sha3_test_8 -; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-SAME: (ptr addrspace(1) nocapture [[ADDR:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 0, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 31 @@ -195,18 +190,14 @@ entry: ; We have two sha3 calls where the second call gets folded on the second iteration. define i256 @sha3_test_9(ptr addrspace(1) %addr) nounwind { -; CHECK-LABEL: define i256 @sha3_test_9 -; CHECK-SAME: (ptr addrspace(1) [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { +; CHECK-LABEL: define noundef i256 @sha3_test_9 +; CHECK-SAME: (ptr addrspace(1) nocapture writeonly [[ADDR:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) [[ADDR]], align 1 ; CHECK-NEXT: [[NEXT_ADDR:%.*]] = getelementptr i8, ptr addrspace(1) [[ADDR]], i256 32 -; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) [[NEXT_ADDR]], align 1 -; CHECK-NEXT: [[HASH:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) -; CHECK-NEXT: [[SUM:%.*]] = add i256 [[HASH]], 10 -; CHECK-NEXT: store i256 [[SUM]], ptr addrspace(1) [[NEXT_ADDR]], align 1 +; CHECK-NEXT: store i256 -53675409633959416604748946233496653964072736789863655143901645101595015023076, ptr addrspace(1) [[NEXT_ADDR]], align 1 ; CHECK-NEXT: store i256 111111111111, ptr addrspace(1) [[ADDR]], align 1 -; CHECK-NEXT: [[HASH2:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[ADDR]], i256 64) -; CHECK-NEXT: ret i256 [[HASH2]] +; CHECK-NEXT: ret i256 -28502626979061174856046376292559402895813043346926066817140289137910599757723 ; entry: store i256 304594385234, ptr addrspace(1) %addr, align 1 @@ -223,7 +214,7 @@ entry: ; Offset of the second store is too big (requires > 64 bits), so no constant folding. define i256 @sha3_test_10() nounwind { ; CHECK-LABEL: define i256 @sha3_test_10 -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i256 304594385234, ptr addrspace(1) null, align 4294967296 ; CHECK-NEXT: store i256 56457598675863654, ptr addrspace(1) inttoptr (i256 18446744073709551616 to ptr addrspace(1)), align 4294967296 From b3a5d80fb9f7fc7ede7203b3fee483987be40ff5 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 6 Nov 2024 16:08:47 +0100 Subject: [PATCH 101/203] [EVM] Remove assert in EVMCodegenPrepare Currently, it is only allowed to have memmove where src and dst are from address space 1 (HEAP). Since MemCpyOptPass can change memmove to memcpy, allow this case, and don't issue an error. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMCodegenPrepare.cpp | 6 ------ llvm/test/CodeGen/EVM/memintrinsics.ll | 9 +++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp b/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp index 9a0f636f1f5c..4923d58e231c 100644 --- a/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp +++ b/llvm/lib/Target/EVM/EVMCodegenPrepare.cpp @@ -81,12 +81,6 @@ void EVMCodegenPrepare::processMemTransfer(MemTransferInst *M) { } } - assert((SrcAS == EVMAS::AS_HEAP && isa(M)) || - ((SrcAS == EVMAS::AS_CALL_DATA || SrcAS == EVMAS::AS_RETURN_DATA || - SrcAS == EVMAS::AS_CODE) && - isa(M) || - isa(M))); - Intrinsic::ID IntrID = Intrinsic::not_intrinsic; switch (SrcAS) { default: diff --git a/llvm/test/CodeGen/EVM/memintrinsics.ll b/llvm/test/CodeGen/EVM/memintrinsics.ll index f224ae74343c..af74658b90b2 100644 --- a/llvm/test/CodeGen/EVM/memintrinsics.ll +++ b/llvm/test/CodeGen/EVM/memintrinsics.ll @@ -3,6 +3,7 @@ target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" +declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(1) noalias nocapture readonly, i256, i1 immarg) declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) @@ -69,6 +70,14 @@ define fastcc void @normal-known-size-2(ptr addrspace(1) %dest, ptr addrspace(1) ret void } +define fastcc void @heap_to_heap(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 %len) { +; CHECK-LABEL: heap_to_heap +; CHECK: MCOPY + + call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 %len, i1 false) + ret void +} + define fastcc void @calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { ; CHECK-LABEL: calldata_to_heap ; CHECK: CALLDATACOPY From e997f3e800d842883bf610815804a804ecedd94a Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 17 Jan 2025 15:22:27 +0100 Subject: [PATCH 102/203] [EVM] Add pre-commit test for Don't avoid transformations to shift Signed-off-by: Vladimir Radosavljevic --- .../EVM/dont-avoid-shift-transformations.ll | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll diff --git a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll new file mode 100644 index 000000000000..fd617c43594b --- /dev/null +++ b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll @@ -0,0 +1,235 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Check the following conversion in TargetLowering::SimplifySetCC +; (X & 8) != 0 --> (X & 8) >> 3 +define i256 @test1(i256 %x) { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: AND +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %and = and i256 %x, 32 + %cmp = icmp ne i256 %and, 0 + %conv = zext i1 %cmp to i256 + ret i256 %conv +} + +; Check the following conversion in TargetLowering::SimplifySetCC +; (X & 8) == 8 --> (X & 8) >> 3 +define i256 @test2(i256 %x) { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: AND +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %and = and i256 %x, 32 + %cmp = icmp eq i256 %and, 32 + %conv = zext i1 %cmp to i256 + ret i256 %conv +} + +; Check the following conversion in DAGCombiner::SimplifySelectCC +; (select_cc seteq (and x, y), 0, 0, A) -> (and (shr (shl x)) A) +define i256 @test3(i256 %x, i256 %a) { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH2 2048 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB2_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB2_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB2_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB2_1: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB2_2: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %and = and i256 %x, 2048 + %cmp = icmp eq i256 %and, 0 + %cond = select i1 %cmp, i256 0, i256 %a + ret i256 %cond +} + +; Check the following conversion in DAGCombiner foldExtendedSignBitTest +; sext i1 (setgt iN X, -1) --> sra (not X), (N - 1) +define i256 @test4(i256 %x) { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB3_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB3_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB3_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB3_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB3_1: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB3_2: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %cmp = icmp sgt i256 %x, -1 + %cond = sext i1 %cmp to i256 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner foldExtendedSignBitTest +; zext i1 (setgt iN X, -1) --> srl (not X), (N - 1) +define i256 @test5(i256 %x) { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SGT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +entry: + %cmp = icmp sgt i256 %x, -1 + %cond = zext i1 %cmp to i256 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner::foldSelectCCToShiftAnd +; select_cc setlt X, 0, A, 0 -> and (sra X, size(X)-1), A +define i256 @test6(i256 %x, i256 %a) { +; CHECK-LABEL: test6: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB5_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB5_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB5_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB5_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB5_1: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB5_2: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %cmp = icmp slt i256 %x, 0 + %cond = select i1 %cmp, i256 %a, i256 0 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner::foldSelectCCToShiftAnd +; select_cc setlt X, 0, A, 0 -> "and (srl X, C2), A" iff A is a single-bit +define i256 @test7(i256 %x) { +; CHECK-LABEL: test7: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB6_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB6_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB6_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB6_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB6_1: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB6_2: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %cmp = icmp slt i256 %x, 0 + %cond = select i1 %cmp, i256 2, i256 0 + ret i256 %cond +} + +; Check the following conversion in DAGCombiner::SimplifySelectCC +; select C, 16, 0 -> shl C, 4 +define i256 @test8(i256 %a, i256 %b) { +; CHECK-LABEL: test8: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB7_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB7_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB7_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB7_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB7_1: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB7_2: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %cmp = icmp sgt i256 %a, %b + %cond = select i1 %cmp, i256 32, i256 0 + ret i256 %cond +} From f4c3df007b0ad3fa01527d941ce35e9fdf898999 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 17 Jan 2025 15:20:04 +0100 Subject: [PATCH 103/203] [EVM] Don't avoid transformations to shift For EVM, transformations to shift are preferable. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMISelLowering.h | 6 - .../EVM/dont-avoid-shift-transformations.ll | 129 +++--------------- 2 files changed, 22 insertions(+), 113 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index c47b593917d2..6367ced2a1a6 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -72,12 +72,6 @@ class EVMTargetLowering final : public TargetLowering { return true; } - /// Return true if creating a shift of the type by the given - /// amount is not profitable. - bool shouldAvoidTransformToShift(EVT VT, unsigned Amount) const override { - return true; - } - /// Return true if it is profitable to fold a pair of shifts into a mask. /// This is usually true on most targets. But some targets, like Thumb1, /// have immediate shift instructions, but no immediate "and" instruction; diff --git a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll index fd617c43594b..aa0514a49970 100644 --- a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll +++ b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll @@ -10,12 +10,10 @@ define i256 @test1(i256 %x) { ; CHECK-LABEL: test1: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 32 ; CHECK-NEXT: AND -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP entry: @@ -31,12 +29,10 @@ define i256 @test2(i256 %x) { ; CHECK-LABEL: test2: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 32 ; CHECK-NEXT: AND -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP entry: @@ -52,28 +48,12 @@ define i256 @test3(i256 %x, i256 %a) { ; CHECK-LABEL: test3: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH2 2048 +; CHECK-NEXT: PUSH1 244 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: SAR ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB2_3 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: PUSH4 @.BB2_1 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB2_3: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH4 @.BB2_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB2_1: ; %entry -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB2_2: ; %entry -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %and = and i256 %x, 2048 @@ -88,28 +68,10 @@ define i256 @test4(i256 %x) { ; CHECK-LABEL: test4: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SUB -; CHECK-NEXT: DUP1 -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SGT -; CHECK-NEXT: PUSH4 @.BB3_3 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: PUSH4 @.BB3_1 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB3_3: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB3_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB3_1: ; %entry -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB3_2: ; %entry -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %cmp = icmp sgt i256 %x, -1 @@ -123,11 +85,9 @@ define i256 @test5(i256 %x) { ; CHECK-LABEL: test5: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SUB -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: SGT +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP entry: @@ -142,25 +102,10 @@ define i256 @test6(i256 %x, i256 %a) { ; CHECK-LABEL: test6: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: SLT -; CHECK-NEXT: PUSH4 @.BB5_3 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: PUSH4 @.BB5_1 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB5_3: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB5_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB5_1: ; %entry -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: SAR +; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB5_2: ; %entry -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %cmp = icmp slt i256 %x, 0 @@ -174,26 +119,12 @@ define i256 @test7(i256 %x) { ; CHECK-LABEL: test7: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 ; CHECK-NEXT: PUSH1 2 -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SLT -; CHECK-NEXT: PUSH4 @.BB6_3 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: PUSH4 @.BB6_1 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB6_3: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB6_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB6_1: ; %entry -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH1 254 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB6_2: ; %entry -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %cmp = icmp slt i256 %x, 0 @@ -207,26 +138,10 @@ define i256 @test8(i256 %a, i256 %b) { ; CHECK-LABEL: test8: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 32 -; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SGT -; CHECK-NEXT: PUSH4 @.BB7_3 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: PUSH4 @.BB7_1 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB7_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB7_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB7_1: ; %entry -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB7_2: ; %entry -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %cmp = icmp sgt i256 %a, %b From 3f1fb75ef8e3ea459250133109e2a3d582928782 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 17 Jan 2025 15:36:13 +0100 Subject: [PATCH 104/203] [EVM] Add pre-commit test for Set that jumps are expensive Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/jumps-are-expensive.ll | 59 ++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/jumps-are-expensive.ll diff --git a/llvm/test/CodeGen/EVM/jumps-are-expensive.ll b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll new file mode 100644 index 000000000000..50d71d9a1291 --- /dev/null +++ b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll @@ -0,0 +1,59 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @bar() + +define i256 @test(i256 %a, i256 %b) { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_4 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB0_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_4: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_1: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.5: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_2: ; %bb3 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_3: ; %bb4 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH4 @bar +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +bb1: + %0 = icmp eq i256 %a, 0 + %1 = icmp eq i256 %b, 0 + %or = or i1 %0, %1 + br i1 %or, label %bb3, label %bb4 + +bb3: + ret i256 0 + +bb4: + %2 = call i256 @bar() + ret i256 %2 +} From 7a606df4acc58491ca3091207b2278e7991b9ad5 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 17 Jan 2025 15:30:11 +0100 Subject: [PATCH 105/203] [EVM] Set that jumps are expensive Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMISelLowering.cpp | 2 +- llvm/test/CodeGen/EVM/jumps-are-expensive.ll | 24 ++++++-------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index d7e02f653c2a..eeba3ee799bd 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -86,7 +86,7 @@ EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); - setJumpIsExpensive(false); + setJumpIsExpensive(true); setMaximumJumpTableSize(0); } diff --git a/llvm/test/CodeGen/EVM/jumps-are-expensive.ll b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll index 50d71d9a1291..b873258b3af5 100644 --- a/llvm/test/CodeGen/EVM/jumps-are-expensive.ll +++ b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll @@ -10,32 +10,22 @@ define i256 @test(i256 %a, i256 %b) { ; CHECK-LABEL: test: ; CHECK: ; %bb.0: ; %bb1 ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB0_4 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: PUSH4 @.BB0_1 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_4: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB0_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_1: ; %bb1 -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: EQ ; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.5: +; CHECK-NEXT: ; %bb.1: ; %bb3 ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB0_2: ; %bb3 -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_3: ; %bb4 +; CHECK-NEXT: .BB0_2: ; %bb4 ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH4 @.FUNC_RET0 ; CHECK-NEXT: PUSH4 @bar From 77a8a0765e75066252d6255761eb2ee4053eccb6 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 17 Jan 2025 16:21:15 +0100 Subject: [PATCH 106/203] [EVM] Add pre-commit test for Allow rematerialization of some of the context instructions Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/context-remat.ll | 293 +++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/context-remat.ll diff --git a/llvm/test/CodeGen/EVM/context-remat.ll b/llvm/test/CodeGen/EVM/context-remat.ll new file mode 100644 index 000000000000..ad9406674385 --- /dev/null +++ b/llvm/test/CodeGen/EVM/context-remat.ll @@ -0,0 +1,293 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @use(i256) + +define i256 @test_address() { +; CHECK-LABEL: test_address: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.address() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_origin() { +; CHECK-LABEL: test_origin: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ORIGIN +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.origin() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_caller() { +; CHECK-LABEL: test_caller: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET2 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.caller() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_callvalue() { +; CHECK-LABEL: test_callvalue: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLVALUE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.callvalue() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_calldatasize() { +; CHECK-LABEL: test_calldatasize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATASIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET4 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET4: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.calldatasize() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_codesize() { +; CHECK-LABEL: test_codesize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODESIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET5 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.codesize() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_gasprice() { +; CHECK-LABEL: test_gasprice: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASPRICE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET6 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET6: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.gasprice() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_coinbase() { +; CHECK-LABEL: test_coinbase: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: COINBASE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET7 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET7: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.coinbase() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_timestamp() { +; CHECK-LABEL: test_timestamp: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TIMESTAMP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET8 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET8: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.timestamp() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_number() { +; CHECK-LABEL: test_number: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NUMBER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET9 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET9: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.number() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_difficulty() { +; CHECK-LABEL: test_difficulty: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIFFICULTY +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET10 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET10: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.difficulty() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_gaslimit() { +; CHECK-LABEL: test_gaslimit: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASLIMIT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET11 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET11: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.gaslimit() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_chainid() { +; CHECK-LABEL: test_chainid: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CHAINID +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET12 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET12: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.chainid() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_basefee() { +; CHECK-LABEL: test_basefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET13 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET13: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.basefee() + call void @use(i256 %ret) + ret i256 %ret +} + +define i256 @test_blobbasefee() { +; CHECK-LABEL: test_blobbasefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBBASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET14 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH4 @use +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET14: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %ret = call i256 @llvm.evm.blobbasefee() + call void @use(i256 %ret) + ret i256 %ret +} + +declare i256 @llvm.evm.address() +declare i256 @llvm.evm.origin() +declare i256 @llvm.evm.caller() +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.calldatasize() +declare i256 @llvm.evm.codesize() +declare i256 @llvm.evm.gasprice() +declare i256 @llvm.evm.coinbase() +declare i256 @llvm.evm.timestamp() +declare i256 @llvm.evm.number() +declare i256 @llvm.evm.difficulty() +declare i256 @llvm.evm.gaslimit() +declare i256 @llvm.evm.chainid() +declare i256 @llvm.evm.basefee() +declare i256 @llvm.evm.blobbasefee() From 4cef05f786267064443b431b0b088e8d5b2de531 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 17 Jan 2025 15:49:30 +0100 Subject: [PATCH 107/203] [EVM] Allow rematerialization of some of the context instructions Since these instructions are cheaper than move, it is beneficial to rematerialize them. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMInstrInfo.cpp | 15 ++++ llvm/lib/Target/EVM/EVMInstrInfo.td | 101 +++++++++++++------------ llvm/test/CodeGen/EVM/context-remat.ll | 90 +++++++++++----------- 3 files changed, 111 insertions(+), 95 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index 47f3d50293d6..c78102a7c02f 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -26,6 +26,21 @@ EVMInstrInfo::EVMInstrInfo() bool EVMInstrInfo::isReallyTriviallyReMaterializable( const MachineInstr &MI) const { switch (MI.getOpcode()) { + case EVM::ADDRESS: + case EVM::ORIGIN: + case EVM::CALLER: + case EVM::CALLVALUE: + case EVM::CALLDATASIZE: + case EVM::CODESIZE: + case EVM::GASPRICE: + case EVM::COINBASE: + case EVM::TIMESTAMP: + case EVM::NUMBER: + case EVM::DIFFICULTY: + case EVM::GASLIMIT: + case EVM::CHAINID: + case EVM::BASEFEE: + case EVM::BLOBBASEFEE: case EVM::CONST_I256: return true; default: diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index ecab375fe435..20b7f18cd24e 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -532,15 +532,11 @@ def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, 0, GPR:$val) // EVM instructions for retrieval values from context. //===----------------------------------------------------------------------===// +let isAsCheapAsAMove = 1, isReMaterializable = 1 in { defm ADDRESS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_address))], "ADDRESS", " $dst", 0x30, 2>; -defm BALANCE - : I<(outs GPR:$dst), (ins GPR:$addr), - [(set GPR:$dst, (int_evm_balance GPR:$addr))], - "BALANCE", " $dst, $addr", 0x31, 100>; - defm ORIGIN : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], "ORIGIN", " $dst", 0x32, 2>; @@ -553,6 +549,56 @@ defm CALLVALUE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_callvalue))], "CALLVALUE", " $dst", 0x34, 2>; +defm CALLDATASIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_calldatasize))], + "CALLDATASIZE", " $dst", 0x36, 2>; + +defm CODESIZE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], + "CODESIZE", " $dst", 0x38, 2>; + +defm GASPRICE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], + "GASPRICE", " $dst", 0x3A, 2>; + +defm COINBASE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], + "COINBASE", " $dst", 0x41, 2>; + +defm TIMESTAMP + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], + "TIMESTAMP", " $dst", 0x42, 2>; + +defm NUMBER + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], + "NUMBER", " $dst", 0x43, 2>; + +defm DIFFICULTY + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], + "DIFFICULTY", " $dst", 0x44, 2>; + +defm GASLIMIT + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], + "GASLIMIT", " $dst", 0x45, 2>; + +defm CHAINID + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], + "CHAINID", " $dst", 0x46, 2>; + +defm BASEFEE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], + "BASEFEE", " $dst", 0x48, 2>; + +defm BLOBBASEFEE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], + "BLOBBASEFEE", " $dst", 0x4A, 2>; +} + +defm BALANCE + : I<(outs GPR:$dst), (ins GPR:$addr), + [(set GPR:$dst, (int_evm_balance GPR:$addr))], + "BALANCE", " $dst, $addr", 0x31, 100>; + let mayLoad = 1 in defm CALLDATALOAD : I<(outs GPR:$dst), (ins GPR:$off), @@ -561,10 +607,6 @@ defm CALLDATALOAD def : Pat<(load_call_data GPR:$off), (CALLDATALOAD GPR:$off)>; -defm CALLDATASIZE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_calldatasize))], - "CALLDATASIZE", " $dst", 0x36, 2>; - let mayStore = 1 in defm CALLDATACOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], @@ -573,10 +615,6 @@ defm CALLDATACOPY def : Pat<(EVMMemcpy_call_data GPR:$dst, GPR:$src, GPR:$size), (CALLDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; -defm CODESIZE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], - "CODESIZE", " $dst", 0x38, 2>; - let mayStore = 1 in defm CODECOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], @@ -585,10 +623,6 @@ defm CODECOPY def : Pat<(EVMMemcpy_code GPR:$dst, GPR:$src, GPR:$size), (CODECOPY GPR:$dst, GPR:$src, GPR:$size)>; -defm GASPRICE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], - "GASPRICE", " $dst", 0x3A, 2>; - defm EXTCODESIZE : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_extcodesize GPR:$addr))], @@ -622,49 +656,16 @@ defm BLOCKHASH [(set GPR:$dst, (int_evm_blockhash GPR:$addr))], "BLOCKHASH", " $dst, $addr", 0x40, 20>; -defm COINBASE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], - "COINBASE", " $dst", 0x41, 2>; - -defm TIMESTAMP - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], - "TIMESTAMP", " $dst", 0x42, 2>; - -defm NUMBER - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], - "NUMBER", " $dst", 0x43, 2>; - -defm DIFFICULTY - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], - "DIFFICULTY", " $dst", 0x44, 2>; - -defm GASLIMIT - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], - "GASLIMIT", " $dst", 0x45, 2>; - -defm CHAINID - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], - "CHAINID", " $dst", 0x46, 2>; - let hasSideEffects = 1 in defm SELFBALANCE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_selfbalance))], "SELFBALANCE", " $dst", 0x47, 5>; -defm BASEFEE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], - "BASEFEE", " $dst", 0x48, 2>; - defm BLOBHASH : I<(outs GPR:$dst), (ins GPR:$index), [(set GPR:$dst, (int_evm_blobhash GPR:$index))], "BLOBHASH", " $dst, $index", 0x49, 3>; -defm BLOBBASEFEE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], - "BLOBBASEFEE", " $dst", 0x4A, 2>; - - //===----------------------------------------------------------------------===// // EVM instructions for logging. //===----------------------------------------------------------------------===// diff --git a/llvm/test/CodeGen/EVM/context-remat.ll b/llvm/test/CodeGen/EVM/context-remat.ll index ad9406674385..4a58830498a1 100644 --- a/llvm/test/CodeGen/EVM/context-remat.ll +++ b/llvm/test/CodeGen/EVM/context-remat.ll @@ -10,14 +10,14 @@ define i256 @test_address() { ; CHECK-LABEL: test_address: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: ADDRESS -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET0 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: ADDRESS ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.address() call void @use(i256 %ret) @@ -28,14 +28,14 @@ define i256 @test_origin() { ; CHECK-LABEL: test_origin: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: ORIGIN -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET1 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: ORIGIN ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET1: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ORIGIN +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.origin() call void @use(i256 %ret) @@ -46,14 +46,14 @@ define i256 @test_caller() { ; CHECK-LABEL: test_caller: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: CALLER -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET2 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: CALLER ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET2: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLER +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.caller() call void @use(i256 %ret) @@ -64,14 +64,14 @@ define i256 @test_callvalue() { ; CHECK-LABEL: test_callvalue: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: CALLVALUE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET3 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: CALLVALUE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET3: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLVALUE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.callvalue() call void @use(i256 %ret) @@ -82,14 +82,14 @@ define i256 @test_calldatasize() { ; CHECK-LABEL: test_calldatasize: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: CALLDATASIZE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET4 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: CALLDATASIZE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET4: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATASIZE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.calldatasize() call void @use(i256 %ret) @@ -100,14 +100,14 @@ define i256 @test_codesize() { ; CHECK-LABEL: test_codesize: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: CODESIZE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET5 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: CODESIZE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET5: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODESIZE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.codesize() call void @use(i256 %ret) @@ -118,14 +118,14 @@ define i256 @test_gasprice() { ; CHECK-LABEL: test_gasprice: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: GASPRICE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET6 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: GASPRICE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET6: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASPRICE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.gasprice() call void @use(i256 %ret) @@ -136,14 +136,14 @@ define i256 @test_coinbase() { ; CHECK-LABEL: test_coinbase: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: COINBASE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET7 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: COINBASE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET7: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: COINBASE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.coinbase() call void @use(i256 %ret) @@ -154,14 +154,14 @@ define i256 @test_timestamp() { ; CHECK-LABEL: test_timestamp: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: TIMESTAMP -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET8 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: TIMESTAMP ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET8: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TIMESTAMP +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.timestamp() call void @use(i256 %ret) @@ -172,14 +172,14 @@ define i256 @test_number() { ; CHECK-LABEL: test_number: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: NUMBER -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET9 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: NUMBER ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET9: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NUMBER +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.number() call void @use(i256 %ret) @@ -190,14 +190,14 @@ define i256 @test_difficulty() { ; CHECK-LABEL: test_difficulty: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DIFFICULTY -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET10 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: DIFFICULTY ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET10: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIFFICULTY +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.difficulty() call void @use(i256 %ret) @@ -208,14 +208,14 @@ define i256 @test_gaslimit() { ; CHECK-LABEL: test_gaslimit: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: GASLIMIT -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET11 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: GASLIMIT ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET11: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASLIMIT +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.gaslimit() call void @use(i256 %ret) @@ -226,14 +226,14 @@ define i256 @test_chainid() { ; CHECK-LABEL: test_chainid: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: CHAINID -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET12 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: CHAINID ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET12: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CHAINID +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.chainid() call void @use(i256 %ret) @@ -244,14 +244,14 @@ define i256 @test_basefee() { ; CHECK-LABEL: test_basefee: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: BASEFEE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET13 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: BASEFEE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET13: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BASEFEE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.basefee() call void @use(i256 %ret) @@ -262,14 +262,14 @@ define i256 @test_blobbasefee() { ; CHECK-LABEL: test_blobbasefee: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: BLOBBASEFEE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH4 @.FUNC_RET14 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: BLOBBASEFEE ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET14: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBBASEFEE +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.blobbasefee() call void @use(i256 %ret) From 15151d547479d20cf1c70b1e3406fe18b66f0dba Mon Sep 17 00:00:00 2001 From: Philip Reames Date: Mon, 25 Nov 2024 18:59:31 -0800 Subject: [PATCH 108/203] [TTI][RISCV] Unconditionally break critical edges to sink ADDI (#108889) This looks like a rather weird change, so let me explain why this isn't as unreasonable as it looks. Let's start with the problem it's solving. ``` define signext i32 @overlap_live_ranges(ptr %arg, i32 signext %arg1) { bb: %i = icmp eq i32 %arg1, 1 br i1 %i, label %bb2, label %bb5 bb2: ; preds = %bb %i3 = getelementptr inbounds nuw i8, ptr %arg, i64 4 %i4 = load i32, ptr %i3, align 4 br label %bb5 bb5: ; preds = %bb2, %bb %i6 = phi i32 [ %i4, %bb2 ], [ 13, %bb ] ret i32 %i6 } ``` Right now, we codegen this as: ``` li a3, 1 li a2, 13 bne a1, a3, .LBB0_2 lw a2, 4(a0) .LBB0_2: mv a0, a2 ret ``` In this example, we have two values which must be assigned to a0 per the ABI (%arg, and the return value). SelectionDAG ensures that all values used in a successor phi are defined before exit the predecessor block. This creates an ADDI to materialize the immediate in the entry block. Currently, this ADDI is not sunk into the tail block because we'd have to split a critical edges to do so. Note that if our immediate was anything large enough to require two instructions we *would* split this critical edge. Looking at other targets, we notice that they don't seem to have this problem. They perform the sinking, and tail duplication that we don't. Why? Well, it turns out for AArch64 that this is entirely an accident of the existance of the gpr32all register class. The immediate is materialized into the gpr32 class, and then copied into the gpr32all register class. The existance of that copy puts us right back into the two instruction case noted above. This change essentially just bypasses this emergent behavior aspect of the aarch64 behavior, and implements the same "always sink immediates" behavior for RISCV as well. --- llvm/include/llvm/CodeGen/TargetInstrInfo.h | 6 ++++++ llvm/lib/CodeGen/MachineSink.cpp | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index c568f1f9ca06..d129034dd1c0 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -159,6 +159,12 @@ class TargetInstrInfo : public MCInstrInfo { virtual bool isSafeToSink(MachineInstr &MI, MachineBasicBlock *SuccToSinkTo, MachineCycleInfo *CI) const { return true; +} + + /// For a "cheap" instruction which doesn't enable additional sinking, + /// should MachineSink break a critical edge to sink it anyways? + virtual bool shouldBreakCriticalEdgeToSink(MachineInstr &MI) const { + return false; } protected: diff --git a/llvm/lib/CodeGen/MachineSink.cpp b/llvm/lib/CodeGen/MachineSink.cpp index 4b3ff57fb478..764d4e5995e2 100644 --- a/llvm/lib/CodeGen/MachineSink.cpp +++ b/llvm/lib/CodeGen/MachineSink.cpp @@ -953,7 +953,9 @@ bool MachineSinking::isWorthBreakingCriticalEdge( } } - return false; + // Let the target decide if it's worth breaking this + // critical edge for a "cheap" instruction. + return TII->shouldBreakCriticalEdgeToSink(MI); } bool MachineSinking::isLegalToBreakCriticalEdge(MachineInstr &MI, From 4be9e8571bde8304f7006487043290905f655e1e Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 20 Jan 2025 13:11:06 +0100 Subject: [PATCH 109/203] [EVM] Add pre-commit test for Enable MachineSink to break critical edges for cheap instructions Signed-off-by: Vladimir Radosavljevic --- .../EVM/machine-sink-cheap-instructions.ll | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll diff --git a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll new file mode 100644 index 000000000000..d7dc7799a6f2 --- /dev/null +++ b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll @@ -0,0 +1,96 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @bar() + +define i256 @test1(i256 %arg) { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 10 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB0_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_1: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH4 @bar +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_2: ; %bb2 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %cmp = icmp eq i256 %arg, 10 + br i1 %cmp, label %bb1, label %bb2 + +bb1: + %call = tail call i256 @bar() + br label %bb2 + +bb2: + %phi = phi i256 [ %call, %bb1 ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test2(i256 %arg) { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 10 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB1_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_1: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: PUSH4 @bar +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB1_2: ; %bb2 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP +entry: + %address = call i256 @llvm.evm.address() + %cmp = icmp eq i256 %arg, 10 + br i1 %cmp, label %bb1, label %bb2 + +bb1: + %call = tail call i256 @bar() + br label %bb2 + +bb2: + %phi = phi i256 [ %call, %bb1 ], [ %address, %entry ] + ret i256 %phi +} + +declare i256 @llvm.evm.address() From 95d44ff9a261cb2b20e7d7086345f070b81a7e2b Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 20 Jan 2025 13:08:32 +0100 Subject: [PATCH 110/203] [EVM] Enable MachineSink to break critical edges for cheap instructions Break critical edges in MachineSink optimizations for instructions that are marked with isAsCheapAsAMove in tablegen. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMInstrInfo.h | 4 +++ .../EVM/machine-sink-cheap-instructions.ll | 30 +++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h index 87931ad678b8..f56d954c3245 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.h +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -50,6 +50,10 @@ class EVMInstrInfo final : public EVMGenInstrInfo { bool isReallyTriviallyReMaterializable(const MachineInstr &MI) const override; + bool shouldBreakCriticalEdgeToSink(MachineInstr &MI) const override { + return true; + } + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, bool KillSrc, bool RenamableDest = false, diff --git a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll index d7dc7799a6f2..e8fd44212c47 100644 --- a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll +++ b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll @@ -10,31 +10,26 @@ define i256 @test1(i256 %arg) { ; CHECK-LABEL: test1: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 10 ; CHECK-NEXT: EQ ; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB0_3 -; CHECK-NEXT: JUMPI ; CHECK-NEXT: PUSH4 @.BB0_1 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: .BB0_1: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_1: ; %bb1 +; CHECK-NEXT: .BB0_2: ; %bb1 ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP ; CHECK-NEXT: PUSH4 @.FUNC_RET0 ; CHECK-NEXT: PUSH4 @bar ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB0_2: ; %bb2 -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %cmp = icmp eq i256 %arg, 10 @@ -53,31 +48,26 @@ define i256 @test2(i256 %arg) { ; CHECK-LABEL: test2: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: ADDRESS -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 10 ; CHECK-NEXT: EQ ; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB1_3 -; CHECK-NEXT: JUMPI ; CHECK-NEXT: PUSH4 @.BB1_1 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB1_2 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB1_3: +; CHECK-NEXT: .BB1_1: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB1_2 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB1_1: ; %bb1 +; CHECK-NEXT: .BB1_2: ; %bb1 ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP ; CHECK-NEXT: PUSH4 @.FUNC_RET1 ; CHECK-NEXT: PUSH4 @bar ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET1: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB1_2: ; %bb2 -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP entry: %address = call i256 @llvm.evm.address() From b4e9fe17bcbfad101d432946722f7965a83f450f Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 28 Jan 2025 12:18:38 +0100 Subject: [PATCH 111/203] [EVM] Add pre-commit test for Implement reverseBranchCondition and run BranchFolder pass after stackification Signed-off-by: Vladimir Radosavljevic --- .../EVM/branch-folder-after-stackification.ll | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll new file mode 100644 index 000000000000..91bfcfe76481 --- /dev/null +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -0,0 +1,53 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test(i256 %arg) { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB0_1 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH1 10 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH1 20 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB0_4 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.6: +; CHECK-NEXT: PUSH4 @.BB0_5 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_4: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %cmp1 = icmp sgt i256 %arg, 0 + %cmp2 = icmp slt i256 %arg, 0 + %select1 = select i1 %cmp2, i256 10, i256 20 + %select2 = select i1 %cmp1, i256 5, i256 %select1 + ret i256 %select2 +} From 674773f1de22a1d84bbe48c3e956cc43e0edb990 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 22 Jan 2025 12:47:59 +0100 Subject: [PATCH 112/203] [EVM] Implement reverseBranchCondition and run BranchFolder pass after stackification This patch adds jump_unless instructions that allows to implement reverseBranchCondition. This is the same trick that is used for WebAssembly https://reviews.llvm.org/D14995. Also it adds one more run of BranchFolder pass after stackification to optimize branches. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/CMakeLists.txt | 1 + llvm/lib/Target/EVM/EVM.h | 2 + llvm/lib/Target/EVM/EVMInstrInfo.cpp | 86 +++++++------- llvm/lib/Target/EVM/EVMInstrInfo.td | 15 +++ llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp | 107 ++++++++++++++++++ llvm/lib/Target/EVM/EVMStackModel.h | 3 +- llvm/lib/Target/EVM/EVMStackSolver.cpp | 2 +- llvm/lib/Target/EVM/EVMStackify.cpp | 17 ++- .../lib/Target/EVM/EVMStackifyCodeEmitter.cpp | 20 ++-- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 10 ++ .../EVM/branch-folder-after-stackification.ll | 20 ++-- .../EVM/machine-sink-cheap-instructions.ll | 16 +-- 12 files changed, 213 insertions(+), 86 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 70db127e45cc..31cc22362125 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -48,6 +48,7 @@ add_llvm_target(EVMCodeGen EVMInstrInfo.cpp EVMLinkRuntime.cpp EVMLowerIntrinsics.cpp + EVMLowerJumpUnless.cpp EVMMachineFunctionInfo.cpp EVMMCInstLower.cpp EVMOptimizeLiveIntervals.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 6cc2073988d9..f8f129f5b97e 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -65,6 +65,7 @@ FunctionPass *createEVMSingleUseExpression(); FunctionPass *createEVMSplitCriticalEdges(); FunctionPass *createEVMStackify(); FunctionPass *createEVMBPStackification(); +FunctionPass *createEVMLowerJumpUnless(); // PassRegistry initialization declarations. void initializeEVMCodegenPreparePass(PassRegistry &); @@ -80,6 +81,7 @@ void initializeEVMStackifyPass(PassRegistry &); void initializeEVMBPStackificationPass(PassRegistry &); void initializeEVMAAWrapperPassPass(PassRegistry &); void initializeEVMExternalAAWrapperPass(PassRegistry &); +void initializeEVMLowerJumpUnlessPass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { EVMLinkRuntimePass() = default; diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index c78102a7c02f..e22441aa6405 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -92,11 +92,8 @@ EVMInstrInfo::BranchType EVMInstrInfo::analyzeBranch( FBB = nullptr; Cond.clear(); - const auto *MFI = MBB.getParent()->getInfo(); - if (MFI->getIsStackified()) { - LLVM_DEBUG(dbgs() << "Can't analyze terminators in stackified code"); - return BT_None; - } + const bool IsStackified = + MBB.getParent()->getInfo()->getIsStackified(); MachineBasicBlock::reverse_iterator I = MBB.rbegin(), REnd = MBB.rend(); @@ -162,23 +159,15 @@ EVMInstrInfo::BranchType EVMInstrInfo::analyzeBranch( FBB = TBB; TBB = CondBr->getOperand(0).getMBB(); - // Put the "use" of the condition into Cond[0]. - const MachineOperand &UseMO = CondBr->getOperand(1); - Cond.push_back(UseMO); - - // reverseBranch needs the instruction which feeds the branch, but only - // supports comparisons. See if we can find one. - for (MachineBasicBlock::reverse_iterator CI = CondBr; CI != REnd; ++CI) { - // If it is the right comparison, put its result into Cond[1]. - // TODO: This info is required for branch reversing, but this - // is not yet implemented. - if (CI->isCompare()) { - const MachineOperand &DefMO = CI->getOperand(0); - if (DefMO.getReg() == UseMO.getReg()) - Cond.push_back(DefMO); - // Only give it one shot, this should be enough. - break; - } + // Set from which instruction this condition comes from. It is needed for + // reversing and inserting of branches. + Cond.push_back( + MachineOperand::CreateImm(CondBr->getOpcode() == EVM::JUMPI || + CondBr->getOpcode() == EVM::PseudoJUMPI)); + if (!IsStackified) { + // Put the "use" of the condition into Cond[1]. + const MachineOperand &UseMO = CondBr->getOperand(1); + Cond.push_back(UseMO); } if (!SecondLastInst) @@ -220,27 +209,39 @@ unsigned EVMInstrInfo::insertBranch(MachineBasicBlock &MBB, // The number of instructions inserted. unsigned InstrCount = 0; - - const bool IsUncondBranch = Cond.empty(); - const bool IsCondBranch = - (Cond.size() == 1 && Cond[0].isReg()) || - (Cond.size() == 2 && Cond[0].isReg() && Cond[1].isReg()); + const bool IsStackified = + MBB.getParent()->getInfo()->getIsStackified(); + unsigned UncondOpc = !IsStackified ? EVM::JUMP : EVM::PseudoJUMP; // Insert a branch to the "true" destination. assert(TBB && "A branch must have a destination"); - if (IsUncondBranch) - BuildMI(&MBB, DL, get(EVM::JUMP)).addMBB(TBB); - else if (IsCondBranch) - BuildMI(&MBB, DL, get(EVM::JUMPI)).addMBB(TBB).add(Cond[0]); - else - llvm_unreachable("Unexpected branching condition"); + if (Cond.empty()) { + BuildMI(&MBB, DL, get(UncondOpc)).addMBB(TBB); + } else { + // Destinguish between stackified and non-stackified instructions. + unsigned CondOpc = 0; + if (!IsStackified) + CondOpc = Cond[0].getImm() ? EVM::JUMPI : EVM::JUMP_UNLESS; + else + CondOpc = Cond[0].getImm() ? EVM::PseudoJUMPI : EVM::PseudoJUMP_UNLESS; + + auto NewMI = BuildMI(&MBB, DL, get(CondOpc)).addMBB(TBB); + + // Add a condition operand, if we are not in stackified form. + if (!IsStackified) { + assert( + Cond.size() == 2 && + "Unexpected number of conditional operands in non-stackified code"); + NewMI.add(Cond[1]); + } + } ++InstrCount; // If there is also a "false" destination, insert another branch. if (FBB) { assert(!Cond.empty() && "Unconditional branch can't have two destinations"); - BuildMI(&MBB, DL, get(EVM::JUMP)).addMBB(FBB); + BuildMI(&MBB, DL, get(UncondOpc)).addMBB(FBB); ++InstrCount; } @@ -249,16 +250,9 @@ unsigned EVMInstrInfo::insertBranch(MachineBasicBlock &MBB, bool EVMInstrInfo::reverseBranchCondition( SmallVectorImpl &Cond) const { - // TODO: CPR-1557. Try to add support for branch reversing. The main problem - // is that it may require insertion of additional instructions in the BB. - // For example, - // - // NE $3, $2, $1 - // - // should be transformed into - // - // EQ $3, $2, $1 - // ISZERO $4, $3 - - return true; + assert((Cond.size() == 1 || Cond.size() == 2) && + "Unexpected number of conditional operands"); + assert(Cond[0].isImm() && "Unexpected condition type"); + Cond.front() = MachineOperand::CreateImm(!Cond.front().getImm()); + return false; } diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 20b7f18cd24e..7d41262e88b1 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -404,7 +404,9 @@ let isBranch = 1, isTerminator = 1 in { defm JUMPI : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], "JUMPI", " $dst, $cond", 0x57, 10>; +def JUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst, GPR:$cond), []>; def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; +def PseudoJUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; let isBarrier = 1 in { defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", " $dst", @@ -413,6 +415,19 @@ let isBarrier = 1 in { } // isBarrier = 1 } // isBranch = 1, isTerminator = 1 +def : Pat<(brcond (setcc GPR:$src, 0, SETEQ), bb:$dst), + (JUMP_UNLESS bb:$dst, GPR:$src)>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), + (JUMP_UNLESS bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETGE), bb:$dst), + (JUMP_UNLESS bb:$dst, (LT GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETLE), bb:$dst), + (JUMP_UNLESS bb:$dst, (GT GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETULE), bb:$dst), + (JUMP_UNLESS bb:$dst, (UGT GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETUGE), bb:$dst), + (JUMP_UNLESS bb:$dst, (ULT GPR:$rs0, GPR:$rs1))>; + // This isn't really a control flow instruction, but it should be used to mark // destination of jump instructions. defm JUMPDEST : I<(outs), (ins), [], "JUMPDEST", "", 0x5B, 1>; diff --git a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp new file mode 100644 index 000000000000..2e85a5157e70 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp @@ -0,0 +1,107 @@ +//===----- EVMLowerJumpUnless.cpp - Lower jump_unless ----------*- 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 pass lowers jump_unless into iszero and jumpi instructions. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-lower-jump-unless" +#define EVM_LOWER_JUMP_UNLESS_NAME "EVM Lower jump_unless" + +namespace { +class EVMLowerJumpUnless final : public MachineFunctionPass { +public: + static char ID; + + EVMLowerJumpUnless() : MachineFunctionPass(ID) { + initializeEVMLowerJumpUnlessPass(*PassRegistry::getPassRegistry()); + } + + StringRef getPassName() const override { return EVM_LOWER_JUMP_UNLESS_NAME; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char EVMLowerJumpUnless::ID = 0; + +INITIALIZE_PASS(EVMLowerJumpUnless, DEBUG_TYPE, EVM_LOWER_JUMP_UNLESS_NAME, + false, false) + +FunctionPass *llvm::createEVMLowerJumpUnless() { + return new EVMLowerJumpUnless(); +} + +// Lower jump_unless into iszero and jumpi instructions. This instruction +// can only be present in non-stackified functions. +static void lowerJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, + const bool IsStackified, MachineRegisterInfo &MRI) { + assert(!IsStackified && "Found jump_unless in stackified function"); + assert(MI.getNumExplicitOperands() == 2 && + "Unexpected number of operands in jump_unless"); + auto NewReg = MRI.createVirtualRegister(&EVM::GPRRegClass); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO), NewReg) + .add(MI.getOperand(1)); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::JUMPI)) + .add(MI.getOperand(0)) + .addReg(NewReg); +} + +// Lower pseudo jump_unless into iszero and jumpi instructions. This pseudo +// instruction can only be present in stackified functions. +static void lowerPseudoJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, + const bool IsStackified) { + assert(IsStackified && "Found pseudo jump_unless in non-stackified function"); + assert(MI.getNumExplicitOperands() == 1 && + "Unexpected number of operands in pseudo jump_unless"); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO_S)); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::PseudoJUMPI)) + .add(MI.getOperand(0)); +} + +bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG({ + dbgs() << "********** Lower jump_unless instructions **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + MachineRegisterInfo &MRI = MF.getRegInfo(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + const bool IsStackified = + MF.getInfo()->getIsStackified(); + + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + for (auto &MI : make_early_inc_range(MBB)) { + if (MI.getOpcode() == EVM::PseudoJUMP_UNLESS) + lowerPseudoJumpUnless(MI, TII, IsStackified); + else if (MI.getOpcode() == EVM::JUMP_UNLESS) + lowerJumpUnless(MI, TII, IsStackified, MRI); + else + continue; + + MI.eraseFromParent(); + Changed = true; + } + } + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h index 055785e50d98..81348bfa1997 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.h +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -321,7 +321,8 @@ class EVMStackModel { LIS.getInterval(MI.getOperand(0).getReg()).containsOneValue()) return true; return Opc == EVM::ARGUMENT || Opc == EVM::RET || Opc == EVM::JUMP || - Opc == EVM::JUMPI || Opc == EVM::PUSHDEPLOYADDRESS; + Opc == EVM::JUMPI || Opc == EVM::PUSHDEPLOYADDRESS || + Opc == EVM::JUMP_UNLESS; } auto instructionsToProcess(const MachineBasicBlock *MBB) const { return make_filter_range( diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index b477ed624991..7ac2ed1f9096 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -155,7 +155,7 @@ BranchInfoTy llvm::getBranchInfo(const MachineBasicBlock *MBB) { "Condition should be defined for a condition branch."); assert(BranchInstrs.size() <= 2); return {BT, TBB, FBB, BranchInstrs, - Cond.empty() ? std::nullopt : std::optional(Cond[0])}; + Cond.empty() ? std::nullopt : std::optional(Cond[1])}; } /// Return cost of transformation in gas from \p Source to \p Target or diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp index b2fd43ca6387..46da07061e20 100644 --- a/llvm/lib/Target/EVM/EVMStackify.cpp +++ b/llvm/lib/Target/EVM/EVMStackify.cpp @@ -366,6 +366,7 @@ void StackModel::handleInstruction(MachineInstr *MI) { handleJump(MI); return; case EVM::JUMPI: + case EVM::JUMP_UNLESS: handleCondJump(MI); return; case EVM::ARGUMENT: @@ -720,7 +721,15 @@ void StackModel::handleArgument(MachineInstr *MI) { void StackModel::handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, const Register &Reg) { - assert(MI->getOpcode() == EVM::JUMP || MI->getOpcode() == EVM::JUMPI); + unsigned PseudoJumpOpc = 0; + if (MI->getOpcode() == EVM::JUMP) + PseudoJumpOpc = EVM::PseudoJUMP; + else if (MI->getOpcode() == EVM::JUMPI) + PseudoJumpOpc = EVM::PseudoJUMPI; + else if (MI->getOpcode() == EVM::JUMP_UNLESS) + PseudoJumpOpc = EVM::PseudoJUMP_UNLESS; + else + llvm_unreachable("Unexpected jump instruction"); // If the condition register is in the L-stack, we need to move it to // the bottom of the L-stack. After that we should clean clean the L-stack. @@ -731,8 +740,6 @@ void StackModel::handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, // Insert pseudo jump instruciton that will be replaced with PUSH and JUMP // instructions in AsmPrinter. ToErase.push_back(MI); - unsigned PseudoJumpOpc = - MI->getOpcode() == EVM::JUMP ? EVM::PseudoJUMP : EVM::PseudoJUMPI; BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(PseudoJumpOpc)) .addMBB(MBB); } @@ -988,8 +995,8 @@ void StackModel::stackifyInstruction(MachineInstr *MI) { unsigned RegOpcode = MI->getOpcode(); if (RegOpcode == EVM::PUSH_LABEL || RegOpcode == EVM::PseudoJUMP || - RegOpcode == EVM::PseudoJUMPI || RegOpcode == EVM::PseudoCALL || - RegOpcode == EVM::PseudoRET) + RegOpcode == EVM::PseudoJUMPI || RegOpcode == EVM::PseudoJUMP_UNLESS || + RegOpcode == EVM::PseudoCALL || RegOpcode == EVM::PseudoRET) return; // Remove register operands. diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp index 3f4928232011..06f180ea1da2 100644 --- a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -59,9 +59,10 @@ void EVMStackifyCodeEmitter::CodeEmitter::enterMBB(MachineBasicBlock *MBB, void EVMStackifyCodeEmitter::CodeEmitter::emitInst(const MachineInstr *MI) { unsigned Opc = MI->getOpcode(); - assert(Opc != EVM::JUMP && Opc != EVM::JUMPI && Opc != EVM::ARGUMENT && - Opc != EVM::RET && Opc != EVM::CONST_I256 && Opc != EVM::COPY_I256 && - Opc != EVM::FCALL && "Unexpected instruction"); + assert(Opc != EVM::JUMP && Opc != EVM::JUMPI && Opc != EVM::JUMP_UNLESS && + Opc != EVM::ARGUMENT && Opc != EVM::RET && Opc != EVM::CONST_I256 && + Opc != EVM::COPY_I256 && Opc != EVM::FCALL && + "Unexpected instruction"); size_t NumInputs = MI->getNumExplicitOperands() - MI->getNumExplicitDefs(); assert(StackHeight >= NumInputs && "Not enough operands on the stack"); @@ -178,13 +179,16 @@ void EVMStackifyCodeEmitter::CodeEmitter::emitUncondJump( void EVMStackifyCodeEmitter::CodeEmitter::emitCondJump( const MachineInstr *MI, MachineBasicBlock *Target) { - assert(MI->getOpcode() == EVM::JUMPI && - "Unexpected conditional jump instruction"); + assert(MI->getOpcode() == EVM::JUMPI || + MI->getOpcode() == EVM::JUMP_UNLESS && + "Unexpected conditional jump instruction"); assert(StackHeight > 0 && "Expected at least one operand on the stack"); StackHeight -= 1; - auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), - TII->get(EVM::PseudoJUMPI)) - .addMBB(Target); + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), + TII->get(MI->getOpcode() == EVM::JUMPI ? EVM::PseudoJUMPI + : EVM::PseudoJUMP_UNLESS)) + .addMBB(Target); verify(NewMI); } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index bc49cf7b79be..206bdad1353b 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -60,6 +60,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMBPStackificationPass(PR); initializeEVMAAWrapperPassPass(PR); initializeEVMExternalAAWrapperPass(PR); + initializeEVMLowerJumpUnlessPass(PR); } static std::string computeDataLayout() { @@ -185,6 +186,7 @@ class EVMPassConfig final : public TargetPassConfig { bool addInstSelector() override; void addPostRegAlloc() override; void addPreEmitPass() override; + void addPreEmitPass2() override; }; } // namespace @@ -254,9 +256,17 @@ void EVMPassConfig::addPreEmitPass() { } else { addPass(createEVMBPStackification()); } + + // Optimize branch instructions after stackification. This is done again + // here, since EVMSplitCriticalEdges may introduce new BBs that could + // contain only branches after stackification. + if (getOptLevel() != CodeGenOptLevel::None) + addPass(&BranchFolderPassID); } } +void EVMPassConfig::addPreEmitPass2() { addPass(createEVMLowerJumpUnless()); } + TargetPassConfig *EVMTargetMachine::createPassConfig(PassManagerBase &PM) { return new EVMPassConfig(*this, PM); } diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll index 91bfcfe76481..e3af7a77578f 100644 --- a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -12,32 +12,26 @@ define i256 @test(i256 %arg) { ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: SLT -; CHECK-NEXT: PUSH4 @.BB0_1 -; CHECK-NEXT: JUMPI +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_1: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: PUSH1 10 -; CHECK-NEXT: SWAP3 ; CHECK-NEXT: PUSH4 @.BB0_3 ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB0_2: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: PUSH1 20 -; CHECK-NEXT: SWAP3 ; CHECK-NEXT: .BB0_3: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 ; CHECK-NEXT: SGT -; CHECK-NEXT: PUSH4 @.BB0_4 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.6: +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_5 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_4: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.4: ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP ; CHECK-NEXT: PUSH1 5 diff --git a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll index e8fd44212c47..22bd43eae755 100644 --- a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll +++ b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll @@ -12,13 +12,9 @@ define i256 @test1(i256 %arg) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 10 ; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB0_1 -; CHECK-NEXT: JUMPI ; CHECK-NEXT: PUSH4 @.BB0_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_1: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -50,13 +46,9 @@ define i256 @test2(i256 %arg) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 10 ; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB1_1 -; CHECK-NEXT: JUMPI ; CHECK-NEXT: PUSH4 @.BB1_2 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB1_1: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: ADDRESS ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP From 10f0507bb593de147d71bed66604403ff7b06b83 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 28 Jan 2025 12:31:48 +0100 Subject: [PATCH 113/203] [EVM] Remove MachineBlockPlacement run Since we disabled MachineBlockPlacement in EVMPassConfig::addPostRegAlloc, adding this pass after that has no effect, since it won't be run. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 206bdad1353b..20a01fc7a61e 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -246,7 +246,6 @@ void EVMPassConfig::addPreEmitPass() { // FIXME: enable all the passes below, but the Stackify with EVMKeepRegisters. if (!EVMKeepRegisters) { addPass(createEVMSplitCriticalEdges()); - addPass(&MachineBlockPlacementID); addPass(createEVMOptimizeLiveIntervals()); addPass(createEVMSingleUseExpression()); if (EVMUseLocalStakify) { From 897247c1553a885b9be9c6e5fc5d4a2a5bbe4f1d Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Tue, 25 Mar 2025 18:01:23 +0100 Subject: [PATCH 114/203] [EVM] Swap the 'combineStack' arguments The order of arguments in 'combineStack' influences the resulting combined stack, which is unexpected. Additionally, passing TBB's stack resolves many 'stack-too-deep' errors in CI. TODO: #781. Investigate the cause of this behavior. --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 8 ++++++-- .../CodeGen/EVM/branch-folder-after-stackification.ll | 8 +++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 7ac2ed1f9096..9cf3303c423f 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -340,8 +340,12 @@ void EVMStackSolver::runPropagation() { Worklist.push_front(TBB); if (FBBVisited && TBBVisited) { - Stack CombinedStack = combineStack(StackModel.getMBBEntryStack(FBB), - StackModel.getMBBEntryStack(TBB)); + // The order of arguments in 'combineStack' influences the + // resulting combined stack, which is unexpected. Additionally, + // passing TBB's stack resolves many 'stack-too-deep' errors in CI. + // TODO: #781. Investigate the cause of this behavior. + Stack CombinedStack = combineStack(StackModel.getMBBEntryStack(TBB), + StackModel.getMBBEntryStack(FBB)); // Retain the jump condition in ExitStack because StackSolver doesn't // propagate stacks through branch instructions. CombinedStack.emplace_back(StackModel.getStackSlot(*Condition)); diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll index e3af7a77578f..d732d9aa3948 100644 --- a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -8,9 +8,8 @@ define i256 @test(i256 %arg) { ; CHECK-LABEL: test: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP2 ; CHECK-NEXT: SLT ; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 @@ -26,18 +25,17 @@ define i256 @test(i256 %arg) { ; CHECK-NEXT: PUSH1 20 ; CHECK-NEXT: .BB0_3: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SGT ; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_5 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.4: -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP ; CHECK-NEXT: PUSH1 5 -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: .BB0_5: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %cmp1 = icmp sgt i256 %arg, 0 %cmp2 = icmp slt i256 %arg, 0 From af7d07ca03467cc144a7eccd352cbe2adadde9ed Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 27 Mar 2025 20:43:09 +0100 Subject: [PATCH 115/203] [EVM] Add custom lowering of bit manipulation intrinsics The implementation was copied from EraVM. TODO: #794. Examine the rationale behind enabling or disabling intrinsics. --- llvm/lib/Target/EVM/EVMISelLowering.cpp | 111 +++ llvm/lib/Target/EVM/EVMISelLowering.h | 4 + .../CodeGen/EVM/bitmanipulation-intrinsics.ll | 712 ++++++++++++++++++ 3 files changed, 827 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index eeba3ee799bd..f771def5eba8 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -71,6 +71,8 @@ EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, } setOperationAction(ISD::UMUL_LOHI, MVT::i256, Custom); + setOperationAction(ISD::BSWAP, MVT::i256, Custom); + setOperationAction(ISD::CTPOP, MVT::i256, Custom); // Custom lowering of extended loads. for (const MVT VT : MVT::integer_valuetypes()) { @@ -130,9 +132,118 @@ SDValue EVMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { return LowerUMUL_LOHI(Op, DAG); case ISD::INTRINSIC_VOID: return LowerINTRINSIC_VOID(Op, DAG); + case ISD::CTPOP: + return LowerCTPOP(Op, DAG); + case ISD::BSWAP: + return LowerBSWAP(Op, DAG); } } +SDValue EVMTargetLowering::LowerBSWAP(SDValue BSWAP, SelectionDAG &DAG) const { + SDNode *N = BSWAP.getNode(); + SDLoc dl(N); + EVT VT = N->getValueType(0); + SDValue Op = N->getOperand(0); + EVT SHVT = getShiftAmountTy(VT, DAG.getDataLayout()); + + assert(VT == MVT::i256 && "Unexpected type for bswap"); + + std::array Tmp; + + for (int i = 32; i >= 17; i--) + Tmp[i] = DAG.getNode(ISD::SHL, dl, VT, Op, + DAG.getConstant(((i - 17) * 16) + 8, dl, SHVT)); + + for (int i = 16; i >= 1; i--) + Tmp[i] = DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(((16 - i) * 16) + 8, dl, SHVT)); + + APInt FFMask = APInt(256, 255); + + // Mask off unwanted bytes + for (int i = 2; i < 32; i++) + Tmp[i] = DAG.getNode(ISD::AND, dl, VT, Tmp[i], + DAG.getConstant(FFMask << ((i - 1) * 8), dl, VT)); + + // OR everything together + for (int i = 2; i <= 32; i += 2) + Tmp[i] = DAG.getNode(ISD::OR, dl, VT, Tmp[i], Tmp[i - 1]); + + for (int i = 4; i <= 32; i += 4) + Tmp[i] = DAG.getNode(ISD::OR, dl, VT, Tmp[i], Tmp[i - 2]); + + for (int i = 8; i <= 32; i += 8) + Tmp[i] = DAG.getNode(ISD::OR, dl, VT, Tmp[i], Tmp[i - 4]); + + Tmp[32] = DAG.getNode(ISD::OR, dl, VT, Tmp[32], Tmp[24]); + Tmp[16] = DAG.getNode(ISD::OR, dl, VT, Tmp[16], Tmp[8]); + + return DAG.getNode(ISD::OR, dl, VT, Tmp[32], Tmp[16]); +} + +// This function only counts the number of bits in the lower 128 bits +// It is a slightly modified version of TargetLowering::expandCTPOP +static SDValue countPOP128(SDValue Op, SelectionDAG &DAG) { + SDLoc dl(Op); + EVT VT = Op->getValueType(0); + + // This is the "best" algorithm from + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + SDValue Mask55 = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x55)), dl, VT); + SDValue Mask33 = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x33)), dl, VT); + SDValue Mask0F = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x0F)), dl, VT); + + // v = v - ((v >> 1) & 0x55555555...) + Op = DAG.getNode(ISD::SUB, dl, VT, Op, + DAG.getNode(ISD::AND, dl, VT, + DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(1, dl, MVT::i256)), + Mask55)); + // v = (v & 0x33333333...) + ((v >> 2) & 0x33333333...) + Op = DAG.getNode(ISD::ADD, dl, VT, DAG.getNode(ISD::AND, dl, VT, Op, Mask33), + DAG.getNode(ISD::AND, dl, VT, + DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(2, dl, MVT::i256)), + Mask33)); + // v = (v + (v >> 4)) & 0x0F0F0F0F... + Op = DAG.getNode(ISD::AND, dl, VT, + DAG.getNode(ISD::ADD, dl, VT, Op, + DAG.getNode(ISD::SRL, dl, VT, Op, + DAG.getConstant(4, dl, MVT::i256))), + Mask0F); + + // v = (v * 0x01010101...) >> (Len - 8) + SDValue Mask01 = DAG.getConstant( + APInt::getSplat(VT.getScalarSizeInBits(), APInt(8, 0x01)), dl, VT); + return DAG.getNode(ISD::SRL, dl, VT, + DAG.getNode(ISD::MUL, dl, VT, Op, Mask01), + DAG.getConstant(120, dl, MVT::i256)); +} + +SDValue EVMTargetLowering::LowerCTPOP(SDValue CTPOP, SelectionDAG &DAG) const { + // TODO: Consider moving this implementation to + // TargetLowering::expandCTPOP() to support i256 types. + SDNode *Node = CTPOP.getNode(); + SDLoc dl(Node); + EVT VT = Node->getValueType(0); + SDValue Op = Node->getOperand(0); + + // Split the Op into two parts and count separately + SDValue hiPart = + DAG.getNode(ISD::SRL, dl, VT, Op, DAG.getConstant(128, dl, MVT::i256)); + SDValue loPart = + DAG.getNode(ISD::AND, dl, VT, Op, + DAG.getConstant(APInt::getLowBitsSet(256, 128), dl, VT)); + auto loSum = DAG.getNode(ISD::AND, dl, VT, countPOP128(loPart, DAG), + DAG.getConstant(255, dl, VT)); + auto hiSum = DAG.getNode(ISD::AND, dl, VT, countPOP128(hiPart, DAG), + DAG.getConstant(255, dl, VT)); + return DAG.getNode(ISD::ADD, dl, VT, loSum, hiSum); +} + SDValue EVMTargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { const SDLoc DL(Op); diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 6367ced2a1a6..4f2efbc25d19 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -123,6 +123,10 @@ class EVMTargetLowering final : public TargetLowering { SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + SDValue LowerBSWAP(SDValue BSWAP, SelectionDAG &DAG) const; + + SDValue LowerCTPOP(SDValue CTPOP, SelectionDAG &DAG) const; + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll new file mode 100644 index 000000000000..33db114b34ee --- /dev/null +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -0,0 +1,712 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.bitreverse.i256(i256) +declare i256 @llvm.bswap.i256(i256) +declare i256 @llvm.ctpop.i256(i256) +declare i256 @llvm.ctlz.i256(i256, i1) +declare i256 @llvm.cttz.i256(i256, i1) + +define i256 @bitreversetest(i256 %v) { +; CHECK-LABEL: bitreversetest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH2 65280 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH3 16711680 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 4278190080 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH5 1095216660480 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH6 280375465082880 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH7 71776119061217280 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH8 18374686479671623680 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH9 4703919738795935662080 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH10 1204203453131759529492480 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH11 308276084001730439550074880 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH12 78918677504442992524819169280 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH13 20203181441137406086353707335680 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH14 5172014448931175958106549077934080 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH15 1324035698926381045275276563951124480 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH16 338953138925153547590470800371487866880 +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH17 86772003564839308183160524895100893921280 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH18 22213632912598862894889094373145828843847680 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH19 5686690025625308901091608159525332184025006080 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH20 1455792646560079078679451688838485039110401556480 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH21 372682917519380244141939632342652170012262798458880 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH22 95406826884961342500336545879718955523139276405473280 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH23 24424147682550103680086155745208052613923654759801159680 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH24 6252581806732826542102055870773261469164455618509096878080 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH25 1600660942523603594778126302917954936106100638338328800788480 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH26 409769201286042520263200333546996463643161763414612173001850880 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH27 104900915529226885187379285388031094692649411434140716288473825280 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH28 26854634375482082607969097059335960241318249327140023369849299271680 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH29 6874786400123413147640088847190005821777471827747845982681420613550080 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH30 1759945318431593765795862744880641490375032787903448571566443677068820480 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH31 450546001518488004043740862689444221536008393703282834321009581329618042880 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SHL +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH32 6811299366900952671974763824040465167839410862684739061144563765171360567055 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH32 38597363079105398474523661669562635951089994888546854679819194669304376546645 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.bitreverse.i256(i256 %v) + ret i256 %res +} + +define i256 @bswaptest(i256 %v) { +; CHECK-LABEL: bswaptest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH2 65280 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH3 16711680 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 4278190080 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH5 1095216660480 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH6 280375465082880 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH7 71776119061217280 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH8 18374686479671623680 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH9 4703919738795935662080 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH10 1204203453131759529492480 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH11 308276084001730439550074880 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH12 78918677504442992524819169280 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH13 20203181441137406086353707335680 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH14 5172014448931175958106549077934080 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH15 1324035698926381045275276563951124480 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH16 338953138925153547590470800371487866880 +; CHECK-NEXT: DUP6 +; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH17 86772003564839308183160524895100893921280 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH18 22213632912598862894889094373145828843847680 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH19 5686690025625308901091608159525332184025006080 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH20 1455792646560079078679451688838485039110401556480 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH21 372682917519380244141939632342652170012262798458880 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH22 95406826884961342500336545879718955523139276405473280 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH23 24424147682550103680086155745208052613923654759801159680 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH24 6252581806732826542102055870773261469164455618509096878080 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH25 1600660942523603594778126302917954936106100638338328800788480 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH26 409769201286042520263200333546996463643161763414612173001850880 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH27 104900915529226885187379285388031094692649411434140716288473825280 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH28 26854634375482082607969097059335960241318249327140023369849299271680 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH29 6874786400123413147640088847190005821777471827747845982681420613550080 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH30 1759945318431593765795862744880641490375032787903448571566443677068820480 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH31 450546001518488004043740862689444221536008393703282834321009581329618042880 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: SHL +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SHL +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.bswap.i256(i256 %v) + ret i256 %res +} + +define i256 @ctpoptest(i256 %v) { +; CHECK-LABEL: ctpoptest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH32 454086624460063511464984254936031011189294057512315937409637584344757371137 +; CHECK-NEXT: PUSH16 20016609818878733144904388672456953615 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 129 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SHR +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 +; CHECK-NEXT: AND +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.ctpop.i256(i256 %v) + ret i256 %res +} + +define i256 @ctlztest(i256 %v) { +; CHECK-LABEL: ctlztest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 16 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 64 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SHR +; CHECK-NEXT: OR +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH32 454086624460063511464984254936031011189294057512315937409637584344757371137 +; CHECK-NEXT: PUSH16 20016609818878733144904388672456953615 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 129 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SHR +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 +; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.ctlz.i256(i256 %v, i1 false) + ret i256 %res +} + +define i256 @cttztest(i256 %v) { +; CHECK-LABEL: cttztest: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: NOT +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH32 454086624460063511464984254936031011189294057512315937409637584344757371137 +; CHECK-NEXT: PUSH16 20016609818878733144904388672456953615 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 129 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SHR +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 +; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP7 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: SHR +; CHECK-NEXT: ADD +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + + %res = call i256 @llvm.cttz.i256(i256 %v, i1 false) + ret i256 %res +} From 91e689328761d87b9e1e15419401ed0b9b8880bb Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 10 Apr 2025 12:14:55 +0200 Subject: [PATCH 116/203] [EVM] Remove 'assert(Sym.isDefined())' from evaluateTargetFixup This change ensures consistent error messages for undefined symbols across EraVM and EVM targets. If a symbol is undefined, getSymbolOffset will raise the error: "unable to evaluate offset to undefined symbol." --- llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp | 3 ++- llvm/test/MC/EVM/undefined-symbol-error.ll | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 llvm/test/MC/EVM/undefined-symbol-error.ll diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp index adb641928711..a28bcaf4ee29 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMAsmBackend.cpp @@ -139,7 +139,8 @@ bool EVMAsmBackend::evaluateTargetFixup(const MCAssembler &Asm, if (const MCSymbolRefExpr *A = Target.getSymA()) { const MCSymbol &Sym = A->getSymbol(); - assert(Sym.isDefined()); + // If 'Sym' is undefined, 'getSymbolOffset' will + // raise 'unable to evaluate offset to undefined symbol' error. Value += Asm.getSymbolOffset(Sym); return true; } diff --git a/llvm/test/MC/EVM/undefined-symbol-error.ll b/llvm/test/MC/EVM/undefined-symbol-error.ll new file mode 100644 index 000000000000..af3e7ab4478a --- /dev/null +++ b/llvm/test/MC/EVM/undefined-symbol-error.ll @@ -0,0 +1,13 @@ +; RUN: not --crash llc -filetype=obj --mtriple=evm %s 2>&1 | FileCheck %s + +; CHECK: LLVM ERROR: unable to evaluate offset to undefined symbol 'foo' + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @foo() + +define void @bar() { + call void @foo() + ret void +} From 5ddf8013ecee3a524191e3c35f983324f7c291e4 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 11 Apr 2025 17:00:18 +0200 Subject: [PATCH 117/203] [EVM] Add 'INVALID' instruction at the end of .text section This is to mimic 'solc' behaviour, which adds the instruction at the end of deploy/runtime code. --- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 19 +++++++++++++++++++ llvm/test/MC/EVM/invalid-instr.ll | 15 +++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 llvm/test/MC/EVM/invalid-instr.ll diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index d6fcb596a519..c872edba21b4 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -315,6 +315,25 @@ void EVMAsmPrinter::emitWideRelocatableSymbol(const MachineInstr *MI) { } void EVMAsmPrinter::emitEndOfAsmFile(Module &) { + + // The deploy and runtime code must end with INVALID instruction to + // comply with 'solc'. To ensure this, we append an INVALID + // instruction at the end of the .text section. + OutStreamer->pushSection(); + OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection()); + MCInst MCI; + MCI.setOpcode(EVM::INVALID_S); + + // Construct a local MCSubtargetInfo to use the streamer, as MachineFunction + // is not available here. Since we're at the global level we can use the + // default constructed subtarget. + std::unique_ptr STI(TM.getTarget().createMCSubtargetInfo( + TM.getTargetTriple().str(), TM.getTargetCPU(), + TM.getTargetFeatureString())); + + OutStreamer->emitInstruction(MCI, *STI); + OutStreamer->popSection(); + WideRelocSymbolsSet.clear(); ImmutablesMap.clear(); ModuleHasPushDeployAddress = false; diff --git a/llvm/test/MC/EVM/invalid-instr.ll b/llvm/test/MC/EVM/invalid-instr.ll new file mode 100644 index 000000000000..d4acef60d8a0 --- /dev/null +++ b/llvm/test/MC/EVM/invalid-instr.ll @@ -0,0 +1,15 @@ +; RUN: llc -O2 -filetype=obj --mtriple=evm %s -o - | llvm-objdump --no-leading-addr --disassemble - | FileCheck %s + +; CHECK-LABEL: : +; CHECK: fe INVALID + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @test() noreturn { +entry: + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} From 0de8431bb42429c596b571d4a7c210eb714d2506 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 11 Apr 2025 17:17:19 +0200 Subject: [PATCH 118/203] [EVM] Support arbitrary metadata This patch adds the LLVMAddMetadata API function, shared by both EraVM and EVM targets, which inserts a .metadata section into an ELF file. The LLVMAssembleEVM function retrieves the metadata section (if present) from the first object file and appends it to the .text section of the resulting ELF file. --- lld/lld-c/LLDAsLibraryC.cpp | 4 + lld/unittests/EVM/CMakeLists.txt | 1 + lld/unittests/EVM/LLDTest.cpp | 30 +++++++- llvm/include/llvm-c/ObjCopy.h | 31 ++++++++ llvm/lib/ObjCopy/CMakeLists.txt | 2 + llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt | 14 ++++ llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp | 96 ++++++++++++++++++++++++ 7 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 llvm/include/llvm-c/ObjCopy.h create mode 100644 llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt create mode 100644 llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp index 9d6e0d4633ee..5abf00c6574b 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -392,6 +392,7 @@ static void getSymbolNameSections(const ObjectFile *oFile, const char *fileID, /// __$b5e66d52578d$__(.text); /// __dataoffset__$12c297efd8baf$__ = ABSOLUTE(.); /// __$12c297efd8baf$__(.text); +/// __$b5e66d52578d$__(.metadata) /// __datasize__$b5e66d52578d$__ = ABSOLUTE(.); /// } /// /DISCARD/ : { @@ -474,6 +475,9 @@ static std::string createEVMAssembeScript(ArrayRef memBufs, textSection << bufIdHash << "(.text);\n"; } + // Append matadata section of the first object (if any). + textSection << EVM::getLinkerSymbolHash(depIDs[0]) << "(.metadata);\n"; + // Define a symbol whose value is the total size of the output // '.text' section. std::string firstDataSizeSym = getDataSizeName(depIDs[0]); diff --git a/lld/unittests/EVM/CMakeLists.txt b/lld/unittests/EVM/CMakeLists.txt index a0bbb663b407..6514cde4f0f4 100644 --- a/lld/unittests/EVM/CMakeLists.txt +++ b/lld/unittests/EVM/CMakeLists.txt @@ -12,5 +12,6 @@ target_link_libraries(EVMLLDTests LLVMEVMInfo LLVMEVMDesc LLVMIRReader + LLVMObjCopyC LLVMTarget ) diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp index fecf4fbcf5d7..933fb3d5529d 100644 --- a/lld/unittests/EVM/LLDTest.cpp +++ b/lld/unittests/EVM/LLDTest.cpp @@ -9,6 +9,7 @@ #include "lld-c/LLDAsLibraryC.h" #include "llvm-c/Core.h" #include "llvm-c/IRReader.h" +#include "llvm-c/ObjCopy.h" #include "llvm-c/TargetMachine.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FileSystem.h" @@ -75,6 +76,17 @@ class LLDCTest : public testing::Test { return OutAssembly; } + LLVMMemoryBufferRef addmd(LLVMMemoryBufferRef InObj) { + char *ErrMsg = nullptr; + LLVMMemoryBufferRef Out; + if (LLVMAddMetadata(InObj, MD.data(), MD.size(), &Out, &ErrMsg)) { + LLVMDisposeMessage(ErrMsg); + exit(1); + } + + return Out; + } + LLVMMemoryBufferRef assemble(uint64_t codeSegment, const std::vector &Objs, const std::vector &IDs) { @@ -117,6 +129,10 @@ class LLDCTest : public testing::Test { LLVMTargetMachineRef TM; LLVMContextRef Context; + std::array MD = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20}; }; TEST_F(LLDCTest, IterativeLinkage) { @@ -245,23 +261,28 @@ TEST_F(LLDCTest, Assembly) { LLVMMemoryBufferRef R_deployed_obj = compileIR("R_deployed.ll"); // A assemble. + LLVMMemoryBufferRef A_deployed_obj_md = addmd(A_deployed_obj); LLVMMemoryBufferRef A_assembly_deployed = - assemble(/*codeSegment=*/1, {A_deployed_obj}, {"A_38_deployed"}); + assemble(/*codeSegment=*/1, {A_deployed_obj_md}, {"A_38_deployed"}); LLVMMemoryBufferRef A_assembly = - assemble(/*codeSegment=*/0, {A_deploy_obj, A_deployed_obj}, + assemble(/*codeSegment=*/0, {A_deploy_obj, A_assembly_deployed}, {"A_38", "A_38_deployed"}); // D assemble. + LLVMMemoryBufferRef D_deployed_obj_md = addmd(D_deployed_obj); + LLVMMemoryBufferRef D_assembly_deployed = + assemble(/*codeSegment=*/1, {D_deployed_obj_md}, {"D_38_deployed"}); LLVMMemoryBufferRef D_assembly = - assemble(/*codeSegment=*/0, {D_deploy_obj, D_deployed_obj}, + assemble(/*codeSegment=*/0, {D_deploy_obj, D_assembly_deployed}, {"D_51", "D_51_deployed"}); // R_deployed assemble. // A_assembly is not required here, but we add it intentionaly to check // that it will be ignored (the total number of library reference is 3). + LLVMMemoryBufferRef R_deployed_obj_md = addmd(R_deployed_obj); LLVMMemoryBufferRef R_deployed_assemble = assemble(/*codeSegment=*/1, - {R_deployed_obj, D_assembly, A_assembly, A_assembly_deployed}, + {R_deployed_obj_md, D_assembly, A_assembly, A_assembly_deployed}, {"R_107_deployed", "D_51", "A_38", "A_38.A_38_deployed"}); // R assemble. @@ -296,6 +317,7 @@ TEST_F(LLDCTest, Assembly) { StringRef LibAddr(LinkerSymbolVal[1], 20); EXPECT_TRUE(Binary.count(LibAddr) == 3); + EXPECT_TRUE(Binary.count(StringRef(MD.data(), MD.size())) == 5); LLVMDisposeMemoryBuffer(A_deploy_obj); LLVMDisposeMemoryBuffer(A_deployed_obj); diff --git a/llvm/include/llvm-c/ObjCopy.h b/llvm/include/llvm-c/ObjCopy.h new file mode 100644 index 000000000000..a48565cbcb0f --- /dev/null +++ b/llvm/include/llvm-c/ObjCopy.h @@ -0,0 +1,31 @@ +//==--------- ObjCopy.h - ObjCopy Public C Interface ----------*- C++ -*----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header declares the C interface for the llvm-objcopy functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_C_OBJCOPY_H +#define LLVM_C_OBJCOPY_H + +#include "llvm-c/ExternC.h" +#include "llvm-c/Types.h" + +LLVM_C_EXTERN_C_BEGIN + +/** Creates new section '.metadata' in the input object file \p InBuffer + * and puts there the data pointed to by the \p MetadataPtr. The result is + * returned via \p OutBuffer. In case of an error the function returns 'true' + * and an error message is passes via \p ErrorMessage. The message should be + * disposed by LLVMDisposeMessage. **/ +LLVMBool LLVMAddMetadata(LLVMMemoryBufferRef InBuffer, const char *MetadataPtr, + uint64_t MetadataSize, LLVMMemoryBufferRef *OutBuffer, + char **ErrorMessage); +LLVM_C_EXTERN_C_END + +#endif // LLVM_C_OBJCOPY_H diff --git a/llvm/lib/ObjCopy/CMakeLists.txt b/llvm/lib/ObjCopy/CMakeLists.txt index 2d6bee94875f..ce9e6faaeb0d 100644 --- a/llvm/lib/ObjCopy/CMakeLists.txt +++ b/llvm/lib/ObjCopy/CMakeLists.txt @@ -81,3 +81,5 @@ add_llvm_component_library(LLVMObjCopy Support MC ) + +add_subdirectory(ObjCopyC) diff --git a/llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt b/llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt new file mode 100644 index 000000000000..271f8e40c26f --- /dev/null +++ b/llvm/lib/ObjCopy/ObjCopyC/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_component_library(LLVMObjCopyC + ObjCopyC.cpp + + DEPENDS + intrinsics_gen + + LINK_COMPONENTS + BinaryFormat + Core + MC + ObjCopy + Object + Support +) diff --git a/llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp b/llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp new file mode 100644 index 000000000000..20a0dae6d18e --- /dev/null +++ b/llvm/lib/ObjCopy/ObjCopyC/ObjCopyC.cpp @@ -0,0 +1,96 @@ +//===-------- ObjCopyC.cpp - ObjCopy Public C Interface ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the C interface for the llv-objcopy functionality. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/Core.h" +#include "llvm-c/ObjCopy.h" +#include "llvm-c/Object.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include + +using namespace llvm; +using namespace object; +using namespace objcopy; + +#ifndef NDEBUG +static void checkSectionData(ObjectFile &File, StringRef SectionName, + StringRef SectionData) { + for (const object::SectionRef &Sec : File.sections()) { + StringRef CurSecName = cantFail(Sec.getName()); + if (CurSecName == SectionName) { + StringRef CurSecData = cantFail(Sec.getContents()); + assert(Sec.getSize() == SectionData.size()); + assert(memcmp(CurSecData.data(), SectionData.data(), + SectionData.size()) == 0); + } + } +} +#endif // NDEBUG + +LLVMBool LLVMAddMetadata(LLVMMemoryBufferRef InBuffer, const char *MetadataPtr, + uint64_t MetadataSize, LLVMMemoryBufferRef *OutBuffer, + char **ErrorMessage) { + if (!MetadataSize) { + *OutBuffer = nullptr; + return false; + } + + StringRef MDSectionName = ".metadata"; + + std::unique_ptr MDSectionBuffer = MemoryBuffer::getMemBuffer( + StringRef(MetadataPtr, MetadataSize), MDSectionName, false); + + Expected> InObjOrErr( + createBinary(unwrap(InBuffer)->getMemBufferRef())); + if (!InObjOrErr) + llvm_unreachable("Cannot create Binary object from the memory buffer"); + + ConfigManager Config; + Config.Common.AddSection.emplace_back(MDSectionName, + std::move(MDSectionBuffer)); + + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + SectionFlagsUpdate SFU = {MDSectionName, SecReadonly | SecNoload}; + [[maybe_unused]] auto It = + Config.Common.SetSectionFlags.try_emplace(SFU.Name, SFU); + assert(It.second); + + SmallString<0> BufferString; + raw_svector_ostream OutStream(BufferString); + if (Error Err = objcopy::executeObjcopyOnBinary(Config, *InObjOrErr.get(), + OutStream)) { + *ErrorMessage = strdup(toString(std::move(Err)).c_str()); + return true; + } + + // Create output buffer and copy there the object code. + llvm::StringRef Data = BufferString.str(); + *OutBuffer = LLVMCreateMemoryBufferWithMemoryRangeCopy(Data.data(), + Data.size(), "result"); + +#ifndef NDEBUG + // Check that copied file has the new section. + Expected> Result = + createBinary(llvm::unwrap(*OutBuffer)->getMemBufferRef()); + assert(Result && (*Result)->isObject()); + checkSectionData(*static_cast((*Result).get()), MDSectionName, + StringRef(MetadataPtr, MetadataSize)); +#endif // NDEBUG + + return false; +} From 021f066ecdef83a9e1c2cb184c4f879fceba3225 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 10 Apr 2025 18:51:19 +0200 Subject: [PATCH 119/203] [EVM][BPS] Fix EVMStackSolver::compressStack to not removing inaccessible copies --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 2 +- .../CodeGen/EVM/bps-compress-stack-bug.ll | 164 ++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 9cf3303c423f..b41e18f8a7b8 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -540,7 +540,7 @@ Stack EVMStackSolver::compressStack(Stack CurStack) { return true; if (auto DistanceToCopy = offset(make_range(std::next(I), CurStack.rend()), *I)) - return (RIdx + *DistanceToCopy <= StackModel.stackDepthLimit()); + return (RIdx + *DistanceToCopy < StackModel.stackDepthLimit()); return false; }; for (auto I = CurStack.rbegin(); I != CurStack.rend();) { diff --git a/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll b/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll new file mode 100644 index 000000000000..859a92bfa4f5 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll @@ -0,0 +1,164 @@ +; RUN: llc < %s + +; calldata_struct_array_reencode test reduced with bugpoint. +; before the fix the test caused stack too deep failure because of an incorrect +; depth check in EVMStackSolver::compressStack. +source_filename = "era-solidity/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_array_reencode.sol:C.runtime" +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.calldatasize() #0 + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.callvalue() #0 + +; 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: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: null_pointer_is_valid +define void @__entry() local_unnamed_addr #3 { +entry: + switch i32 poison, label %if_join [ + i32 14668030, label %switch_case_branch_1_block + i32 594809185, label %switch_case_branch_2_block + i32 655224780, label %switch_case_branch_3_block + i32 1501966044, label %switch_case_branch_4_block + i32 1931402874, label %switch_case_branch_5_block + i32 -1789535483, label %switch_case_branch_6_block + i32 -1503295334, label %switch_case_branch_7_block + ] + +if_join: ; preds = %switch_case_branch_6_block, %calldata_access_struct_D_calldata.exit, %for_body335, %for_body296, %entry + unreachable + +switch_case_branch_1_block: ; preds = %entry + unreachable + +switch_case_branch_2_block: ; preds = %entry + unreachable + +switch_case_branch_3_block: ; preds = %entry + %calldata_load_result264 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %addition_result273 = add nuw nsw i256 %calldata_load_result264, 4 + %calldataload_pointer.i853 = inttoptr i256 %addition_result273 to ptr addrspace(2) + %calldata_load_result.i854 = load i256, ptr addrspace(2) %calldataload_pointer.i853, align 1 + %addition_result16.i856 = add nuw nsw i256 %calldata_load_result264, 36 + br i1 poison, label %for_join298, label %for_body296.lr.ph + +for_body296.lr.ph: ; preds = %switch_case_branch_3_block + %reass.sub.i878 = add i256 0, -31 + br label %for_body296 + +for_body296: ; preds = %for_join337, %for_body296.lr.ph + %pos_3.0952 = phi i256 [ 224, %for_body296.lr.ph ], [ %addition_result362, %for_join337 ] + %tail_4.0951 = phi i256 [ poison, %for_body296.lr.ph ], [ %tail_5.0.lcssa, %for_join337 ] + %srcPtr_3.0950 = phi i256 [ %addition_result16.i856, %for_body296.lr.ph ], [ 0, %for_join337 ] + %i_3.0949 = phi i256 [ 0, %for_body296.lr.ph ], [ %addition_result364, %for_join337 ] + %memory_store_pointer309 = inttoptr i256 %pos_3.0952 to ptr addrspace(1) + %calldataload_pointer.i858 = inttoptr i256 %srcPtr_3.0950 to ptr addrspace(2) + %calldata_load_result.i859 = load i256, ptr addrspace(2) %calldataload_pointer.i858, align 1 + %comparison_result.not.i861 = icmp slt i256 %calldata_load_result.i859, poison + br i1 %comparison_result.not.i861, label %if_join.i863, label %if_join + +if_join.i863: ; preds = %for_body296 + %addition_result8.i = add i256 %calldata_load_result.i859, %addition_result16.i856 + %calldataload_pointer10.i = inttoptr i256 %addition_result8.i to ptr addrspace(2) + %calldata_load_result11.i = load i256, ptr addrspace(2) %calldataload_pointer10.i, align 1 + %addition_result13.i = add i256 %addition_result8.i, 32 + br i1 poison, label %for_join337, label %for_body335.lr.ph + +for_body335.lr.ph: ; preds = %if_join.i863 + %addition_result.i871 = add i256 0, -63 + br label %for_body335 + +for_join298: ; preds = %for_join337, %switch_case_branch_3_block + %tail_4.0.lcssa = phi i256 [ poison, %switch_case_branch_3_block ], [ %tail_5.0.lcssa, %for_join337 ] + %addition_result369 = add i256 %tail_4.0.lcssa, -160 + store i256 %addition_result369, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + unreachable + +for_body335: ; preds = %if_join.i.i879, %for_body335.lr.ph + %pos_4.0.in946 = phi i256 [ %tail_4.0951, %for_body335.lr.ph ], [ %pos_4.0, %if_join.i.i879 ] + %tail_5.0945 = phi i256 [ poison, %for_body335.lr.ph ], [ %addition_result19.i, %if_join.i.i879 ] + %srcPtr_4.0944 = phi i256 [ %addition_result13.i, %for_body335.lr.ph ], [ %addition_result353, %if_join.i.i879 ] + %i_4.0943 = phi i256 [ 0, %for_body335.lr.ph ], [ %addition_result357, %if_join.i.i879 ] + %pos_4.0 = add i256 %pos_4.0.in946, 32 + %reass.sub = sub i256 %tail_5.0945, %tail_4.0951 + %addition_result346 = add i256 %reass.sub, -32 + %memory_store_pointer348 = inttoptr i256 %pos_4.0 to ptr addrspace(1) + store i256 %addition_result346, ptr addrspace(1) %memory_store_pointer348, align 1 + %calldataload_pointer.i867 = inttoptr i256 %srcPtr_4.0944 to ptr addrspace(2) + %calldata_load_result.i868 = load i256, ptr addrspace(2) %calldataload_pointer.i867, align 1 + %comparison_result.not.i872 = icmp slt i256 %calldata_load_result.i868, %addition_result.i871 + br i1 %comparison_result.not.i872, label %calldata_access_struct_D_calldata.exit, label %if_join + +calldata_access_struct_D_calldata.exit: ; preds = %for_body335 + %addition_result8.i875 = add i256 %calldata_load_result.i868, %addition_result13.i + %calldataload_pointer.i.i876 = inttoptr i256 %addition_result8.i875 to ptr addrspace(2) + %calldata_load_result.i.i877 = load i256, ptr addrspace(2) %calldataload_pointer.i.i876, align 1 + %addition_result.i.i = sub i256 %reass.sub.i878, %addition_result8.i875 + %comparison_result.not.i.i = icmp slt i256 %calldata_load_result.i.i877, %addition_result.i.i + br i1 %comparison_result.not.i.i, label %if_join.i.i879, label %if_join + +if_join.i.i879: ; preds = %calldata_access_struct_D_calldata.exit + %addition_result8.i.i = add i256 %calldata_load_result.i.i877, %addition_result8.i875 + %calldataload_pointer10.i.i = inttoptr i256 %addition_result8.i.i to ptr addrspace(2) + %calldata_load_result11.i.i = load i256, ptr addrspace(2) %calldataload_pointer10.i.i, align 1 + %shift_left_non_overflow_result.i.i880 = shl nuw nsw i256 %calldata_load_result11.i.i, 5 + %addition_result15.i = add i256 %tail_5.0945, 64 + %addition_result19.i = add i256 %shift_left_non_overflow_result.i.i880, %addition_result15.i + %addition_result353 = add i256 %srcPtr_4.0944, 32 + %addition_result357 = add nuw nsw i256 %i_4.0943, 1 + %comparison_result340 = icmp ult i256 %addition_result357, %calldata_load_result11.i + br i1 %comparison_result340, label %for_body335, label %for_join337 + +for_join337: ; preds = %if_join.i.i879, %if_join.i863 + %tail_5.0.lcssa = phi i256 [ poison, %if_join.i863 ], [ %addition_result19.i, %if_join.i.i879 ] + %addition_result362 = add i256 %pos_3.0952, 32 + %addition_result364 = add nuw nsw i256 %i_3.0949, 1 + %comparison_result301 = icmp ult i256 %addition_result364, %calldata_load_result.i854 + br i1 %comparison_result301, label %for_body296, label %for_join298 + +switch_case_branch_4_block: ; preds = %entry + unreachable + +switch_case_branch_5_block: ; preds = %entry + unreachable + +switch_case_branch_6_block: ; preds = %entry + br label %if_join + +switch_case_branch_7_block: ; preds = %entry + unreachable +} + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @finalize_allocation() unnamed_addr #3 + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @abi_decode_array_struct_S_dyn() unnamed_addr #3 + +; Function Attrs: memory(readwrite, inaccessiblemem: none) +declare dso_local fastcc void @abi_encode_bytes() unnamed_addr #4 + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @abi_encode_struct_D_calldata() unnamed_addr #3 + +; Function Attrs: null_pointer_is_valid +declare dso_local fastcc void @abi_encode_array_struct_D_calldata_calldata() unnamed_addr #3 + +attributes #0 = { nounwind willreturn memory(none) } +attributes #1 = { noreturn nounwind } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { null_pointer_is_valid } +attributes #4 = { memory(readwrite, inaccessiblemem: none) } From fcbe6953f28246c11379281d89c0c82ab35ca43d Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Sun, 13 Apr 2025 16:09:39 +0200 Subject: [PATCH 120/203] [EVM][BPS] Make EVMStackSolver to detect stack-too-deep errors properly --- llvm/lib/Target/EVM/EVMStackModel.h | 4 +- llvm/lib/Target/EVM/EVMStackSolver.cpp | 52 +++-- llvm/lib/Target/EVM/EVMStackSolver.h | 5 +- llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll | 178 ++++++++++++++++++ 4 files changed, 223 insertions(+), 16 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h index 81348bfa1997..c71ddbb76e0a 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.h +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -174,7 +174,9 @@ class UnknownSlot final : public StackSlot { size_t getIndex() const { return Index; } bool isRematerializable() const override { return true; } - std::string toString() const override { return "UNKNOWN"; } + std::string toString() const override { + return "UNKNOWN(" + std::to_string(Index) + ")"; + } static bool classof(const StackSlot *S) { return S->getSlotKind() == SK_Unknown; diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index b41e18f8a7b8..5cab3dd3964f 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -51,6 +51,12 @@ std::optional add(std::optional a, std::optional b) { return std::nullopt; } +#ifndef NDEBUG +std::string getMBBId(const MachineBasicBlock *MBB) { + return std::to_string(MBB->getNumber()) + "." + std::string(MBB->getName()); +} +#endif + /// Given stack \p AfterInst, compute stack before the instruction excluding /// instruction input operands. /// \param CompressStack: remove duplicates and rematerializable slots. @@ -208,9 +214,13 @@ void EVMStackSolver::run() { }); } -Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, - const MachineInstr &MI, - bool CompressStack) { +std::pair +EVMStackSolver::propagateThroughMI(const Stack &ExitStack, + const MachineInstr &MI, bool CompressStack) { + LLVM_DEBUG({ + dbgs() << "\tMI: "; + MI.dump(); + }); const Stack MIDefs = StackModel.getSlotsForInstructionDefs(&MI); // Stack before MI except for MI inputs. Stack BeforeMI = calculateStackBeforeInst(MIDefs, ExitStack, CompressStack, @@ -223,10 +233,19 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, assert(!MI.definesRegister(RegSlot->getReg(), /*TRI=*/nullptr)); #endif // NDEBUG + // Check the exit stack from the MI can be smoothly transformed + // to the entry stack of the next MI. + Stack AfterMI = BeforeMI; + AfterMI.append(MIDefs); + bool Err = !calculateStackTransformCost(AfterMI, ExitStack, + StackModel.stackDepthLimit()); + BeforeMI.append(StackModel.getMIInput(MI)); // Store computed stack to StackModel. insertInstEntryStack(&MI, BeforeMI); + LLVM_DEBUG({ dbgs() << "\tstack before: " << BeforeMI.toString() << ".\n"; }); + // If the top stack slots can be rematerialized just before MI, remove it // from propagation to reduce pressure. while (!BeforeMI.empty()) { @@ -242,21 +261,32 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, break; } - return BeforeMI; + return {BeforeMI, Err}; } Stack EVMStackSolver::propagateThroughMBB(const Stack &ExitStack, const MachineBasicBlock *MBB, bool CompressStack) { Stack CurrentStack = ExitStack; + + LLVM_DEBUG({ + dbgs() << "EVMStackSolver: start back propagating stack through " + << getMBBId(MBB) + << " (CompressStack=" << (CompressStack ? "true" : "false") + << "):\n"; + }); for (const auto &MI : StackModel.reverseInstructionsToProcess(MBB)) { - Stack BeforeMI = propagateThroughMI(CurrentStack, MI, CompressStack); - if (!CompressStack && - !calculateStackTransformCost(BeforeMI, CurrentStack, - StackModel.stackDepthLimit())) + auto [BeforeMI, Err] = propagateThroughMI(CurrentStack, MI, CompressStack); + CurrentStack = std::move(BeforeMI); + + if (!CompressStack && Err) { + LLVM_DEBUG({ + dbgs() << "\terror: stack-too-deep detected, trying to rerun with " + "Compressstack=true.\n"; + }); return propagateThroughMBB(ExitStack, MBB, /*CompressStack*/ true); - CurrentStack = std::move(BeforeMI); + } } return CurrentStack; } @@ -556,10 +586,6 @@ Stack EVMStackSolver::compressStack(Stack CurStack) { } #ifndef NDEBUG -static std::string getMBBId(const MachineBasicBlock *MBB) { - return std::to_string(MBB->getNumber()) + "." + std::string(MBB->getName()); -} - void EVMStackSolver::dump(raw_ostream &OS) { OS << "Function: " << MF.getName() << "("; for (const StackSlot *ParamSlot : StackModel.getFunctionParameters()) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h index 7b604ebdc944..716e461dde18 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.h +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -45,8 +45,9 @@ class EVMStackSolver { /// minimal. /// Side effect: the computed entry stack is stored in StackModel. /// \param CompressStack: remove duplicates and rematerializable slots. - Stack propagateThroughMI(const Stack &ExitStack, const MachineInstr &MI, - bool CompressStack = false); + std::pair propagateThroughMI(const Stack &ExitStack, + const MachineInstr &MI, + bool CompressStack = false); /// Given \p ExitStack, compute the stack at the entry of \p MBB. /// \param CompressStack: remove duplicates and rematerializable slots. diff --git a/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll b/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll new file mode 100644 index 000000000000..3377869f2596 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll @@ -0,0 +1,178 @@ +; RUN: llc < %s + +; calldata_struct_array_reencode test reduced with bugpoint. +; before the fix the test caused stack too deep failure because of unreachable +; slots mishandling in EVMStackSolver. +source_filename = "era-solidity/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_array_reencode.sol:C.runtime" +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.calldatasize() #0 + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.callvalue() #0 + +; 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: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #2 + +; Function Attrs: null_pointer_is_valid +define dso_local fastcc void @main() unnamed_addr #3 { +entry: + br i1 poison, label %"block_rt_7/0", label %"block_rt_2/0" + +"block_rt_2/0": ; preds = %conditional_rt_181_join_block, %"block_rt_165/1", %entry + unreachable + +"block_rt_7/0": ; preds = %entry + %calldata_load_result2781 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %calldata_load_result2605 = load i256, ptr addrspace(2) poison, align 1 + %addition_result2659 = add nuw nsw i256 %calldata_load_result2781, 36 + %subtraction_result1906 = add i256 0, -31 + br label %conditional_rt_187_join_block + +"block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = add i256 0, %addition_result4054 + %addition_result3239 = add i256 0, %addition_result3756 + %addition_result3251 = add i256 %addition_result3239, 32 + %comparison_result3399.not = icmp sgt i256 %addition_result3251, poison + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %shift_left_join3672 + +"block_rt_181/0": ; preds = %shift_left_join3672 + %addition_result4058 = add i256 %stack_var_012.36954, 32 + %addition_result4064 = add i256 %stack_var_011.36953, 32 + %addition_result4044 = add nuw i256 %stack_var_013.36955, 1 + %comparison_result4003.not = icmp ult i256 %addition_result4044, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + +"block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + unreachable + +shift_left_join3672: ; preds = %"block_rt_165/1" + %addition_result3684 = add i256 %stack_var_021.0.in6947, 128 + %calldatacopy_destination_pointer3688 = inttoptr i256 %addition_result3684 to ptr addrspace(1) + tail call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) align 1 %calldatacopy_destination_pointer3688, ptr addrspace(2) align 1 poison, i256 poison, i1 false) + %addition_result3694 = add i256 poison, %stack_var_021.06950 + %addition_result3972 = add i256 %stack_var_022.06948, 32 + %addition_result3978 = add i256 %stack_var_017.26946, 32 + %stack_var_021.0 = add i256 %addition_result3694, 64 + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + +conditional_rt_181_join_block: ; preds = %conditional_rt_187_join_block, %shift_left_join3672 + %stack_var_021.06950 = phi i256 [ poison, %conditional_rt_187_join_block ], [ %stack_var_021.0, %shift_left_join3672 ] + %comparison_result3909.not = phi i1 [ true, %conditional_rt_187_join_block ], [ false, %shift_left_join3672 ] + %stack_var_022.06948 = phi i256 [ %addition_result4054, %conditional_rt_187_join_block ], [ %addition_result3972, %shift_left_join3672 ] + %stack_var_021.0.in6947 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3694, %shift_left_join3672 ] + %stack_var_017.26946 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3978, %shift_left_join3672 ] + %subtraction_result3918 = sub i256 %stack_var_021.06950, %stack_var_010.26952 + %memory_store_pointer3922 = inttoptr i256 %stack_var_017.26946 to ptr addrspace(1) + store i256 %subtraction_result3918, ptr addrspace(1) %memory_store_pointer3922, align 1 + %calldataload_pointer1898 = inttoptr i256 %stack_var_022.06948 to ptr addrspace(2) + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + +conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %"block_rt_7/0" + %stack_var_013.36955 = phi i256 [ 0, %"block_rt_7/0" ], [ %addition_result4044, %"block_rt_181/0" ] + %stack_var_012.36954 = phi i256 [ %addition_result2659, %"block_rt_7/0" ], [ %addition_result4058, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 224, %"block_rt_7/0" ], [ %addition_result4064, %"block_rt_181/0" ] + %stack_var_010.26952 = phi i256 [ poison, %"block_rt_7/0" ], [ %stack_var_021.0, %"block_rt_181/0" ] + %memory_store_pointer4021 = inttoptr i256 %stack_var_011.36953 to ptr addrspace(1) + %calldataload_pointer4024 = inttoptr i256 %stack_var_012.36954 to ptr addrspace(2) + %calldata_load_result4025 = load i256, ptr addrspace(2) %calldataload_pointer4024, align 1 + %addition_result4054 = add i256 %calldata_load_result4025, %addition_result2659 + %addition_result1909 = sub i256 %subtraction_result1906, %addition_result4054 + br label %conditional_rt_181_join_block +} + +; Function Attrs: null_pointer_is_valid +define dso_local fastcc void @main2() unnamed_addr #3 { +entry: + switch i32 poison, label %"block_rt_2/0" [ + i32 1931402874, label %"block_rt_7/0" + ] + +"block_rt_2/0": ; preds = %conditional_rt_181_join_block, %"block_rt_165/1", %entry + unreachable + +"block_rt_7/0": ; preds = %entry + %calldata_load_result2781 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %addition_result2798 = add nuw nsw i256 %calldata_load_result2781, 4 + %calldataload_pointer2604 = inttoptr i256 %addition_result2798 to ptr addrspace(2) + %calldata_load_result2605 = load i256, ptr addrspace(2) %calldataload_pointer2604, align 1 + %addition_result2659 = add nuw nsw i256 %calldata_load_result2781, 36 + %subtraction_result1906 = add i256 0, -31 + br label %conditional_rt_187_join_block + +"block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = add i256 %calldata_load_result1899, %addition_result4054 + %addition_result3239 = add i256 0, %addition_result3756 + %calldataload_pointer3244 = inttoptr i256 %addition_result3239 to ptr addrspace(2) + %calldata_load_result3245 = load i256, ptr addrspace(2) %calldataload_pointer3244, align 1 + %addition_result3251 = add i256 %addition_result3239, 32 + %shift_left_non_overflow_result3390 = shl nuw nsw i256 %calldata_load_result3245, 5 + %comparison_result3399.not = icmp sgt i256 %addition_result3251, poison + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %shift_left_join3672 + +"block_rt_181/0": ; preds = %shift_left_join3672 + %addition_result4064 = add i256 %stack_var_011.36953, 32 + %addition_result4044 = add nuw i256 %stack_var_013.36955, 1 + %comparison_result4003.not = icmp ult i256 %addition_result4044, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + +"block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + %addition_result439 = add i256 %stack_var_021.0, 32 + %memory_store_pointer442 = inttoptr i256 %addition_result439 to ptr addrspace(1) + store i256 0, ptr addrspace(1) %memory_store_pointer442, align 1 + unreachable + +shift_left_join3672: ; preds = %"block_rt_165/1" + %addition_result3535 = add i256 %stack_var_021.0.in6947, 96 + %memory_store_pointer3538 = inttoptr i256 %addition_result3535 to ptr addrspace(1) + store i256 %calldata_load_result3245, ptr addrspace(1) %memory_store_pointer3538, align 1 + %addition_result3694 = add i256 %shift_left_non_overflow_result3390, %stack_var_021.06950 + %addition_result3972 = add i256 %stack_var_022.06948, 32 + %addition_result3978 = add i256 %stack_var_017.26946, 32 + %stack_var_021.0 = add i256 %addition_result3694, 64 + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + +conditional_rt_181_join_block: ; preds = %conditional_rt_187_join_block, %shift_left_join3672 + %stack_var_021.06950 = phi i256 [ poison, %conditional_rt_187_join_block ], [ %stack_var_021.0, %shift_left_join3672 ] + %comparison_result3909.not = phi i1 [ true, %conditional_rt_187_join_block ], [ false, %shift_left_join3672 ] + %stack_var_022.06948 = phi i256 [ %addition_result4054, %conditional_rt_187_join_block ], [ %addition_result3972, %shift_left_join3672 ] + %stack_var_021.0.in6947 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3694, %shift_left_join3672 ] + %stack_var_017.26946 = phi i256 [ %stack_var_010.26952, %conditional_rt_187_join_block ], [ %addition_result3978, %shift_left_join3672 ] + %subtraction_result3918 = sub i256 %stack_var_021.06950, %stack_var_010.26952 + %memory_store_pointer3922 = inttoptr i256 %stack_var_017.26946 to ptr addrspace(1) + store i256 %subtraction_result3918, ptr addrspace(1) %memory_store_pointer3922, align 1 + %calldataload_pointer1898 = inttoptr i256 %stack_var_022.06948 to ptr addrspace(2) + %calldata_load_result1899 = load i256, ptr addrspace(2) %calldataload_pointer1898, align 1 + %comparison_result1913.not = icmp slt i256 %calldata_load_result1899, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + +conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %"block_rt_7/0" + %stack_var_013.36955 = phi i256 [ 0, %"block_rt_7/0" ], [ %addition_result4044, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 224, %"block_rt_7/0" ], [ %addition_result4064, %"block_rt_181/0" ] + %stack_var_010.26952 = phi i256 [ poison, %"block_rt_7/0" ], [ %stack_var_021.0, %"block_rt_181/0" ] + %memory_store_pointer4021 = inttoptr i256 %stack_var_011.36953 to ptr addrspace(1) + %calldata_load_result4025 = load i256, ptr addrspace(2) poison, align 1 + %addition_result4054 = add i256 %calldata_load_result4025, %addition_result2659 + %addition_result1909 = sub i256 %subtraction_result1906, %addition_result4054 + br label %conditional_rt_181_join_block +} + +; Function Attrs: null_pointer_is_valid +declare void @__entry() local_unnamed_addr #3 + +attributes #0 = { nounwind willreturn memory(none) } +attributes #1 = { noreturn nounwind } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { null_pointer_is_valid } From a19cde18da67310e0ba874955aa87bb5b4ad9ef1 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 29 Apr 2025 20:34:09 +0200 Subject: [PATCH 121/203] [EVM] Do not assert vector register in getRegisterBitWidth() --- llvm/lib/Target/EVM/EVMTargetTransformInfo.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h index 9903fe510a73..8e2147e9a8e6 100644 --- a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h @@ -56,8 +56,6 @@ class EVMTTIImpl final : public BasicTTIImplBase { } TypeSize getRegisterBitWidth(TargetTransformInfo::RegisterKind RK) const { - assert(RK == TargetTransformInfo::RGK_Scalar && - "Vector registers aren't supported"); return TypeSize::getFixed(256); } From 5db6b13ef507f573c8ea17f3af922d8184452163 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Fri, 11 Apr 2025 19:53:48 +0200 Subject: [PATCH 122/203] [EVM] Refine memory-related attributes of intrinsics Also remove 'pop' intrinsic, as it's not needed. Co-authored-by: Oleksandr Zarudnyi Co-authored-by: --- llvm/include/llvm/IR/IntrinsicsEVM.td | 137 ++++++++++++++++--------- llvm/lib/Analysis/MemoryLocation.cpp | 31 ++++-- llvm/lib/Target/EVM/EVMInstrInfo.td | 2 - llvm/lib/Target/EVM/EVMStackify.cpp | 3 +- llvm/test/CodeGen/EVM/aa-memory-opt.ll | 12 ++- llvm/test/CodeGen/EVM/cse-intrinsic.ll | 115 +++++++++++++++++---- 6 files changed, 217 insertions(+), 83 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index 3dccff349131..0eccab48feb3 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -23,6 +23,37 @@ def AS { let TargetPrefix = "evm" in { +// Ethereum Virtual Machine (EVM) opcodes and corresponding intrinsics can be grouped +// by the runtime effects i.e., how their behavior or outputs are affected by state +// changes during a transaction: +// +// - 'Readnone' +// These do not read or write any persistent world-state once execution begins. +// Their results depend only on provided inputs or fixed environment data +// (constant during the transaction). They have no lasting side effects and +// return the same result given the same inputs. +// +// - 'Volatile (State-Dependent)' +// These read data from the world state or execution environment that may change +// within the same transaction (for example, due to prior storage writes, value +// transfers, or external calls). Re-invoking them after a state mutation can +// yield a different result. They themselves do not permanently modify state. +// Such intrinsics have 'IntrInaccessibleMemOnly' attribute, or +// 'IntrReadMem, IntrInaccessibleMemOnly' attributes to model dependency from +// the State. +// NOTE: we assume (to be confirmed) that the heap is not part of the State; +// therefore, it cannot be modified in a volatile manner. +// +// - 'Side-Effecting (State-Changing)' +// These opcodes cause persistent changes in world state - writing to storage, +// emitting logs, creating or destroying contracts, or transferring Ether. +// Their execution produces lasting side effects recorded on-chain (storage +// updates, logs, new accounts, etc.). +// Such intrinsics have empty memory attributes list which means they can +// arbitrary change the State. + +// TODO: Further improvements of intrinsics' attributes, #807 + // Arithmetical operations. def int_evm_div : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; @@ -58,7 +89,8 @@ def int_evm_exp def int_evm_sha3 : Intrinsic<[llvm_i256_ty], - [LLVMQualPointerType, llvm_i256_ty], [IntrReadMem, IntrWillReturn]>; + [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem,IntrArgMemOnly, IntrWillReturn]>; def int_evm_signextend : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; @@ -68,17 +100,15 @@ def int_evm_byte [IntrNoMem, IntrWillReturn]>; // Memory operations. -def int_evm_mstore8 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], [IntrWriteMem]>; - -// TODO: CPR-1555. -// Review system intrinsics attributes taking into account EVM. -def int_evm_msize : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_mstore8 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrWriteMem, IntrArgMemOnly]>; -def int_evm_pc : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; +// Special getters that must not be relocated. +def int_evm_msize : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; -def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_pc : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; +def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; // Getting values from the context. def int_evm_address : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; @@ -87,31 +117,36 @@ def int_evm_origin : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_caller : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; def int_evm_callvalue : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_calldatasize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_calldataload - : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], []>; + : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], + [IntrReadMem, IntrArgMemOnly]>; def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -// TODO: Ensure the gasprice is fixed while a function execution. def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_extcodesize - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; +// This should have 'read of inaccessible' memory along with read/write memory accessed through +// its pointer-typed arguments. Since we can't express that precisely here, we represent +// it as read/write access to arbitrary memory. def int_evm_extcodecopy : Intrinsic<[], [llvm_i256_ty, LLVMQualPointerType, - LLVMQualPointerType, llvm_i256_ty], [IntrWriteMem]>; + LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; -def int_evm_returndatasize : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_returndatasize + : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; def int_evm_extcodehash - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; @@ -129,62 +164,73 @@ def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn] def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], [IntrInaccessibleMemOnly]>; +def int_evm_selfbalance + : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; // Logging. -def int_evm_log0 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], [IntrReadMem]>; - -def int_evm_log1 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], [IntrReadMem]>; - -def int_evm_log2 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty], [IntrReadMem]>; - -def int_evm_log3 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty], [IntrReadMem]>; - -def int_evm_log4 - : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], [IntrReadMem]>; +// Log intrinsics should have 'read/write of inaccessible' memory along with read memory +// accessed through its pointer-typed arguments. Since we can't express that precisely +// here, we represent it as read/write access to arbitrary memory. +// Note: Attempting to mark them with both IntrReadMem and IntrHasSideEffects won't work, +// because IntrHasSideEffects does not override IntrReadMem. As a result, intrinsics will +// lack the 'HasSideEffects' flag entirely, making them subject to DCE. +def int_evm_log0 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrWillReturn]>; + +def int_evm_log1 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], + [IntrWillReturn]>; + +def int_evm_log2 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty], + [IntrWillReturn]>; + +def int_evm_log3 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrWillReturn]>; + +def int_evm_log4 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrWillReturn]>; // System calls +// The above considerations also apply to xCALL intrinsics. def int_evm_create : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], []>; + [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [IntrWillReturn]>; def int_evm_call : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty], []>; + LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; def int_evm_callcode : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty], []>; + LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; def int_evm_delegatecall : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty], []>; + LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; def int_evm_create2 - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, LLVMQualPointerType, - llvm_i256_ty, llvm_i256_ty], []>; + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, llvm_i256_ty], [IntrWillReturn]>; def int_evm_staticcall : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, - llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], []>; + llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [IntrWillReturn]>; def int_evm_selfdestruct: Intrinsic<[], [llvm_i256_ty], []>; @@ -194,15 +240,14 @@ def int_evm_stop: Intrinsic<[], [], [IntrNoReturn]>; def int_evm_invalid: Intrinsic<[], [], [IntrNoReturn]>; // Return with error. +// These should ideally have the IntrReadMem attribute, but doing so causes DSE +// to incorrectly eliminate necessary stores. def int_evm_return: Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], [IntrNoReturn]>; def int_evm_revert: Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], [IntrNoReturn]>; -// Stack manipulation. -def int_evm_pop: Intrinsic<[], [llvm_i256_ty], [IntrNoReturn]>; - def int_evm_memmoveas1as1 : Intrinsic<[], [LLVMQualPointerType, LLVMQualPointerType, @@ -252,14 +297,14 @@ def int_evm_memcpyas1as4 // passed in the metadata. def int_evm_datasize : DefaultAttrsIntrinsic< [llvm_i256_ty], [llvm_metadata_ty], - [IntrNoMem, IntrWillReturn, IntrSpeculatable] + [IntrNoMem, IntrWillReturn] >; // 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, IntrWillReturn, IntrSpeculatable] + [IntrNoMem, IntrWillReturn] >; diff --git a/llvm/lib/Analysis/MemoryLocation.cpp b/llvm/lib/Analysis/MemoryLocation.cpp index 99cbd02a2365..b9cb22ffae2d 100644 --- a/llvm/lib/Analysis/MemoryLocation.cpp +++ b/llvm/lib/Analysis/MemoryLocation.cpp @@ -176,16 +176,25 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, // location. auto T = Call->getModule()->getTargetTriple(); if (Triple(T).isEVM()) { + // For EVM intrinsics, the memory size argument always immediately + // follows the memory argument, meaning its index is ArgIdx + 1. + auto GetMemLocation = [Call, Arg, &AATags](unsigned MemSizeArgIdx) { + const auto *LenCI = + dyn_cast(Call->getArgOperand(MemSizeArgIdx)); + if (LenCI && LenCI->getValue().getActiveBits() <= 64) + return MemoryLocation( + Arg, LocationSize::precise(LenCI->getZExtValue()), AATags); + return MemoryLocation::getAfter(Arg, AATags); + }; + switch (II->getIntrinsicID()) { case Intrinsic::memcpy: case Intrinsic::memcpy_inline: case Intrinsic::memmove: case Intrinsic::memset: if (ConstantInt *LenCI = dyn_cast(II->getArgOperand(2))) - if (LenCI->getValue().getActiveBits() > 64) { - return MemoryLocation::getBeforeOrAfter(Call->getArgOperand(ArgIdx), - AATags); - } + if (LenCI->getValue().getActiveBits() > 64) + return MemoryLocation::getAfter(Arg, AATags); break; case Intrinsic::lifetime_start: case Intrinsic::lifetime_end: @@ -193,11 +202,15 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, break; case Intrinsic::evm_sha3: { assert((ArgIdx == 0) && "Invalid argument index for sha3"); - const auto *LenCI = dyn_cast(Call->getArgOperand(1)); - if (LenCI && LenCI->getValue().getActiveBits() <= 64) - return MemoryLocation( - Arg, LocationSize::precise(LenCI->getZExtValue()), AATags); - return MemoryLocation::getAfter(Arg, AATags); + return GetMemLocation(1); + } + case Intrinsic::evm_mstore8: { + assert((ArgIdx == 0) && "Invalid argument index for mstore8"); + return MemoryLocation(Arg, LocationSize::precise(1), AATags); + } + case Intrinsic::evm_calldataload: { + assert((ArgIdx == 0) && "Invalid argument index for calldataload"); + return MemoryLocation(Arg, LocationSize::precise(32), AATags); } default: llvm_unreachable("Unexpected intrinsic for EVM target"); diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 7d41262e88b1..2f605e875ad8 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -797,8 +797,6 @@ let hasSideEffects = 1 in { // EVM instructions for stack manipulation. //===----------------------------------------------------------------------===// -def POP_KILL : EVMPseudo<(outs), (ins GPR:$val), [(int_evm_pop GPR:$val)]>; - defm POP : I<(outs), (ins), [], "POP", "", 0x50, 2>; foreach I = {1-16} in { diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp index 46da07061e20..7eb73c4da234 100644 --- a/llvm/lib/Target/EVM/EVMStackify.cpp +++ b/llvm/lib/Target/EVM/EVMStackify.cpp @@ -1023,8 +1023,7 @@ void StackModel::postProcess() { MI.removeOperand(0); if (Opc == EVM::PUSH0) MI.removeOperand(0); - } else if (MI.getOpcode() == EVM::COPY_I256 || - MI.getOpcode() == EVM::POP_KILL) { + } else if (MI.getOpcode() == EVM::COPY_I256) { // Just remove the copy instruction, as the code that // loads the argument and stores the result performs copying. // TODO: POP_KILL should be handled before stackification pass. diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll index 8fdbf224f60e..b0245969041c 100644 --- a/llvm/test/CodeGen/EVM/aa-memory-opt.ll +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -54,12 +54,13 @@ define i256 @test_different_locsizes() { } define i256 @test_gas() { -; CHECK-LABEL: define noundef i256 @test_gas +; CHECK-LABEL: define i256 @test_gas ; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() ; CHECK-NEXT: store i256 [[GAS]], ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 -; CHECK-NEXT: ret i256 2 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 [[RET]] ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 %gas = call i256 @llvm.evm.gas() @@ -69,11 +70,12 @@ define i256 @test_gas() { } define i256 @test_log0(ptr addrspace(1) %off, i256 %size) { -; CHECK-LABEL: define noundef i256 @test_log0 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +; CHECK-LABEL: define i256 @test_log0 +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) -; CHECK-NEXT: ret i256 2 +; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: ret i256 [[RET]] ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) diff --git a/llvm/test/CodeGen/EVM/cse-intrinsic.ll b/llvm/test/CodeGen/EVM/cse-intrinsic.ll index b833d99b5ba8..721235d95d97 100644 --- a/llvm/test/CodeGen/EVM/cse-intrinsic.ll +++ b/llvm/test/CodeGen/EVM/cse-intrinsic.ll @@ -237,10 +237,9 @@ define i256 @caller() nounwind { define i256 @balance(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @balance -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) -; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) -; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] ; %res1 = call i256 @llvm.evm.balance(i256 %rs1) @@ -249,6 +248,26 @@ define i256 @balance(i256 %rs1) nounwind { ret i256 %ret } +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @balance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @balance_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.balance(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.balance(i256 %addr) + %v2 = call i256 @llvm.evm.balance(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.balance(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + define i256 @calldatasize() nounwind { ; CHECK-LABEL: define i256 @calldatasize ; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { @@ -264,10 +283,9 @@ define i256 @calldatasize() nounwind { define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { ; CHECK-LABEL: define i256 @calldataload -; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) -; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) -; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] ; %res1 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) @@ -317,7 +335,7 @@ define i256 @gasprice() nounwind { define i256 @extcodesize(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @extcodesize -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -328,9 +346,29 @@ define i256 @extcodesize(i256 %rs1) nounwind { ret i256 %ret } +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @extcodesize_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @extcodesize_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.extcodesize(i256 %addr) + %v2 = call i256 @llvm.evm.extcodesize(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.extcodesize(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + define i256 @extcodehash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @extcodehash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -341,12 +379,31 @@ define i256 @extcodehash(i256 %rs1) nounwind { ret i256 %ret } +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @extcodehash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @extcodehash_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.extcodehash(i256 %addr) + %v2 = call i256 @llvm.evm.extcodehash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.extcodehash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + define i256 @returndatasize() nounwind { ; CHECK-LABEL: define i256 @returndatasize -; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.returndatasize() -; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.returndatasize() -; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] ; %res1 = call i256 @llvm.evm.returndatasize() @@ -461,10 +518,9 @@ define i256 @chainid() nounwind { define i256 @selfbalance() nounwind { ; CHECK-LABEL: define i256 @selfbalance -; CHECK-SAME: () local_unnamed_addr #[[ATTR5:[0-9]+]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.selfbalance() -; CHECK-NEXT: [[RES2:%.*]] = tail call i256 @llvm.evm.selfbalance() -; CHECK-NEXT: [[RET:%.*]] = add i256 [[RES2]], [[RES1]] +; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] ; %res1 = call i256 @llvm.evm.selfbalance() @@ -473,6 +529,26 @@ define i256 @selfbalance() nounwind { ret i256 %ret } +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @selfbalance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @selfbalance_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.selfbalance() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.selfbalance() + %v2 = call i256 @llvm.evm.selfbalance() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.selfbalance() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + define i256 @basefee() nounwind { ; CHECK-LABEL: define i256 @basefee ; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { @@ -501,7 +577,7 @@ define i256 @blobbasefee() nounwind { define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { ; CHECK-LABEL: define void @log0 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: ret void @@ -513,7 +589,7 @@ define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { ; CHECK-LABEL: define void @log1 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) ; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) ; CHECK-NEXT: ret void @@ -525,7 +601,7 @@ define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { ; CHECK-LABEL: define void @log2 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) ; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) ; CHECK-NEXT: ret void @@ -537,7 +613,7 @@ define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwin define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { ; CHECK-LABEL: define void @log3 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) ; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) ; CHECK-NEXT: ret void @@ -549,7 +625,7 @@ define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { ; CHECK-LABEL: define void @log4 -; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) ; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) ; CHECK-NEXT: ret void @@ -596,3 +672,4 @@ declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) From 69b16f7d8ac6db03e49a2b1291ead56d6277130e Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 30 Apr 2025 02:39:55 +0200 Subject: [PATCH 123/203] [EVM] Resolve a memory leak by disposing of the temporary buffer --- lld/lld-c/LLDAsLibraryC.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lld/lld-c/LLDAsLibraryC.cpp b/lld/lld-c/LLDAsLibraryC.cpp index 5abf00c6574b..f681e3205cc6 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -745,6 +745,7 @@ LLVMBool LLVMAssembleEVM(uint64_t codeSegment, } *outBuffer = removeLocalSymbols(Tmp, firstLoadImmutables); + LLVMDisposeMemoryBuffer(Tmp); return false; } From 22a8ab0bc2920b6a67c27e9868f9a4f5e898e6d0 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 30 Apr 2025 18:22:30 +0200 Subject: [PATCH 124/203] [EVM] Remove '--evm-keep-registers' from LIT tests Remove tests for unsupported features: globals and frame indexes. --- llvm/test/CodeGen/EVM/add.ll | 35 +- llvm/test/CodeGen/EVM/aext.ll | 14 +- llvm/test/CodeGen/EVM/br.ll | 65 ++- llvm/test/CodeGen/EVM/call.ll | 77 +-- llvm/test/CodeGen/EVM/div.ll | 32 +- llvm/test/CodeGen/EVM/frameidx.ll | 53 -- llvm/test/CodeGen/EVM/globals.ll | 89 --- llvm/test/CodeGen/EVM/icmp.ll | 64 ++- llvm/test/CodeGen/EVM/intrinsic.ll | 506 +++++++++++------- .../CodeGen/EVM/load-narrowing-disable.ll | 21 +- llvm/test/CodeGen/EVM/logical.ll | 44 +- llvm/test/CodeGen/EVM/mem_call_data.ll | 12 +- .../CodeGen/EVM/memintrinsics-no-expansion.ll | 4 +- llvm/test/CodeGen/EVM/memintrinsics-opt.ll | 1 + llvm/test/CodeGen/EVM/memintrinsics.ll | 83 +-- llvm/test/CodeGen/EVM/memory.ll | 30 +- llvm/test/CodeGen/EVM/mod.ll | 32 +- llvm/test/CodeGen/EVM/mul.ll | 28 +- .../EVM/no-mul-fold-to-overflow-int.ll | 2 +- llvm/test/CodeGen/EVM/select.ll | 38 +- llvm/test/CodeGen/EVM/sext.ll | 87 +-- .../test/CodeGen/EVM/sha3-constant-folding.ll | 2 +- llvm/test/CodeGen/EVM/shift.ll | 36 +- llvm/test/CodeGen/EVM/signextload.ll | 63 ++- llvm/test/CodeGen/EVM/stdlib-call-ellision.ll | 14 +- llvm/test/CodeGen/EVM/storage.ll | 21 +- llvm/test/CodeGen/EVM/sub.ll | 25 +- llvm/test/CodeGen/EVM/truncstore.ll | 51 +- llvm/test/CodeGen/EVM/tstorage.ll | 21 +- .../CodeGen/EVM/unused_function_arguments.ll | 64 +-- llvm/test/CodeGen/EVM/zero_any_extload.ll | 123 +++-- llvm/test/CodeGen/EVM/zext.ll | 53 +- 32 files changed, 1050 insertions(+), 740 deletions(-) delete mode 100644 llvm/test/CodeGen/EVM/frameidx.ll delete mode 100644 llvm/test/CodeGen/EVM/globals.ll diff --git a/llvm/test/CodeGen/EVM/add.ll b/llvm/test/CodeGen/EVM/add.ll index c20432a02b05..61d06295fa78 100644 --- a/llvm/test/CodeGen/EVM/add.ll +++ b/llvm/test/CodeGen/EVM/add.ll @@ -1,33 +1,42 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @addrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @addrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: addrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = add i256 %rs1, %rs2 ret i256 %res } define i256 @addrri(i256 %rs1) nounwind { -; CHECK-LABEL: @addrri -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C1:\$[0-9]+]], 18446744073709551616 -; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[C1]] +; CHECK-LABEL: addrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH9 18446744073709551616 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = add i256 %rs1, 18446744073709551616 ; 65-bits ret i256 %res } define i256 @subrri(i256 %rs1) nounwind { -; CHECK-LABEL: @subrri -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C1:\$[0-9]+]], 1 -; CHECK: ADD [[REG:\$[0-9]+]], [[IN1]], [[C1]] +; CHECK-LABEL: subrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sub i256 %rs1, -1 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/aext.ll b/llvm/test/CodeGen/EVM/aext.ll index 4d473cd3af8b..e3996638aebb 100644 --- a/llvm/test/CodeGen/EVM/aext.ll +++ b/llvm/test/CodeGen/EVM/aext.ll @@ -1,13 +1,17 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i8 @aexti8(i8 %rs1) nounwind { -; CHECK-LABEL: @aexti8 -; CHECK: ARGUMENT [[IN:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C:\$[0-9]+]], 1 -; CHECK: ADD {{.*}}, [[IN]], [[C]] +; CHECK-LABEL: aexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = add i8 %rs1, 1 ret i8 %res diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index f7538ca177f2..081b87bbc45c 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -1,15 +1,33 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: diamond +; CHECK-LABEL: diamond: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true_bb +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: ; %false_bb +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %cmp = icmp eq i256 %rs1, %rs2 -; CHECK: EQ -; CHECK: ISZERO -; CHECK: JUMPI br i1 %cmp, label %true_bb, label %false_bb true_bb: @@ -27,22 +45,51 @@ end_bb: define i256 @loop(i256 %p1) nounwind { -; CHECK-LABEL: loop +; CHECK-LABEL: loop: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: .BB1_1: ; %loop.cond +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.2: ; %loop.body +; CHECK-NEXT: ; in Loop: Header=BB1_1 Depth=1 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: DUP4 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB1_1 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_3: ; %loop.exit +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: JUMP entry: br label %loop.cond loop.cond: %i = phi i256 [0, %entry], [%i.next, %loop.body] %res = phi i256 [0, %entry], [%res.next, %loop.body] -; CHECK: EQ -; CHECK: JUMPI %cond = icmp ne i256 %i, %p1 br i1 %cond, label %loop.body, label %loop.exit loop.body: %i.next = add i256 %i, 1 %res.next = add i256 %res, %i -; CHECK: JUMP br label %loop.cond loop.exit: diff --git a/llvm/test/CodeGen/EVM/call.ll b/llvm/test/CodeGen/EVM/call.ll index 4e57b307631c..b3d3b16256c9 100644 --- a/llvm/test/CodeGen/EVM/call.ll +++ b/llvm/test/CodeGen/EVM/call.ll @@ -1,17 +1,42 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" -declare i256 @foo(i256) -declare void @foo2(i256) +define i256 @foo(i256) noinline { +; CHECK-LABEL: foo: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + ret i256 0 +} + +define void @foo2(i256) noinline { +; CHECK-LABEL: foo2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: JUMP + ret void +} define i256 @call(i256 %a, i256 %b) nounwind { -; CHECK-LABEL: @call -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ADD [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] -; CHECK: FCALL 1 [[RES1:\$[0-9]+]], @foo, [[TMP1]] +; CHECK-LABEL: call: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH4 @foo +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %sum = add i256 %a, %b %res = call i256 @foo(i256 %sum) @@ -19,33 +44,19 @@ define i256 @call(i256 %a, i256 %b) nounwind { } define void @call2(i256 %a, i256 %b) nounwind { -; CHECK-LABEL: @call2 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ADD [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] -; CHECK: FCALL 0 @foo2, [[TMP1]] +; CHECK-LABEL: call2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: ADD +; CHECK-NEXT: PUSH4 @foo2 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET1: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP %sum = add i256 %a, %b call void @foo2(i256 %sum) ret void } - -define void @call3_indir(void (i256)* %callee) nounwind { -; CHECK-LABEL: @call3_indir -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C1:\$[0-9]+]], 10 -; CHECK: FCALL 0 [[IN1]], [[C1]] - - call void %callee(i256 10) - ret void -} - -define i256 @call4_indir(i256 (i256)* %callee) nounwind { -; CHECK-LABEL: @call4_indir -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C1:\$[0-9]+]], 10 -; CHECK: FCALL 1 [[RES1:\$[0-9]+]], [[IN1]], [[C1]] - - %res = call i256 %callee(i256 10) - ret i256 %res -} diff --git a/llvm/test/CodeGen/EVM/div.ll b/llvm/test/CodeGen/EVM/div.ll index f5965662751e..a5b04c26d47a 100644 --- a/llvm/test/CodeGen/EVM/div.ll +++ b/llvm/test/CodeGen/EVM/div.ll @@ -1,31 +1,41 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @udivrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @udivrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: DIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: udivrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = udiv i256 %rs1, %rs2 ret i256 %res } define i256 @sdivrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @sdivrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SDIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: sdivrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SDIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sdiv i256 %rs1, %rs2 ret i256 %res } define i256 @sdivrri(i256 %rs1) nounwind { -; CHECK-LABEL: @sdivrri -; CHECK: CONST_I256 [[TMP:\$[0-9]+]], 0 +; CHECK-LABEL: sdivrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sdiv i256 %rs1, 0 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/frameidx.ll b/llvm/test/CodeGen/EVM/frameidx.ll deleted file mode 100644 index b1325535a8e0..000000000000 --- a/llvm/test/CodeGen/EVM/frameidx.ll +++ /dev/null @@ -1,53 +0,0 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s - -target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" -target triple = "evm" - -define i256 @alloca() nounwind { -; CHECK-LABEL: alloca: -; CHECK: STACK_LOAD [[RES:\$[0-9]+]], %SP - - %var = alloca i256, align 1 - %rv = load i256, ptr %var - ret i256 %rv -} - -define i256 @alloca2() nounwind { -; CHECK-LABEL: alloca2: -; CHECK: STACK_LOAD [[RES:\$[0-9]+]], %SP, 4096 - - %fill = alloca i256, i32 128 - %var = alloca i256, align 1 - %rv = load i256, ptr %var - ret i256 %rv -} - -define void @alloca3(i256 %val) nounwind { -; CHECK-LABEL: alloca3: -; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 -; CHECK: STACK_STORE %SP, 0, [[VAL]] - - %fill = alloca i256, align 1 - store i256 %val, ptr %fill - ret void -} - -define i256 @alloca4() nounwind { -; CHECK-LABEL: alloca4: -; CHECK: STACK_LOAD [[RES:\$[0-9]+]], %SP, 64 - - %alloca_ptr = alloca i256, i32 128 - %elm = getelementptr i256, ptr %alloca_ptr, i256 2 - %rv = load i256, ptr %elm - ret i256 %rv -} - -define void @alloca5(i256 %val) nounwind { -; CHECK-LABEL: alloca5: -; CHECK: STACK_STORE %SP, 64, [[RES:\$[0-9]+]] - - %alloca_ptr = alloca i256, i32 128 - %elm = getelementptr i256, ptr %alloca_ptr, i256 2 - store i256 %val, ptr %elm - ret void -} diff --git a/llvm/test/CodeGen/EVM/globals.ll b/llvm/test/CodeGen/EVM/globals.ll deleted file mode 100644 index df70cab339df..000000000000 --- a/llvm/test/CodeGen/EVM/globals.ll +++ /dev/null @@ -1,89 +0,0 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s - -target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" -target triple = "evm" - -%struct.w = type { i32, i256 } - -@val = addrspace(1) global i256 0 -@val2 = addrspace(1) global i256 42 -@val3 = addrspace(1) global i256 0 -@val.arr = addrspace(1) global [4 x i256] zeroinitializer -@val2.arr = addrspace(1) global [4 x i256] [i256 1, i256 2, i256 3, i256 4] -@as_ptr = addrspace(1) global ptr zeroinitializer -@w = external dso_local local_unnamed_addr addrspace(1) global %struct.w, align 1 - -define i256 @load.stelem() { -; CHECK-LABEL: load.stelem -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @w+32 -; CHECK: MLOAD [[TMP:\$[0-9]+]], [[ADDR]] - - %elem = getelementptr inbounds %struct.w, ptr addrspace(1) @w, i32 0, i32 1 - %load = load i256, ptr addrspace(1) %elem - ret i256 %load -} - -define i256 @load.elem() nounwind { -; CHECK-LABEL: load.elem -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @val -; CHECK: MLOAD [[TMP:\$[0-9]+]], [[ADDR]] - - %res = load i256, i256 addrspace(1)* @val - ret i256 %res -} - -define void @store.elem(i256 %val) nounwind { -; CHECK-LABEL: store.elem -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @val -; CHECK: MSTORE [[ADDR]], {{.*}} - - store i256 %val, i256 addrspace(1)* @val - ret void -} - -define i256 @load.fromarray(i256 %i) nounwind { -; CHECK-LABEL: load.fromarray -; CHECK: ARGUMENT [[IDX:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C:\$[0-9]+]], 5 -; CHECK: SHL [[SHL:\$[0-9]+]], [[IDX]], [[C]] -; CHECK: CONST_I256 [[TMP:\$[0-9]+]], @val2.arr -; CHECK: ADD [[ADDR:\$[0-9]+]], [[TMP]], [[SHL]] -; CHECK: MLOAD [[RES:\$[0-9]+]], [[ADDR]] - - %elem = getelementptr [4 x i256], [4 x i256] addrspace(1)* @val2.arr, i256 0, i256 %i - %res = load i256, i256 addrspace(1)* %elem - ret i256 %res -} - -define void @store.toarray(i256 %val, i256 %i) nounwind { -; CHECK-LABEL: store.toarray -; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IDX:\$[0-9]+]], 1 -; CHECK: CONST_I256 [[C:\$[0-9]+]], 5 -; CHECK: SHL [[SHL:\$[0-9]+]], [[IDX]], [[C]] -; CHECK: CONST_I256 [[TMP:\$[0-9]+]], @val.arr -; CHECK: ADD [[ADDR:\$[0-9]+]], [[TMP]], [[SHL]] -; CHECK: MSTORE [[ADDR]], [[VAL]] - - %elem = getelementptr [4 x i256], [4 x i256] addrspace(1)* @val.arr, i256 0, i256 %i - store i256 %val, i256 addrspace(1)* %elem - ret void -} - -define ptr @load.ptr() nounwind { -; CHECK-LABEL: load.ptr -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @as_ptr -; CHECK: MLOAD [[TMP:\$[0-9]+]], [[ADDR]] - - %res = load ptr, ptr addrspace(1) @as_ptr - ret ptr %res -} - -define void @store.ptr(ptr addrspace(1) %val) nounwind { -; CHECK-LABEL: store.ptr -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @as_ptr -; CHECK: MSTORE [[ADDR]], {{.*}} - - store ptr addrspace(1) %val, ptr addrspace(1) @as_ptr - ret void -} diff --git a/llvm/test/CodeGen/EVM/icmp.ll b/llvm/test/CodeGen/EVM/icmp.ll index 9dd6caaf4390..dd8f85ff6784 100644 --- a/llvm/test/CodeGen/EVM/icmp.ll +++ b/llvm/test/CodeGen/EVM/icmp.ll @@ -1,10 +1,16 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @icmp_eq(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : EQ [[RES:\$[0-9]+]], [[IN1]], [[IN2]] @@ -16,6 +22,12 @@ define i256 @icmp_eq(i256 %a, i256 %b) nounwind { define i256 @icmp_big_imm_eq(i256 %a) nounwind { ; CHECK-LABEL: icmp_big_imm_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH32 43576122634770472758325941782982599838796957244005075818703754470792663924736 +; CHECK-NEXT: EQ +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : CONST_I256 [[C1:\$[0-9]+]], [[[0-9]+]] ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : EQ [[RES:\$[0-9]+]], [[IN1]], [[IN2]] @@ -27,6 +39,12 @@ define i256 @icmp_big_imm_eq(i256 %a) nounwind { define i256 @icmp_ne(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_ne: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : EQ [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] @@ -39,6 +57,11 @@ define i256 @icmp_ne(i256 %a, i256 %b) nounwind { define i256 @icmp_ugt(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_ugt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : GT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] @@ -50,6 +73,12 @@ define i256 @icmp_ugt(i256 %a, i256 %b) nounwind { define i256 @icmp_uge(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_uge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : LT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] @@ -62,6 +91,11 @@ define i256 @icmp_uge(i256 %a, i256 %b) nounwind { define i256 @icmp_ult(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_ult: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : LT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] @@ -73,6 +107,12 @@ define i256 @icmp_ult(i256 %a, i256 %b) nounwind { define i256 @icmp_ule(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_ule: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : GT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] @@ -85,6 +125,11 @@ define i256 @icmp_ule(i256 %a, i256 %b) nounwind { define i256 @icmp_sgt(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_sgt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : SGT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] @@ -96,6 +141,12 @@ define i256 @icmp_sgt(i256 %a, i256 %b) nounwind { define i256 @icmp_sge(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_sge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : SLT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] @@ -108,6 +159,11 @@ define i256 @icmp_sge(i256 %a, i256 %b) nounwind { define i256 @icmp_slt(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_slt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : SLT [[RES:\$[0-9]+]], [[IN1]], [[IN2]] @@ -119,6 +175,12 @@ define i256 @icmp_slt(i256 %a, i256 %b) nounwind { define i256 @icmp_sle(i256 %a, i256 %b) nounwind { ; CHECK-LABEL: icmp_sle: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP ; CHECK : ARGUMENT [[IN2:\$[0-9]+]], 1 ; CHECK : ARGUMENT [[IN1:\$[0-9]+]], 0 ; CHECK : SGT [[TMP1:\$[0-9]+]], [[IN1]], [[IN2]] diff --git a/llvm/test/CodeGen/EVM/intrinsic.ll b/llvm/test/CodeGen/EVM/intrinsic.ll index 8398c33cf70a..c14059843005 100644 --- a/llvm/test/CodeGen/EVM/intrinsic.ll +++ b/llvm/test/CodeGen/EVM/intrinsic.ll @@ -1,516 +1,630 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @sdiv(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @sdiv -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SDIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: sdiv: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SDIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.sdiv(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @div(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @div -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: DIV [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: div: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIV +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.div(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @smod(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @smod -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: smod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.smod(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @mod(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @mod -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: MOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: mod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.mod(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @shl(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @shl -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SHL [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] +; CHECK-LABEL: shl: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.shl(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @shr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @shr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SHR [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] +; CHECK-LABEL: shr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.shr(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @sar(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @sar -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SAR [[TMP:\$[0-9]+]], [[IN2]], [[IN1]] +; CHECK-LABEL: sar: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.sar(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { -; CHECK-LABEL: @addmod -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ADDMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] +; CHECK-LABEL: addmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.addmod(i256 %rs1, i256 %rs2, i256 %rs3) ret i256 %res } define i256 @mulmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { -; CHECK-LABEL: @mulmod -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: MULMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] +; CHECK-LABEL: mulmod: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MULMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.mulmod(i256 %rs1, i256 %rs2, i256 %rs3) ret i256 %res } define i256 @exp(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @exp -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: EXP [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: exp: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.exp(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { -; CHECK-LABEL: @sha3 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SHA3 [[RES1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: sha3: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SHA3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.sha3(ptr addrspace(1) %offset, i256 %size) ret i256 %res } define i256 @signextend(i256 %bytesize, i256 %val) nounwind { -; CHECK-LABEL: @signextend -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SIGNEXTEND [[RES1:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: signextend: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.signextend(i256 %bytesize, i256 %val) ret i256 %res } define i256 @byte(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @byte -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: BYTE [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: byte: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.byte(i256 %rs1, i256 %rs2) ret i256 %res } define i256 @pc() nounwind { -; CHECK-LABEL: @pc -; CHECK: PC [[RES1:\$[0-9]+]] +; CHECK-LABEL: pc: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PC +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.pc() ret i256 %res } define i256 @msize() nounwind { -; CHECK-LABEL: @msize -; CHECK: MSIZE [[RES1:\$[0-9]+]] +; CHECK-LABEL: msize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MSIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.msize() ret i256 %res } define i256 @address() nounwind { -; CHECK-LABEL: @address -; CHECK: ADDRESS [[RES1:\$[0-9]+]] +; CHECK-LABEL: address: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ADDRESS +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.address() ret i256 %res } define i256 @origin() nounwind { -; CHECK-LABEL: @origin -; CHECK: ORIGIN [[RES1:\$[0-9]+]] +; CHECK-LABEL: origin: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ORIGIN +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.origin() ret i256 %res } define i256 @caller() nounwind { -; CHECK-LABEL: @caller -; CHECK: CALLER [[RES1:\$[0-9]+]] +; CHECK-LABEL: caller: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.caller() ret i256 %res } define i256 @balance(i256 %rs1) nounwind { -; CHECK-LABEL: @balance -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: BALANCE [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: balance: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BALANCE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.balance(i256 %rs1) ret i256 %res } define i256 @calldatasize() nounwind { -; CHECK-LABEL: @calldatasize -; CHECK: CALLDATASIZE [[RES1:\$[0-9]+]] +; CHECK-LABEL: calldatasize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATASIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.calldatasize() ret i256 %res } define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { -; CHECK-LABEL: @calldataload -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CALLDATALOAD [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: calldataload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATALOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.calldataload(ptr addrspace(2) %rs1) ret i256 %res } define i256 @callvalue() nounwind { -; CHECK-LABEL: @callvalue -; CHECK: CALLVALUE [[RES1:\$[0-9]+]] +; CHECK-LABEL: callvalue: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLVALUE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.callvalue() ret i256 %res } define i256 @codesize() nounwind { -; CHECK-LABEL: @codesize -; CHECK: CODESIZE [[RES1:\$[0-9]+]] +; CHECK-LABEL: codesize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODESIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.codesize() ret i256 %res } define i256 @gasprice() nounwind { -; CHECK-LABEL: @gasprice -; CHECK: GASPRICE [[RES1:\$[0-9]+]] +; CHECK-LABEL: gasprice: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASPRICE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.gasprice() ret i256 %res } define i256 @extcodesize(i256 %rs1) nounwind { -; CHECK-LABEL: @extcodesize -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: EXTCODESIZE [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: extcodesize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXTCODESIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.extcodesize(i256 %rs1) ret i256 %res } define void @extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) nounwind { -; CHECK-LABEL: @extcodecopy -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: EXTCODECOPY [[IN1]], [[IN2]], [[IN3]], [[IN4]] +; CHECK-LABEL: extcodecopy: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXTCODECOPY +; CHECK-NEXT: JUMP call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) %dst, ptr addrspace(4) %src, i256 %size) ret void } define i256 @extcodehash(i256 %rs1) nounwind { -; CHECK-LABEL: @extcodehash -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: EXTCODEHASH [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: extcodehash: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EXTCODEHASH +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.extcodehash(i256 %rs1) ret i256 %res } define i256 @returndatasize() nounwind { -; CHECK-LABEL: @returndatasize -; CHECK: RETURNDATASIZE [[RES1:\$[0-9]+]] +; CHECK-LABEL: returndatasize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURNDATASIZE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.returndatasize() ret i256 %res } define i256 @blockhash(i256 %rs1) nounwind { -; CHECK-LABEL: @blockhash -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: BLOCKHASH [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: blockhash: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOCKHASH +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.blockhash(i256 %rs1) ret i256 %res } define i256 @blobhash(i256 %rs1) nounwind { -; CHECK-LABEL: @blobhash -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: BLOBHASH [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: blobhash: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBHASH +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.blobhash(i256 %rs1) ret i256 %res } define i256 @coinbase() nounwind { -; CHECK-LABEL: @coinbase -; CHECK: COINBASE [[RES1:\$[0-9]+]] +; CHECK-LABEL: coinbase: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: COINBASE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.coinbase() ret i256 %res } define i256 @timestamp() nounwind { -; CHECK-LABEL: @timestamp -; CHECK: TIMESTAMP [[RES1:\$[0-9]+]] +; CHECK-LABEL: timestamp: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TIMESTAMP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.timestamp() ret i256 %res } define i256 @number() nounwind { -; CHECK-LABEL: @number -; CHECK: NUMBER [[RES1:\$[0-9]+]] +; CHECK-LABEL: number: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: NUMBER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.number() ret i256 %res } define i256 @difficulty() nounwind { -; CHECK-LABEL: @difficulty -; CHECK: DIFFICULTY [[RES1:\$[0-9]+]] +; CHECK-LABEL: difficulty: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DIFFICULTY +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.difficulty() ret i256 %res } define i256 @gaslimit() nounwind { -; CHECK-LABEL: @gaslimit -; CHECK: GASLIMIT [[RES1:\$[0-9]+]] +; CHECK-LABEL: gaslimit: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GASLIMIT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.gaslimit() ret i256 %res } define i256 @chainid() nounwind { -; CHECK-LABEL: @chainid -; CHECK: CHAINID [[RES1:\$[0-9]+]] +; CHECK-LABEL: chainid: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CHAINID +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.chainid() ret i256 %res } define i256 @selfbalance() nounwind { -; CHECK-LABEL: @selfbalance -; CHECK: SELFBALANCE [[RES1:\$[0-9]+]] +; CHECK-LABEL: selfbalance: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SELFBALANCE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.selfbalance() ret i256 %res } define i256 @basefee() nounwind { -; CHECK-LABEL: @basefee -; CHECK: BASEFEE [[RES1:\$[0-9]+]] +; CHECK-LABEL: basefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.basefee() ret i256 %res } define i256 @blobbasefee() nounwind { -; CHECK-LABEL: @blobbasefee -; CHECK: BLOBBASEFEE [[RES1:\$[0-9]+]] +; CHECK-LABEL: blobbasefee: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: BLOBBASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @llvm.evm.blobbasefee() ret i256 %res } define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { -; CHECK-LABEL: @log0 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: LOG0 [[IN1]], [[IN2]] +; CHECK-LABEL: log0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG0 +; CHECK-NEXT: JUMP call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) ret void } define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { -; CHECK-LABEL: @log1 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: LOG1 [[IN1]], [[IN2]], [[IN3]] +; CHECK-LABEL: log1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG1 +; CHECK-NEXT: JUMP call void @llvm.evm.log1(ptr addrspace(1) %off, i256 %size, i256 %t1) ret void } define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { -; CHECK-LABEL: @log2 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: LOG2 [[IN1]], [[IN2]], [[IN3]], [[IN4]] +; CHECK-LABEL: log2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG2 +; CHECK-NEXT: JUMP call void @llvm.evm.log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) ret void } define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { -; CHECK-LABEL: @log3 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: LOG3 [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]] +; CHECK-LABEL: log3: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG3 +; CHECK-NEXT: JUMP call void @llvm.evm.log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) ret void } define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { -; CHECK-LABEL: @log4 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: LOG4 [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] +; CHECK-LABEL: log4: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LOG4 +; CHECK-NEXT: JUMP call void @llvm.evm.log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) ret void } define i256 @create(i256 %val, ptr addrspace(1) %off, i256 %size) nounwind { -; CHECK-LABEL: @create -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: CREATE [[RES1:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]] +; CHECK-LABEL: create: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CREATE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.create(i256 %val, ptr addrspace(1) %off, i256 %size) ret i256 %ret } define i256 @call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { -; CHECK-LABEL: @call -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: ARGUMENT [[IN7:\$[0-9]+]], 6 -; CHECK: CALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]], [[IN7]] +; CHECK-LABEL: call: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) ret i256 %ret } define i256 @delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { -; CHECK-LABEL: @delegatecall -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: DELEGATECALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] +; CHECK-LABEL: delegatecall: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DELEGATECALL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.delegatecall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace (1) %ret_off, i256 %ret_size) ret i256 %ret } define i256 @create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) nounwind { -; CHECK-LABEL: @create2 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: CREATE2 [[RES1:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]] +; CHECK-LABEL: create2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CREATE2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.create2(i256 %val, ptr addrspace(1) %off, i256 %size, i256 %salt) ret i256 %ret } define i256 @staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) nounwind { -; CHECK-LABEL: @staticcall -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: ARGUMENT [[IN5:\$[0-9]+]], 4 -; CHECK: ARGUMENT [[IN6:\$[0-9]+]], 5 -; CHECK: STATICCALL [[RES:\$[0-9]+]], [[IN1]], [[IN2]], [[IN3]], [[IN4]], [[IN5]], [[IN6]] +; CHECK-LABEL: staticcall: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: STATICCALL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.staticcall(i256 %gas, i256 %addr, ptr addrspace(1) %arg_off, i256 %arg_size, ptr addrspace(1) %ret_off, i256 %ret_size) ret i256 %ret } define void @selfdestruct(i256 %addr) nounwind { -; CHECK-LABEL: @selfdestruct -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: SELFDESTRUCT [[IN1]] +; CHECK-LABEL: selfdestruct: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SELFDESTRUCT +; CHECK-NEXT: JUMP call void @llvm.evm.selfdestruct(i256 %addr) ret void } define void @return(ptr addrspace(1) %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @return -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: RETURN [[IN1]], [[IN2]] +; CHECK-LABEL: return: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURN +; CHECK-NEXT: JUMP call void @llvm.evm.return(ptr addrspace(1) %rs1, i256 %rs2) ret void } define void @revert(ptr addrspace(1) %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @revert -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: REVERT [[IN1]], [[IN2]] +; CHECK-LABEL: revert: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: REVERT +; CHECK-NEXT: JUMP call void @llvm.evm.revert(ptr addrspace(1) %rs1, i256 %rs2) ret void } define void @invalid() nounwind { -; CHECK-LABEL: @invalid -; CHECK: INVALID +; CHECK-LABEL: invalid: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: INVALID +; CHECK-NEXT: JUMP call void @llvm.evm.invalid() ret void diff --git a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll index 526773d0a943..9a62b4d1287b 100644 --- a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll +++ b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll @@ -1,14 +1,21 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" -@ptr = private unnamed_addr addrspace(1) global i256 0 - -define i1 @simplify_setcc() { -; CHECK-LABEL: simplify_setcc -; CHECK-NOT: @ptr+31 - %val = load i256, ptr addrspace(1) @ptr, align 32 +define i1 @simplify_setcc(ptr addrspace(1) %glob) { +; CHECK-LABEL: simplify_setcc: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: MLOAD +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %val = load i256, ptr addrspace(1) %glob, align 32 %val.and = and i256 %val, 2 %cmp = icmp eq i256 %val.and, 0 ret i1 %cmp diff --git a/llvm/test/CodeGen/EVM/logical.ll b/llvm/test/CodeGen/EVM/logical.ll index 43b75a30a4fe..dad3129034c7 100644 --- a/llvm/test/CodeGen/EVM/logical.ll +++ b/llvm/test/CodeGen/EVM/logical.ll @@ -1,42 +1,54 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @andrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @andrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: AND [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: andrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = and i256 %rs1, %rs2 ret i256 %res } define i256 @orrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @orrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: OR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: orrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = or i256 %rs1, %rs2 ret i256 %res } define i256 @xorrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @xorrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: XOR [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: xorrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: XOR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = xor i256 %rs1, %rs2 ret i256 %res } define i256 @notrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @notrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: NOT [[TMP:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: notrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: NOT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = xor i256 %rs1, -1 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/mem_call_data.ll b/llvm/test/CodeGen/EVM/mem_call_data.ll index 8a57cb22a9cb..bd21b079c92e 100644 --- a/llvm/test/CodeGen/EVM/mem_call_data.ll +++ b/llvm/test/CodeGen/EVM/mem_call_data.ll @@ -1,12 +1,16 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @mload(ptr addrspace(2) %offset) nounwind { -; CHECK-LABEL: @mload -; CHECK: ARGUMENT [[OFF:\$[0-9]+]], 0 -; CHECK: CALLDATALOAD [[RES1:\$[0-9]+]], [[OFF]] +; CHECK-LABEL: mload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATALOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %val = load i256, ptr addrspace(2) %offset, align 32 ret i256 %val diff --git a/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll b/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll index 3391914454fa..ddd545957b0e 100644 --- a/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll +++ b/llvm/test/CodeGen/EVM/memintrinsics-no-expansion.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: opt -O3 -S < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" @@ -10,7 +11,6 @@ declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonl define fastcc void @calldata_to_heap() { ; CHECK-LABEL: calldata_to_heap ; CHECK: llvm.memcpy.p1.p2.i256 - call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) null, ptr addrspace(2) null, i256 4, i1 false) ret void } @@ -18,7 +18,6 @@ define fastcc void @calldata_to_heap() { define fastcc void @returndata_to_heap() { ; CHECK-LABEL: returndata_to_heap ; CHECK: llvm.memcpy.p1.p3.i256 - call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) null, ptr addrspace(3) null, i256 4, i1 false) ret void } @@ -26,7 +25,6 @@ define fastcc void @returndata_to_heap() { define fastcc void @code_to_heap() { ; CHECK-LABEL: code_to_heap ; CHECK: llvm.memcpy.p1.p4.i256 - call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) null, i256 4, i1 false) ret void } diff --git a/llvm/test/CodeGen/EVM/memintrinsics-opt.ll b/llvm/test/CodeGen/EVM/memintrinsics-opt.ll index e650f6f3372f..f7cb81728858 100644 --- a/llvm/test/CodeGen/EVM/memintrinsics-opt.ll +++ b/llvm/test/CodeGen/EVM/memintrinsics-opt.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: opt -O3 -S < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" diff --git a/llvm/test/CodeGen/EVM/memintrinsics.ll b/llvm/test/CodeGen/EVM/memintrinsics.ll index af74658b90b2..000034c7c730 100644 --- a/llvm/test/CodeGen/EVM/memintrinsics.ll +++ b/llvm/test/CodeGen/EVM/memintrinsics.ll @@ -1,4 +1,5 @@ -; RUN: llc -O3 --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -O3 < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" @@ -15,120 +16,126 @@ declare void @llvm.memmove.p1.p4.i256(ptr addrspace(1) nocapture writeonly, ptr declare void @llvm.memmove.p1.p1.i8(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i8, i1 immarg) define fastcc void @memmove-imm8(ptr addrspace(1) %dest, ptr addrspace(1) %src) { -; CHECK-LABEL: memmove-imm8 -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i8(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 77, i1 false) ret void } define fastcc void @memmove-imm8-arg(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 %size) { -; CHECK-LABEL: memmove-imm8-arg -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i8(ptr addrspace(1) %dest, ptr addrspace(1) %src, i8 %size, i1 false) ret void } define fastcc void @huge-copysize0(ptr addrspace(1) %dest, ptr addrspace(1) %src) { -; CHECK-LABEL: huge-copysize0 -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144064, i1 false) ret void } define fastcc void @huge-copysize1(ptr addrspace(1) %dest, ptr addrspace(1) %src) { -; CHECK-LABEL: huge-copysize1 -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144065, i1 false) ret void } define fastcc void @huge-movesize1(ptr addrspace(1) %dest, ptr addrspace(1) %src) { -; CHECK-LABEL: huge-movesize1 -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 81129638414606681695789005144065, i1 false) ret void } define fastcc void @normal-known-size(ptr addrspace(1) %dest, ptr addrspace(1) %src) { -; CHECK-LABEL: normal-known-size -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 1024, i1 false) ret void } define fastcc void @normal-known-size-2(ptr addrspace(1) %dest, ptr addrspace(1) %src) { -; CHECK-LABEL: normal-known-size-2 -; CHECK: MCOPY - call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 1060, i1 false) ret void } define fastcc void @heap_to_heap(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 %len) { -; CHECK-LABEL: heap_to_heap -; CHECK: MCOPY +; CHECK-LABEL: heap_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MCOPY +; CHECK-NEXT: JUMP call void @llvm.memcpy.p1.p1.i256(ptr addrspace(1) %dest, ptr addrspace(1) %src, i256 %len, i1 false) ret void } define fastcc void @calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { -; CHECK-LABEL: calldata_to_heap -; CHECK: CALLDATACOPY +; CHECK-LABEL: calldata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATACOPY +; CHECK-NEXT: JUMP call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len, i1 false) ret void } define fastcc void @move_calldata_to_heap(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len) { -; CHECK-LABEL: move_calldata_to_heap -; CHECK: CALLDATACOPY +; CHECK-LABEL: move_calldata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CALLDATACOPY +; CHECK-NEXT: JUMP call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 %len, i1 false) ret void } define fastcc void @calldata_to_heap_csize(ptr addrspace(1) %dest, ptr addrspace(2) %src) { -; CHECK-LABEL: calldata_to_heap_csize -; CHECK: CALLDATACOPY +; CHECK-LABEL: calldata_to_heap_csize: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 42 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: CALLDATACOPY +; CHECK-NEXT: JUMP call void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) %dest, ptr addrspace(2) %src, i256 42, i1 false) ret void } define fastcc void @returndata_to_heap(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len) { -; CHECK-LABEL: returndata_to_heap -; CHECK: RETURNDATACOPY +; CHECK-LABEL: returndata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURNDATACOPY +; CHECK-NEXT: JUMP call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len, i1 false) ret void } define fastcc void @move_returndata_to_heap(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len) { -; CHECK-LABEL: move_returndata_to_heap -; CHECK: RETURNDATACOPY +; CHECK-LABEL: move_returndata_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: RETURNDATACOPY +; CHECK-NEXT: JUMP call void @llvm.memmove.p1.p3.i256(ptr addrspace(1) %dest, ptr addrspace(3) %src, i256 %len, i1 false) ret void } define fastcc void @code_to_heap(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len) { -; CHECK-LABEL: code_to_heap -; CHECK: CODECOPY +; CHECK-LABEL: code_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: JUMP call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len, i1 false) ret void } define fastcc void @move_code_to_heap(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len) { -; CHECK-LABEL: move_code_to_heap -; CHECK: CODECOPY +; CHECK-LABEL: move_code_to_heap: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: JUMP call void @llvm.memmove.p1.p4.i256(ptr addrspace(1) %dest, ptr addrspace(4) %src, i256 %len, i1 false) ret void diff --git a/llvm/test/CodeGen/EVM/memory.ll b/llvm/test/CodeGen/EVM/memory.ll index 6c60d756ea85..4c0a2410ca80 100644 --- a/llvm/test/CodeGen/EVM/memory.ll +++ b/llvm/test/CodeGen/EVM/memory.ll @@ -1,32 +1,38 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define void @mstore8(ptr addrspace(1) %offset, i256 %val) nounwind { -; CHECK-LABEL: @mstore8 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: MSTORE8 [[IN1]], [[IN2]] +; CHECK-LABEL: mstore8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MSTORE8 +; CHECK-NEXT: JUMP call void @llvm.evm.mstore8(ptr addrspace(1) %offset, i256 %val) ret void } define void @mstore(ptr addrspace(1) %offset, i256 %val) nounwind { -; CHECK-LABEL: @mstore -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: MSTORE [[IN1]], [[IN2]] +; CHECK-LABEL: mstore: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MSTORE +; CHECK-NEXT: JUMP store i256 %val, ptr addrspace(1) %offset, align 32 ret void } define i256 @mload(ptr addrspace(1) %offset) nounwind { -; CHECK-LABEL: @mload -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: MLOAD [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: mload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %val = load i256, ptr addrspace(1) %offset, align 32 ret i256 %val diff --git a/llvm/test/CodeGen/EVM/mod.ll b/llvm/test/CodeGen/EVM/mod.ll index 8ab6f36ba61a..fe176b5362b8 100644 --- a/llvm/test/CodeGen/EVM/mod.ll +++ b/llvm/test/CodeGen/EVM/mod.ll @@ -1,31 +1,41 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @umodrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @umodrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: MOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: umodrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = urem i256 %rs1, %rs2 ret i256 %res } define i256 @smodrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @smodrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SMOD [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: smodrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SMOD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = srem i256 %rs1, %rs2 ret i256 %res } define i256 @smodrri(i256 %rs1) nounwind { -; CHECK-LABEL: @smodrri -; CHECK: CONST_I256 [[TMP:\$[0-9]+]], 0 +; CHECK-LABEL: smodrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = srem i256 %rs1, 0 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll index e2b89fc873bd..0c772d95f2a9 100644 --- a/llvm/test/CodeGen/EVM/mul.ll +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -1,25 +1,31 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @mulrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @mulrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: MUL [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: mulrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = mul i256 %rs1, %rs2 ret i256 %res } define i256 @mulrri(i256 %rs1) nounwind { -; CHECK-LABEL: @mulrri -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[REG1:\$[0-9]+]], 500000 -; CHECK: CONST_I256 [[REG2:\$[0-9]+]], 0 -; CHECK: SUB [[REG3:\$[0-9]+]], [[REG2]], [[REG1]] -; CHECK: MUL [[TMP:\$[0-9]+]], [[IN1]], [[REG3]] +; CHECK-LABEL: mulrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 500000 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: MUL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = mul i256 %rs1, -500000 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll b/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll index aa4f4f87e988..980c0de56a61 100644 --- a/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll +++ b/llvm/test/CodeGen/EVM/no-mul-fold-to-overflow-int.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: opt -O3 -S < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" @@ -8,7 +9,6 @@ declare void @llvm.evm.revert(ptr addrspace(1), i256) define fastcc i256 @checked_mul(i256 %0) { ; CHECK-LABEL: checked_mul ; CHECK-NOT: @llvm.smul.with.overflow - entry: %multiplication_result = mul i256 100, %0 %division_signed_is_divided_int_min = icmp eq i256 %multiplication_result, -57896044618658097711785492504343953926634992332820282019728792003956564819968 diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll index d5cb9af50ba7..caec55c8ec4c 100644 --- a/llvm/test/CodeGen/EVM/select.ll +++ b/llvm/test/CodeGen/EVM/select.ll @@ -1,20 +1,34 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { -; CHECK-LABEL: @select -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: ARGUMENT [[IN3:\$[0-9]+]], 2 -; CHECK: ARGUMENT [[IN4:\$[0-9]+]], 3 -; CHECK: EQ [[TMP1:\$[0-9]+]], [[IN3]], [[IN4]] -; CHECK: ISZERO [[COND:\$[0-9]+]], [[TMP1]] -; CHECK: JUMPI @.BB0_2, [[COND]] -; CHECK: COPY_I256 [[IN1]], [[IN2]] -; CHECK-LABEL: .BB0_2: -; CHECK: RET +; CHECK-LABEL: select: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB0_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP %1 = icmp ne i256 %v3, %v4 %2 = select i1 %1, i256 %v1, i256 %v2 diff --git a/llvm/test/CodeGen/EVM/sext.ll b/llvm/test/CodeGen/EVM/sext.ll index 6f57ab33ab58..dae54e28b9c4 100644 --- a/llvm/test/CodeGen/EVM/sext.ll +++ b/llvm/test/CodeGen/EVM/sext.ll @@ -1,65 +1,84 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @sexti1(i1 %rs1) nounwind { -; CHECK-LABEL: @sexti1 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[MASK:\$[0-9]+]], 1 -; CHECK: AND [[TMP:\$[0-9]+]], [[IN1]], [[MASK]] -; CHECK: CONST_I256 [[ZERO:\$[0-9]+]], 0 -; CHECK: SUB [[RES:\$[0-9]+]], [[ZERO]], [[TMP]] +; CHECK-LABEL: sexti1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i1 %rs1 to i256 ret i256 %res } define i256 @sexti8(i8 %rs1) nounwind { -; CHECK-LABEL: @sexti8 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 0 -; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] +; CHECK-LABEL: sexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i8 %rs1 to i256 ret i256 %res } define i256 @sexti16(i16 %rs1) nounwind { -; CHECK-LABEL: @sexti16 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 1 -; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] +; CHECK-LABEL: sexti16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i16 %rs1 to i256 ret i256 %res } define i256 @sexti32(i32 %rs1) nounwind { -; CHECK-LABEL: @sexti32 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 3 -; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] +; CHECK-LABEL: sexti32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i32 %rs1 to i256 ret i256 %res } define i256 @sexti64(i64 %rs1) nounwind { -; CHECK-LABEL: @sexti64 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 7 -; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] +; CHECK-LABEL: sexti64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 7 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i64 %rs1 to i256 ret i256 %res } define i256 @sexti128(i128 %rs1) nounwind { -; CHECK-LABEL: @sexti128 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 15 -; CHECK: SIGNEXTEND [[TMP:\$[0-9]+]], [[EXT]], [[IN1]] +; CHECK-LABEL: sexti128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 15 +; CHECK-NEXT: SIGNEXTEND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i128 %rs1 to i256 ret i256 %res @@ -67,11 +86,15 @@ define i256 @sexti128(i128 %rs1) nounwind { ; Check that 'sext' also gets lowered for types not declared in MVT. define i256 @sexti40(i40 %rs1) nounwind { -; CHECK-LABEL: @sexti40 -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C1:\$[0-9]+]], 216 -; CHECK: SHL [[TMP1:\$[0-9]+]], [[IN1]], [[C1]] -; CHECK: SAR [[TMP2:\$[0-9]]], [[TMP1]], [[C1]] +; CHECK-LABEL: sexti40: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sext i40 %rs1 to i256 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/sha3-constant-folding.ll b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll index 6662883763a9..debb1e3394cc 100644 --- a/llvm/test/CodeGen/EVM/sha3-constant-folding.ll +++ b/llvm/test/CodeGen/EVM/sha3-constant-folding.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: opt -O3 -S < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" diff --git a/llvm/test/CodeGen/EVM/shift.ll b/llvm/test/CodeGen/EVM/shift.ll index db9eb65614d9..6c0729a37134 100644 --- a/llvm/test/CodeGen/EVM/shift.ll +++ b/llvm/test/CodeGen/EVM/shift.ll @@ -1,33 +1,43 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @shl(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @shl -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SHL [[RES:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: shl: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = shl i256 %rs1, %rs2 ret i256 %res } define i256 @shr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @shr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SHR [[RES:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: shr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = lshr i256 %rs1, %rs2 ret i256 %res } define i256 @sar(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @sar -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SAR [[RES:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: sar: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = ashr i256 %rs1, %rs2 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/signextload.ll b/llvm/test/CodeGen/EVM/signextload.ll index 06da46715708..ee664eba2d94 100644 --- a/llvm/test/CodeGen/EVM/signextload.ll +++ b/llvm/test/CodeGen/EVM/signextload.ll @@ -1,13 +1,18 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @load_signexti8(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_signexti8 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 248 -; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_signexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i8, ptr addrspace(1) %ptr %sext = sext i8 %load to i256 @@ -15,10 +20,14 @@ define i256 @load_signexti8(ptr addrspace(1) %ptr) nounwind { } define i256 @load_signexti16(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_signexti16 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 240 -; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_signexti16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 240 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i16, ptr addrspace(1) %ptr %sext = sext i16 %load to i256 @@ -26,10 +35,14 @@ define i256 @load_signexti16(ptr addrspace(1) %ptr) nounwind { } define i256 @load_signexti32(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_signexti32 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 224 -; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_signexti32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i32, ptr addrspace(1) %ptr %sext = sext i32 %load to i256 @@ -37,10 +50,14 @@ define i256 @load_signexti32(ptr addrspace(1) %ptr) nounwind { } define i256 @load_signexti64(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_signexti64 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 192 -; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_signexti64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 192 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i64, ptr addrspace(1) %ptr %sext = sext i64 %load to i256 @@ -48,10 +65,14 @@ define i256 @load_signexti64(ptr addrspace(1) %ptr) nounwind { } define i256 @load_signexti128(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_signexti128 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 128 -; CHECK: SAR [[RES:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_signexti128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SAR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i128, ptr addrspace(1) %ptr %sext = sext i128 %load to i256 diff --git a/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll b/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll index 0f8637acb448..6bdc9aa65cf8 100644 --- a/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll +++ b/llvm/test/CodeGen/EVM/stdlib-call-ellision.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: opt -O2 -S < %s | FileCheck %s ; UNSUPPORTED: evm @@ -7,91 +8,78 @@ target triple = "evm" define i256 @test__addmod(i256 %arg1, i256 %arg2, i256 %modulo) { ; CHECK: @llvm.evm.addmod - %res = call i256 @__addmod(i256 %arg1, i256 %arg2, i256 %modulo) ret i256 %res } define i256 @test__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) { ; CHECK: @llvm.evm.mulmod - %res = call i256 @__mulmod(i256 %arg1, i256 %arg2, i256 %modulo) ret i256 %res } define i256 @test__signextend(i256 %bytesize, i256 %val) { ; CHECK: @llvm.evm.signextend - %res = call i256 @__signextend(i256 %bytesize, i256 %val) ret i256 %res } define i256 @test__exp(i256 %base, i256 %exp) { ; CHECK: @llvm.evm.exp - %res = call i256 @__exp(i256 %base, i256 %exp) ret i256 %res } define i256 @test__byte(i256 %index, i256 %val) { ; CHECK: @llvm.evm.byte - %res = call i256 @__byte(i256 %index, i256 %val) ret i256 %res } define i256 @test__sdiv(i256 %dividend, i256 %divisor) { ; CHECK: @llvm.evm.sdiv - %res = call i256 @__sdiv(i256 %dividend, i256 %divisor) ret i256 %res } define i256 @test__div(i256 %dividend, i256 %divisor) { ; CHECK: @llvm.evm.div - %res = call i256 @__div(i256 %dividend, i256 %divisor) ret i256 %res } define i256 @test__smod(i256 %val, i256 %mod) { ; CHECK: @llvm.evm.smod - %res = call i256 @__smod(i256 %val, i256 %mod) ret i256 %res } define i256 @test__mod(i256 %val, i256 %mod) { ; CHECK: @llvm.evm.mod - %res = call i256 @__mod(i256 %val, i256 %mod) ret i256 %res } define i256 @test__shl(i256 %shift, i256 %val) { ; CHECK: @llvm.evm.shl - %res = call i256 @__shl(i256 %shift, i256 %val) ret i256 %res } define i256 @test__shr(i256 %shift, i256 %val) { ; CHECK: @llvm.evm.shr - %res = call i256 @__shr(i256 %shift, i256 %val) ret i256 %res } define i256 @test__sar(i256 %shift, i256 %val) { ; CHECK: @llvm.evm.sar - %res = call i256 @__sar(i256 %shift, i256 %val) ret i256 %res } define i256 @test__sha3(ptr addrspace(1) %offset, i256 %len) { ; CHECK: @llvm.evm.sha3 - %res = call i256 @__sha3(ptr addrspace(1) %offset, i256 %len, i1 undef) ret i256 %res } diff --git a/llvm/test/CodeGen/EVM/storage.ll b/llvm/test/CodeGen/EVM/storage.ll index e66d6adfa95d..95755edca9e3 100644 --- a/llvm/test/CodeGen/EVM/storage.ll +++ b/llvm/test/CodeGen/EVM/storage.ll @@ -1,22 +1,27 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define void @sstore(ptr addrspace(5) %key, i256 %val) nounwind { -; CHECK-LABEL: @sstore -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SSTORE [[IN1]], [[IN2]] +; CHECK-LABEL: sstore: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SSTORE +; CHECK-NEXT: JUMP store i256 %val, ptr addrspace(5) %key, align 32 ret void } define i256 @sload(ptr addrspace(5) %key) nounwind { -; CHECK-LABEL: @sload -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: SLOAD [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: sload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %val = load i256, ptr addrspace(5) %key, align 32 ret i256 %val diff --git a/llvm/test/CodeGen/EVM/sub.ll b/llvm/test/CodeGen/EVM/sub.ll index ef913e9a9434..0dba7bd56bab 100644 --- a/llvm/test/CodeGen/EVM/sub.ll +++ b/llvm/test/CodeGen/EVM/sub.ll @@ -1,23 +1,30 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @subrrr(i256 %rs1, i256 %rs2) nounwind { -; CHECK-LABEL: @subrrr -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: SUB [[TMP:\$[0-9]+]], [[IN1]], [[IN2]] +; CHECK-LABEL: subrrr: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = sub i256 %rs1, %rs2 ret i256 %res } define i256 @addrri(i256 %rs1) nounwind { -; CHECK-LABEL: @addrri -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[C1:\$[0-9]+]], 3 -; CHECK: SUB [[TMP:\$[0-9]+]], [[IN1]], [[C1]] +; CHECK-LABEL: addrri: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = add i256 %rs1, -3 ret i256 %res diff --git a/llvm/test/CodeGen/EVM/truncstore.ll b/llvm/test/CodeGen/EVM/truncstore.ll index ef71df6530ea..0a7928b5923b 100644 --- a/llvm/test/CodeGen/EVM/truncstore.ll +++ b/llvm/test/CodeGen/EVM/truncstore.ll @@ -1,34 +1,39 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" -@glob_i8 = addrspace(1) global i8 0 -@glob_i32 = addrspace(1) global i32 0 +define void @storei8(i8 %val, ptr addrspace(1) %glob) nounwind { +; CHECK-LABEL: storei8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: MSTORE8 +; CHECK-NEXT: JUMP -define void @storei8(i8 %val) nounwind { -; CHECK-LABEL: @storei8 -; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @glob_i8 -; CHECK: MSTORE8 [[ADDR]], [[VAL]] - - store i8 %val, ptr addrspace(1) @glob_i8 + store i8 %val, ptr addrspace(1) %glob ret void } -define void @storei32(i32 %val) nounwind { -; CHECK-LABEL: @storei32 -; CHECK: ARGUMENT [[VAL:\$[0-9]+]], 0 -; CHECK: CONST_I256 [[TMP1:\$[0-9]+]], 224 -; CHECK: SHL [[SHL_VAL:\$[0-9]+]], [[VAL]], [[TMP1]] -; CHECK: CONST_I256 [[ADDR:\$[0-9]+]], @glob_i32 -; CHECK: MLOAD [[ORIG_MEM:\$[0-9]+]], [[ADDR]] -; CHECK: CONST_I256 [[TMP2:\$[0-9]+]], 32 -; CHECK: SHR [[SHR_MEM:\$[0-9]+]], [[ORIG_MEM]], [[TMP2]] -; CHECK: SHL [[SHL_MEM:\$[0-9]+]], [[SHR_MEM]], [[TMP2]] -; CHECK: OR [[RES_MEM:\$[0-9]+]], [[SHL_VAL]], [[SHL_MEM]] -; CHECK: MSTORE [[ADDR]], [[RES_MEM]] +define void @storei32(i32 %val, ptr addrspace(1) %glob) nounwind { +; CHECK-LABEL: storei32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: SHL +; CHECK-NEXT: OR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: MSTORE +; CHECK-NEXT: JUMP - store i32 %val, ptr addrspace(1) @glob_i32 + store i32 %val, ptr addrspace(1) %glob ret void } diff --git a/llvm/test/CodeGen/EVM/tstorage.ll b/llvm/test/CodeGen/EVM/tstorage.ll index 8bb70f90e0e0..d7ab2b3ce9ac 100644 --- a/llvm/test/CodeGen/EVM/tstorage.ll +++ b/llvm/test/CodeGen/EVM/tstorage.ll @@ -1,22 +1,27 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define void @tstore(ptr addrspace(6) %key, i256 %val) nounwind { -; CHECK-LABEL: @tstore -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: ARGUMENT [[IN2:\$[0-9]+]], 1 -; CHECK: TSTORE [[IN1]], [[IN2]] +; CHECK-LABEL: tstore: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TSTORE +; CHECK-NEXT: JUMP store i256 %val, ptr addrspace(6) %key, align 32 ret void } define i256 @tload(ptr addrspace(6) %key) nounwind { -; CHECK-LABEL: @tload -; CHECK: ARGUMENT [[IN1:\$[0-9]+]], 0 -; CHECK: TLOAD [[RES1:\$[0-9]+]], [[IN1]] +; CHECK-LABEL: tload: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: TLOAD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %val = load i256, ptr addrspace(6) %key, align 32 ret i256 %val diff --git a/llvm/test/CodeGen/EVM/unused_function_arguments.ll b/llvm/test/CodeGen/EVM/unused_function_arguments.ll index 50e68f31e91c..b9e818046e6d 100644 --- a/llvm/test/CodeGen/EVM/unused_function_arguments.ll +++ b/llvm/test/CodeGen/EVM/unused_function_arguments.ll @@ -1,51 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @foo(i256 %a1, i256 %a2, i256 %a3) nounwind { -; CHECK-LABEL: @foo -; CHECK: JUMPDEST -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: POP -; CHECK-NEXT: POP -; CHECK-NEXT: DUP1 -; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: JUMP +; CHECK-LABEL: foo: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %x1 = add i256 %a1, %a1 ret i256 %x1 } define i256 @wat(i256 %a1, i256 %a2, i256 %a3) nounwind { -; CHECK-LABEL: @wat -; CHECK: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: DUP1 -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: POP -; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: JUMP +; CHECK-LABEL: wat: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %x1 = add i256 %a2, %a2 ret i256 %x1 } define i256 @bar() nounwind { -; CHECK-LABEL: @bar -; CHECK: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET0 -; CHECK-NEXT: PUSH1 3 -; CHECK-NEXT: PUSH1 2 -; CHECK-NEXT: PUSH1 1 -; CHECK-NEXT: PUSH4 @foo -; CHECK-NEXT: JUMP -; CHECK-LABEL: .FUNC_RET0: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: JUMP +; CHECK-LABEL: bar: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 @.FUNC_RET0 +; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH4 @foo +; CHECK-NEXT: JUMP +; CHECK-NEXT: .FUNC_RET0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = call i256 @foo(i256 1, i256 2, i256 3) ret i256 %res diff --git a/llvm/test/CodeGen/EVM/zero_any_extload.ll b/llvm/test/CodeGen/EVM/zero_any_extload.ll index 51971aa83a22..1f24642ece45 100644 --- a/llvm/test/CodeGen/EVM/zero_any_extload.ll +++ b/llvm/test/CodeGen/EVM/zero_any_extload.ll @@ -1,63 +1,88 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i8 @load_anyext_i8(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_anyext_i8 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 248 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_anyext_i8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i8, ptr addrspace(1) %ptr ret i8 %load } define i16 @load_anyext_i16(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_anyext_i16 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 240 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_anyext_i16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 240 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i16, ptr addrspace(1) %ptr ret i16 %load } define i32 @load_anyext_i32(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_anyext_i32 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 224 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_anyext_i32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i32, ptr addrspace(1) %ptr ret i32 %load } define i64 @load_anyext_i64(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_anyext_i64 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 192 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_anyext_i64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 192 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i64, ptr addrspace(1) %ptr ret i64 %load } define i128 @load_anyext_i128(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_anyext_i128 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 128 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_anyext_i128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i128, ptr addrspace(1) %ptr ret i128 %load } define i256 @load_zeroext_i8(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_zeroext_i8 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 248 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_zeroext_i8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i8, ptr addrspace(1) %ptr %zext = zext i8 %load to i256 @@ -65,10 +90,14 @@ define i256 @load_zeroext_i8(ptr addrspace(1) %ptr) nounwind { } define i256 @load_zeroext_i16(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_zeroext_i16 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 240 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_zeroext_i16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 240 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i16, ptr addrspace(1) %ptr %zext = zext i16 %load to i256 @@ -76,10 +105,14 @@ define i256 @load_zeroext_i16(ptr addrspace(1) %ptr) nounwind { } define i256 @load_zeroext_i32(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_zeroext_i32 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 224 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_zeroext_i32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i32, ptr addrspace(1) %ptr %zext = zext i32 %load to i256 @@ -87,10 +120,14 @@ define i256 @load_zeroext_i32(ptr addrspace(1) %ptr) nounwind { } define i256 @load_zeroext_i64(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_zeroext_i64 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 192 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_zeroext_i64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 192 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i64, ptr addrspace(1) %ptr %zext = zext i64 %load to i256 @@ -98,10 +135,14 @@ define i256 @load_zeroext_i64(ptr addrspace(1) %ptr) nounwind { } define i256 @load_zeroext_i128(ptr addrspace(1) %ptr) nounwind { -; CHECK-LABEL: @load_zeroext_i128 -; CHECK: MLOAD [[LOAD:\$[0-9]+]], [[PTR:\$[0-9]+]] -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 128 -; CHECK: SHR [[SHR:\$[0-9]+]], [[LOAD]], [[EXT]] +; CHECK-LABEL: load_zeroext_i128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MLOAD +; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: SHR +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %load = load i128, ptr addrspace(1) %ptr %zext = zext i128 %load to i256 diff --git a/llvm/test/CodeGen/EVM/zext.ll b/llvm/test/CodeGen/EVM/zext.ll index b73f810fd440..3e03939ab3ee 100644 --- a/llvm/test/CodeGen/EVM/zext.ll +++ b/llvm/test/CodeGen/EVM/zext.ll @@ -1,48 +1,69 @@ -; RUN: llc --evm-keep-registers < %s | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" define i256 @zexti8(i8 %rs1) nounwind { -; CHECK-LABEL: @zexti8 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 255 -; CHECK: AND {{.*}}, {{.*}}, [[EXT]] +; CHECK-LABEL: zexti8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = zext i8 %rs1 to i256 ret i256 %res } define i256 @zexti16(i16 %rs1) nounwind { -; CHECK-LABEL: @zexti16 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 65535 -; CHECK: AND {{.*}}, {{.*}}, [[EXT]] +; CHECK-LABEL: zexti16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 65535 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = zext i16 %rs1 to i256 ret i256 %res } define i256 @zexti32(i32 %rs1) nounwind { -; CHECK-LABEL: @zexti32 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 4294967295 -; CHECK: AND {{.*}}, {{.*}}, [[EXT]] +; CHECK-LABEL: zexti32: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 4294967295 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = zext i32 %rs1 to i256 ret i256 %res } define i256 @zexti64(i64 %rs1) nounwind { -; CHECK-LABEL: @zexti64 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 18446744073709551615 -; CHECK: AND {{.*}}, {{.*}}, [[EXT]] +; CHECK-LABEL: zexti64: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH8 18446744073709551615 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = zext i64 %rs1 to i256 ret i256 %res } define i256 @zexti128(i128 %rs1) nounwind { -; CHECK-LABEL: @zexti128 -; CHECK: CONST_I256 [[EXT:\$[0-9]+]], 340282366920938463463374607431768211455 -; CHECK: AND {{.*}}, {{.*}}, [[EXT]] +; CHECK-LABEL: zexti128: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP %res = zext i128 %rs1 to i256 ret i256 %res From 27e7e233d2a90f35635767c8320d35cb733fae9f Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 30 Apr 2025 18:56:15 +0200 Subject: [PATCH 125/203] [EVM][MC] Emit immediate values using '0x'-prefixed hexadecimal notation Remove a space between the instruction opcode and its first argument. Add padding to the mnemonic so that it is 16 characters long. Also disable generation of '.note.GNU-stack' section. --- llvm/lib/Target/EVM/EVMInstrInfo.td | 192 ++++----- llvm/lib/Target/EVM/EVMMCInstLower.cpp | 4 +- .../EVM/MCTargetDesc/EVMInstPrinter.cpp | 12 +- .../Target/EVM/MCTargetDesc/EVMMCAsmInfo.h | 8 +- .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 2 +- .../lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp | 2 +- llvm/test/CodeGen/EVM/aa-eval.ll | 1 + llvm/test/CodeGen/EVM/add.ll | 4 +- llvm/test/CodeGen/EVM/aext.ll | 2 +- .../CodeGen/EVM/bitmanipulation-intrinsics.ll | 386 +++++++++--------- .../CodeGen/EVM/bps-compress-stack-bug.ll | 1 + llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll | 1 + llvm/test/CodeGen/EVM/br.ll | 2 +- .../EVM/branch-folder-after-stackification.ll | 6 +- .../EVM/dont-avoid-shift-transformations.ll | 24 +- llvm/test/CodeGen/EVM/icmp.ll | 2 +- .../CodeGen/EVM/load-narrowing-disable.ll | 2 +- .../EVM/machine-sink-cheap-instructions.ll | 4 +- llvm/test/CodeGen/EVM/memintrinsics.ll | 2 +- llvm/test/CodeGen/EVM/mul.ll | 2 +- llvm/test/CodeGen/EVM/sext.ll | 14 +- llvm/test/CodeGen/EVM/signextload.ll | 10 +- llvm/test/CodeGen/EVM/stack-ops-commutable.ll | 20 +- llvm/test/CodeGen/EVM/stack-ops.ll | 8 +- llvm/test/CodeGen/EVM/sub.ll | 2 +- llvm/test/CodeGen/EVM/truncstore.ll | 6 +- .../CodeGen/EVM/unused_function_arguments.ll | 6 +- llvm/test/CodeGen/EVM/zero_any_extload.ll | 20 +- llvm/test/CodeGen/EVM/zext.ll | 10 +- llvm/test/MC/EVM/imm-hex-output.ll | 21 + 30 files changed, 407 insertions(+), 369 deletions(-) create mode 100644 llvm/test/MC/EVM/imm-hex-output.ll diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 2f605e875ad8..fbba20f55ee8 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -259,7 +259,7 @@ let isCall = 1 in def PseudoCALL multiclass BinaryInst inst, int cost> : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), [(set GPR:$dst, (node GPR:$lhs, GPR:$rhs))], - opcode_str, " $dst, $lhs, $rhs", inst, cost>; + opcode_str, "$dst, $lhs, $rhs", inst, cost>; let isCommutable = 1 in { defm ADD : BinaryInst; @@ -275,23 +275,23 @@ let isCommutable = 1 in { defm ADDMOD : I<(outs GPR:$dst), (ins GPR:$add_op1, GPR:$add_op2, GPR:$denom), [(set GPR:$dst, (int_evm_addmod GPR:$add_op1, GPR:$add_op2, GPR:$denom))], - "ADDMOD", " $dst, $add_op1, $add_op2, $denom", 0x08, 8>; + "ADDMOD", "$dst, $add_op1, $add_op2, $denom", 0x08, 8>; defm MULMOD : I<(outs GPR:$dst), (ins GPR:$mul_op1, GPR:$mul_op2, GPR:$denom), [(set GPR:$dst, (int_evm_mulmod GPR:$mul_op1, GPR:$mul_op2, GPR:$denom))], - "MULMOD", " $dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; + "MULMOD", "$dst, $mul_op1, $mul_op2, $denom", 0x09, 8>; } defm EXP : I<(outs GPR:$dst), (ins GPR:$base, GPR:$exp), [(set GPR:$dst, (int_evm_exp GPR:$base, GPR:$exp))], - "EXP", " $dst, $base, $exp", 0x0A, 10>; + "EXP", "$dst, $base, $exp", 0x0A, 10>; defm SIGNEXTEND : I<(outs GPR:$dst), (ins GPR:$size, GPR:$src), [(set GPR:$dst, (int_evm_signextend GPR:$size, GPR:$src))], - "SIGNEXTEND", " $dst, $size, $src", 0x0B, 5>; + "SIGNEXTEND", "$dst, $size, $src", 0x0B, 5>; // The first operand of SIGNEXTEND is the type size in bytes of // the value being extendent minus one. @@ -326,7 +326,7 @@ multiclass ComparisonInst inst, int cost> : I<(outs GPR:$dst), (ins GPR:$lhs, GPR:$rhs), [(set GPR:$dst, (setcc GPR:$lhs, GPR:$rhs, cond))], - opcode_str, " $dst, $lhs, $rhs", inst, cost>; + opcode_str, "$dst, $lhs, $rhs", inst, cost>; let isCompare = 1 in { defm ULT : ComparisonInst; @@ -338,7 +338,7 @@ defm EQ : ComparisonInst; defm ISZERO : I<(outs GPR:$dst), (ins GPR:$src), [(set GPR:$dst, (setcc GPR:$src, 0, SETEQ))], - "ISZERO", " $dst, $src", 0x15, 3>; + "ISZERO", "$dst, $src", 0x15, 3>; } // isCompare = 1 // Patterns for comparison operations that have no @@ -363,18 +363,18 @@ let isCommutable = 1 in { } defm NOT : I<(outs GPR:$dst), (ins GPR:$src), [(set GPR:$dst, (not GPR:$src))], - "NOT", " $dst, $src", 0x19, 3>; + "NOT", "$dst, $src", 0x19, 3>; let mayLoad = 1 in defm SHA3 : I<(outs GPR:$dst), (ins GPR:$offset, GPR:$size), [(set GPR:$dst, (int_evm_sha3 GPR:$offset, GPR:$size))], - "SHA3", " $dst, $offset, $size", 0x20, 30>; + "SHA3", "$dst, $offset, $size", 0x20, 30>; defm BYTE : I<(outs GPR:$dst), (ins GPR:$idx, GPR:$val), [(set GPR:$dst, (int_evm_byte GPR:$idx, GPR:$val))], - "BYTE", " $dst, $idx, $val", 0x1a, 3>; + "BYTE", "$dst, $idx, $val", 0x1a, 3>; //===----------------------------------------------------------------------===// @@ -384,7 +384,7 @@ defm BYTE multiclass ShiftInst inst, int cost> : I<(outs GPR:$dst), (ins GPR:$val, GPR:$shift), [(set GPR:$dst, (node GPR:$shift, GPR:$val))], - opcode_str, " $dst, $shift, $val", inst, cost>; + opcode_str, "$dst, $shift, $val", inst, cost>; defm SHL : ShiftInst; defm SHR : ShiftInst; @@ -403,13 +403,13 @@ let isBranch = 1, isTerminator = 1 in { // The condition operand is a boolean value which EVM represents as i256. defm JUMPI : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], - "JUMPI", " $dst, $cond", 0x57, 10>; + "JUMPI", "$dst, $cond", 0x57, 10>; def JUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst, GPR:$cond), []>; def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; def PseudoJUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; let isBarrier = 1 in { - defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", " $dst", + defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", "$dst", 0x56, 8>; def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; } // isBarrier = 1 @@ -445,57 +445,57 @@ let mayLoad = 1 in { defm MLOAD : I<(outs GPR:$dst), (ins GPR:$offset), [(set GPR:$dst, (load_heap GPR:$offset))], - "MLOAD", " $dst, $offset", 0x51, 3>; + "MLOAD", "$dst, $offset", 0x51, 3>; defm SLOAD : I<(outs GPR:$dst), (ins GPR:$key), [(set GPR:$dst, (load_storage GPR:$key))], - "SLOAD", " $dst, $key", 0x54, 100>; + "SLOAD", "$dst, $key", 0x54, 100>; defm TLOAD : I<(outs GPR:$dst), (ins GPR:$key), [(set GPR:$dst, (load_tstorage GPR:$key))], - "TLOAD", " $dst, $key", 0x5c, 100>; + "TLOAD", "$dst, $key", 0x5c, 100>; } let mayStore = 1 in { defm MSTORE : I<(outs), (ins GPR:$offset, GPR:$val), [(store_heap GPR:$val, GPR:$offset)], - "MSTORE", " $offset, $val", 0x52, 3>; + "MSTORE", "$offset, $val", 0x52, 3>; defm MSTORE8 : I<(outs), (ins GPR:$offset, GPR:$val), [(int_evm_mstore8 GPR:$offset, GPR:$val)], - "MSTORE8", " $offset, $val", 0x53, 3>; + "MSTORE8", "$offset, $val", 0x53, 3>; defm SSTORE : I<(outs), (ins GPR:$key, GPR:$val), [(store_storage GPR:$val, GPR:$key)], - "SSTORE", " $key, $val", 0x55, 100>; + "SSTORE", "$key, $val", 0x55, 100>; defm TSTORE : I<(outs), (ins GPR:$key, GPR:$val), [(store_tstorage GPR:$val, GPR:$key)], - "TSTORE", " $key, $val", 0x5d, 100>; + "TSTORE", "$key, $val", 0x5d, 100>; } let mayStore = 1, mayLoad = 1 in defm MCOPY : I<(outs), (ins GPR:$dst, GPR:$src, GPR:$size), [], - "MCOPY", " $dst, $src, $size", 0x5E, 3>; + "MCOPY", "$dst, $src, $size", 0x5E, 3>; def : Pat<(EVMMemcpy_heap GPR:$dst, GPR:$src, GPR:$size), (MCOPY GPR:$dst, GPR:$src, GPR:$size)>; let hasSideEffects = 1 in { defm PC : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_pc))], - "PC", " $dst", 0x58, 2>; + "PC", "$dst", 0x58, 2>; defm GAS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gas))], - "GAS", " $dst", 0x5A, 2>; + "GAS", "$dst", 0x5A, 2>; } defm MSIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_msize))], - "MSIZE", " $dst", 0x59, 2>; + "MSIZE", "$dst", 0x59, 2>; // The i8 store is handled a speciall way, as EVM has a dedicated instruction // for this. @@ -550,82 +550,82 @@ def : Pat<(truncstorei8 GPR:$val, GPR:$off), (STACK_STORE GPR:$off, 0, GPR:$val) let isAsCheapAsAMove = 1, isReMaterializable = 1 in { defm ADDRESS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_address))], - "ADDRESS", " $dst", 0x30, 2>; + "ADDRESS", "$dst", 0x30, 2>; defm ORIGIN : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], - "ORIGIN", " $dst", 0x32, 2>; + "ORIGIN", "$dst", 0x32, 2>; defm CALLER : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_caller))], - "CALLER", " $dst", 0x33, 2>; + "CALLER", "$dst", 0x33, 2>; defm CALLVALUE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_callvalue))], - "CALLVALUE", " $dst", 0x34, 2>; + "CALLVALUE", "$dst", 0x34, 2>; defm CALLDATASIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_calldatasize))], - "CALLDATASIZE", " $dst", 0x36, 2>; + "CALLDATASIZE", "$dst", 0x36, 2>; defm CODESIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], - "CODESIZE", " $dst", 0x38, 2>; + "CODESIZE", "$dst", 0x38, 2>; defm GASPRICE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], - "GASPRICE", " $dst", 0x3A, 2>; + "GASPRICE", "$dst", 0x3A, 2>; defm COINBASE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], - "COINBASE", " $dst", 0x41, 2>; + "COINBASE", "$dst", 0x41, 2>; defm TIMESTAMP : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], - "TIMESTAMP", " $dst", 0x42, 2>; + "TIMESTAMP", "$dst", 0x42, 2>; defm NUMBER : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], - "NUMBER", " $dst", 0x43, 2>; + "NUMBER", "$dst", 0x43, 2>; defm DIFFICULTY : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], - "DIFFICULTY", " $dst", 0x44, 2>; + "DIFFICULTY", "$dst", 0x44, 2>; defm GASLIMIT : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], - "GASLIMIT", " $dst", 0x45, 2>; + "GASLIMIT", "$dst", 0x45, 2>; defm CHAINID : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], - "CHAINID", " $dst", 0x46, 2>; + "CHAINID", "$dst", 0x46, 2>; defm BASEFEE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], - "BASEFEE", " $dst", 0x48, 2>; + "BASEFEE", "$dst", 0x48, 2>; defm BLOBBASEFEE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], - "BLOBBASEFEE", " $dst", 0x4A, 2>; + "BLOBBASEFEE", "$dst", 0x4A, 2>; } defm BALANCE : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_balance GPR:$addr))], - "BALANCE", " $dst, $addr", 0x31, 100>; + "BALANCE", "$dst, $addr", 0x31, 100>; let mayLoad = 1 in defm CALLDATALOAD : I<(outs GPR:$dst), (ins GPR:$off), [(set GPR:$dst, (int_evm_calldataload GPR:$off))], - "CALLDATALOAD", " $dst, $off", 0x35, 2>; + "CALLDATALOAD", "$dst, $off", 0x35, 2>; def : Pat<(load_call_data GPR:$off), (CALLDATALOAD GPR:$off)>; let mayStore = 1 in defm CALLDATACOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], - "CALLDATACOPY", " $dst_off, $src_off, $size", 0x37, 3>; + "CALLDATACOPY", "$dst_off, $src_off, $size", 0x37, 3>; def : Pat<(EVMMemcpy_call_data GPR:$dst, GPR:$src, GPR:$size), (CALLDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; @@ -633,7 +633,7 @@ def : Pat<(EVMMemcpy_call_data GPR:$dst, GPR:$src, GPR:$size), let mayStore = 1 in defm CODECOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], - "CODECOPY", " $dst_off, $src_off, $size", 0x39, 3>; + "CODECOPY", "$dst_off, $src_off, $size", 0x39, 3>; def : Pat<(EVMMemcpy_code GPR:$dst, GPR:$src, GPR:$size), (CODECOPY GPR:$dst, GPR:$src, GPR:$size)>; @@ -641,22 +641,22 @@ def : Pat<(EVMMemcpy_code GPR:$dst, GPR:$src, GPR:$size), defm EXTCODESIZE : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_extcodesize GPR:$addr))], - "EXTCODESIZE", " $dst, $addr", 0x3B, 100>; + "EXTCODESIZE", "$dst, $addr", 0x3B, 100>; let mayStore = 1 in defm EXTCODECOPY : I<(outs), (ins GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size), [(int_evm_extcodecopy GPR:$addr, GPR:$dst_off, GPR:$src_off, GPR:$size)], - "EXTCODECOPY", " $addr, $dst_off, $src_off, $size", 0x3C, 100>; + "EXTCODECOPY", "$addr, $dst_off, $src_off, $size", 0x3C, 100>; defm RETURNDATASIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_returndatasize))], - "RETURNDATASIZE", " $dst", 0x3D, 2>; + "RETURNDATASIZE", "$dst", 0x3D, 2>; let mayStore = 1 in defm RETURNDATACOPY : I<(outs), (ins GPR:$dst_off, GPR:$src_off, GPR:$size), [], - "RETURNDATACOPY", " $dst_off, $src_off, $size", 0x3E, 3>; + "RETURNDATACOPY", "$dst_off, $src_off, $size", 0x3E, 3>; def : Pat<(EVMMemcpy_return_data GPR:$dst, GPR:$src, GPR:$size), (RETURNDATACOPY GPR:$dst, GPR:$src, GPR:$size)>; @@ -664,22 +664,22 @@ def : Pat<(EVMMemcpy_return_data GPR:$dst, GPR:$src, GPR:$size), defm EXTCODEHASH : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_extcodehash GPR:$addr))], - "EXTCODEHASH", " $dst, $addr", 0x3F, 100>; + "EXTCODEHASH", "$dst, $addr", 0x3F, 100>; defm BLOCKHASH : I<(outs GPR:$dst), (ins GPR:$addr), [(set GPR:$dst, (int_evm_blockhash GPR:$addr))], - "BLOCKHASH", " $dst, $addr", 0x40, 20>; + "BLOCKHASH", "$dst, $addr", 0x40, 20>; let hasSideEffects = 1 in defm SELFBALANCE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_selfbalance))], - "SELFBALANCE", " $dst", 0x47, 5>; + "SELFBALANCE", "$dst", 0x47, 5>; defm BLOBHASH : I<(outs GPR:$dst), (ins GPR:$index), [(set GPR:$dst, (int_evm_blobhash GPR:$index))], - "BLOBHASH", " $dst, $index", 0x49, 3>; + "BLOBHASH", "$dst, $index", 0x49, 3>; //===----------------------------------------------------------------------===// // EVM instructions for logging. @@ -689,28 +689,28 @@ let mayLoad = 1, hasSideEffects = 1 in { defm LOG0 : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_log0 GPR:$offset, GPR:$size)], - "LOG0", " $offset, $size", 0xA0, 375>; + "LOG0", "$offset, $size", 0xA0, 375>; defm LOG1 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1), [(int_evm_log1 GPR:$offset, GPR:$size, GPR:$t1)], - "LOG1", " $offset, $size, $t1", 0xA1, 750>; + "LOG1", "$offset, $size, $t1", 0xA1, 750>; defm LOG2 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2), [(int_evm_log2 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2)], - "LOG2", " $offset, $size, $t1, $t2", 0xA2, 1125>; + "LOG2", "$offset, $size, $t1, $t2", 0xA2, 1125>; defm LOG3 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3), [(int_evm_log3 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3)], - "LOG3", " $offset, $size, $t1, $t2, $t3", 0xA3, 1500>; + "LOG3", "$offset, $size, $t1, $t2, $t3", 0xA3, 1500>; defm LOG4 : I<(outs), (ins GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, GPR:$t4), [(int_evm_log4 GPR:$offset, GPR:$size, GPR:$t1, GPR:$t2, GPR:$t3, GPR:$t4)], - "LOG4", " $offset, $size, $t1, $t2, $t3, $t4", 0xA4, 1875>; + "LOG4", "$offset, $size, $t1, $t2, $t3, $t4", 0xA4, 1875>; } //===----------------------------------------------------------------------===// @@ -721,7 +721,7 @@ let mayLoad = 1, hasSideEffects = 1 in defm CREATE : I<(outs GPR:$dst), (ins GPR:$value, GPR:$offset, GPR:$size), [(set GPR:$dst, (int_evm_create GPR:$value, GPR:$offset, GPR:$size))], - "CREATE", " $dst, $value, $offset, $size", 0xF0, 32000>; + "CREATE", "$dst, $value, $offset, $size", 0xF0, 32000>; let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { defm CALL @@ -729,7 +729,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_call GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "CALL", " $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + "CALL", "$dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", 0xF1, 100>; defm CALLCODE @@ -737,7 +737,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$arg_size, GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_callcode GPR:$gas, GPR:$addr, GPR:$value, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "CALLCODE", " $dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", + "CALLCODE", "$dst, $gas, $addr, $value, $arg_off, $arg_size, $ret_off, $ret_size", 0xF2, 100>; defm DELEGATECALL @@ -745,7 +745,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_delegatecall GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "DELEGATECALL", " $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + "DELEGATECALL", "$dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", 0xF4, 100>; defm STATICCALL @@ -753,7 +753,7 @@ let isCall = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1 in { GPR:$ret_off, GPR:$ret_size), [(set GPR:$dst, (int_evm_staticcall GPR:$gas, GPR:$addr, GPR:$arg_off, GPR:$arg_size, GPR:$ret_off, GPR:$ret_size))], - "STATICCALL", " $dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", + "STATICCALL", "$dst, $gas, $addr, $arg_off, $arg_size, $ret_off, $ret_size", 0xFA, 100>; } @@ -773,18 +773,18 @@ let isTerminator = 1, isBarrier = 1 in { defm REVERT : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_revert GPR:$offset, GPR:$size)], - "REVERT", " $offset, $size", 0xFD, 0>; + "REVERT", "$offset, $size", 0xFD, 0>; defm RETURN : I<(outs), (ins GPR:$offset, GPR:$size), [(int_evm_return GPR:$offset, GPR:$size)], - "RETURN", " $offset, $size", 0xF3, 0>; + "RETURN", "$offset, $size", 0xF3, 0>; } let hasSideEffects = 1 in { defm SELFDESTRUCT : I<(outs), (ins GPR:$addr), [(int_evm_selfdestruct GPR:$addr)], - "SELFDESTRUCT", " $addr", 0xFF, 5000>; + "SELFDESTRUCT", "$addr", 0xFF, 5000>; let isTerminator = 1, isBarrier = 1 in { defm STOP : I<(outs), (ins), [(int_evm_stop)], "STOP", "", 0x00, 0>; @@ -826,7 +826,7 @@ foreach I = {1 - 32} in { } // Define stack PUSH* instructions -def PUSH1_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH1 $imm", +def PUSH1_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH1$imm", 0x60, 3> { bits<8> imm; let Inst{15-8} = Opc; @@ -835,7 +835,7 @@ def PUSH1_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH1 $imm", let BaseName = "PUSH1"; } -def PUSH2_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH2 $imm", +def PUSH2_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH2$imm", 0x61, 3> { bits<16> imm; let Inst{23-16} = Opc; @@ -844,7 +844,7 @@ def PUSH2_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH2 $imm", let BaseName = "PUSH2"; } -def PUSH3_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH3 $imm", +def PUSH3_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH3$imm", 0x62, 3> { bits<24> imm; let Inst{31-24} = Opc; @@ -853,7 +853,7 @@ def PUSH3_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH3 $imm", let BaseName = "PUSH3"; } -def PUSH4_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH4 $imm", +def PUSH4_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH4$imm", 0x63, 3> { bits<32> imm; let Inst{39-32} = Opc; @@ -862,7 +862,7 @@ def PUSH4_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH4 $imm", let BaseName = "PUSH4"; } -def PUSH5_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH5 $imm", +def PUSH5_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH5$imm", 0x64, 3> { bits<40> imm; let Inst{47-40} = Opc; @@ -871,7 +871,7 @@ def PUSH5_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH5 $imm", let BaseName = "PUSH5"; } -def PUSH6_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH6 $imm", +def PUSH6_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH6$imm", 0x65, 3> { bits<48> imm; let Inst{55-48} = Opc; @@ -880,7 +880,7 @@ def PUSH6_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH6 $imm", let BaseName = "PUSH6"; } -def PUSH7_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH7 $imm", +def PUSH7_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH7$imm", 0x66, 3> { bits<56> imm; let Inst{63-56} = Opc; @@ -889,7 +889,7 @@ def PUSH7_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH7 $imm", let BaseName = "PUSH7"; } -def PUSH8_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH8 $imm", +def PUSH8_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH8$imm", 0x67, 3> { bits<64> imm; let Inst{71-64} = Opc; @@ -898,7 +898,7 @@ def PUSH8_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH8 $imm", let BaseName = "PUSH8"; } -def PUSH9_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH9 $imm", +def PUSH9_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH9$imm", 0x68, 3> { bits<72> imm; let Inst{79-72} = Opc; @@ -908,7 +908,7 @@ def PUSH9_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH9 $imm", let DecoderMethod = "decodePUSH<9>"; } -def PUSH10_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH10 $imm", +def PUSH10_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH10$imm", 0x69, 3> { bits<80> imm; let Inst{87-80} = Opc; @@ -918,7 +918,7 @@ def PUSH10_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH10 $imm", let DecoderMethod = "decodePUSH<10>"; } -def PUSH11_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH11 $imm", +def PUSH11_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH11$imm", 0x6a, 3> { bits<88> imm; let Inst{95-88} = Opc; @@ -928,7 +928,7 @@ def PUSH11_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH11 $imm", let DecoderMethod = "decodePUSH<11>"; } -def PUSH12_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH12 $imm", +def PUSH12_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH12$imm", 0x6b, 3> { bits<96> imm; let Inst{103-96} = Opc; @@ -938,7 +938,7 @@ def PUSH12_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH12 $imm", let DecoderMethod = "decodePUSH<12>"; } -def PUSH13_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH13 $imm", +def PUSH13_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH13$imm", 0x6c, 3> { bits<104> imm; let Inst{111-104} = Opc; @@ -948,7 +948,7 @@ def PUSH13_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH13 $imm", let DecoderMethod = "decodePUSH<13>"; } -def PUSH14_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH14 $imm", +def PUSH14_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH14$imm", 0x6d, 3> { bits<112> imm; let Inst{119-112} = Opc; @@ -958,7 +958,7 @@ def PUSH14_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH14 $imm", let DecoderMethod = "decodePUSH<14>"; } -def PUSH15_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH15 $imm", +def PUSH15_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH15$imm", 0x6e, 3> { bits<120> imm; let Inst{127-120} = Opc; @@ -968,7 +968,7 @@ def PUSH15_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH15 $imm", let DecoderMethod = "decodePUSH<15>"; } -def PUSH16_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH16 $imm", +def PUSH16_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH16$imm", 0x6f, 3> { bits<128> imm; let Inst{135-128} = Opc; @@ -978,7 +978,7 @@ def PUSH16_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH16 $imm", let DecoderMethod = "decodePUSH<16>"; } -def PUSH17_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH17 $imm", +def PUSH17_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH17$imm", 0x70, 3> { bits<136> imm; let Inst{143-136} = Opc; @@ -988,7 +988,7 @@ def PUSH17_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH17 $imm", let DecoderMethod = "decodePUSH<17>"; } -def PUSH18_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH18 $imm", +def PUSH18_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH18$imm", 0x71, 3> { bits<144> imm; let Inst{151-144} = Opc; @@ -998,7 +998,7 @@ def PUSH18_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH18 $imm", let DecoderMethod = "decodePUSH<18>"; } -def PUSH19_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH19 $imm", +def PUSH19_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH19$imm", 0x72, 3> { bits<152> imm; let Inst{159-152} = Opc; @@ -1008,7 +1008,7 @@ def PUSH19_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH19 $imm", let DecoderMethod = "decodePUSH<19>"; } -def PUSH20_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH20 $imm", +def PUSH20_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH20$imm", 0x73, 3> { bits<160> imm; let Inst{167-160} = Opc; @@ -1018,7 +1018,7 @@ def PUSH20_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH20 $imm", let DecoderMethod = "decodePUSH<20>"; } -def PUSH21_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH21 $imm", +def PUSH21_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH21$imm", 0x74, 3> { bits<168> imm; let Inst{175-168} = Opc; @@ -1028,7 +1028,7 @@ def PUSH21_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH21 $imm", let DecoderMethod = "decodePUSH<21>"; } -def PUSH22_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH22 $imm", +def PUSH22_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH22$imm", 0x75, 3> { bits<176> imm; let Inst{183-176} = Opc; @@ -1038,7 +1038,7 @@ def PUSH22_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH22 $imm", let DecoderMethod = "decodePUSH<22>"; } -def PUSH23_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH23 $imm", +def PUSH23_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH23$imm", 0x76, 3> { bits<184> imm; let Inst{191-184} = Opc; @@ -1048,7 +1048,7 @@ def PUSH23_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH23 $imm", let DecoderMethod = "decodePUSH<23>"; } -def PUSH24_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH24 $imm", +def PUSH24_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH24$imm", 0x77, 3> { bits<192> imm; let Inst{199-192} = Opc; @@ -1058,7 +1058,7 @@ def PUSH24_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH24 $imm", let DecoderMethod = "decodePUSH<24>"; } -def PUSH25_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH25 $imm", +def PUSH25_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH25$imm", 0x78, 3> { bits<200> imm; let Inst{207-200} = Opc; @@ -1068,7 +1068,7 @@ def PUSH25_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH25 $imm", let DecoderMethod = "decodePUSH<25>"; } -def PUSH26_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH26 $imm", +def PUSH26_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH26$imm", 0x79, 3> { bits<208> imm; let Inst{215-208} = Opc; @@ -1078,7 +1078,7 @@ def PUSH26_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH26 $imm", let DecoderMethod = "decodePUSH<26>"; } -def PUSH27_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH27 $imm", +def PUSH27_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH27$imm", 0x7a, 3> { bits<216> imm; let Inst{223-216} = Opc; @@ -1088,7 +1088,7 @@ def PUSH27_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH27 $imm", let DecoderMethod = "decodePUSH<27>"; } -def PUSH28_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH28 $imm", +def PUSH28_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH28$imm", 0x7b, 3> { bits<224> imm; let Inst{231-224} = Opc; @@ -1098,7 +1098,7 @@ def PUSH28_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH28 $imm", let DecoderMethod = "decodePUSH<28>"; } -def PUSH29_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH29 $imm", +def PUSH29_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH29$imm", 0x7c, 3> { bits<232> imm; let Inst{239-232} = Opc; @@ -1108,7 +1108,7 @@ def PUSH29_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH29 $imm", let DecoderMethod = "decodePUSH<29>"; } -def PUSH30_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH30 $imm", +def PUSH30_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH30$imm", 0x7d, 3> { bits<240> imm; let Inst{247-240} = Opc; @@ -1118,7 +1118,7 @@ def PUSH30_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH30 $imm", let DecoderMethod = "decodePUSH<30>"; } -def PUSH31_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH31 $imm", +def PUSH31_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH31$imm", 0x7e, 3> { bits<248> imm; let Inst{255-248} = Opc; @@ -1128,7 +1128,7 @@ def PUSH31_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH31 $imm", let DecoderMethod = "decodePUSH<31>"; } -def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32 $imm", +def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32$imm", 0x7f, 3> { bits<256> imm; let Inst{263-256} = Opc; diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index bd896467552b..d4ae7abc03df 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -16,6 +16,7 @@ #include "MCTargetDesc/EVMMCExpr.h" #include "MCTargetDesc/EVMMCTargetDesc.h" #include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -118,7 +119,8 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { // will be reallocated into the generic heap but not into the Ctx // arena and thus never deallocated. auto *Str = new (Ctx) SmallString<80>(); - CImmVal.toStringUnsigned(*Str); + CImmVal.toString(*Str, /*Radix=*/16, /*Signed=*/false, + /*formatAsCLiteral=*/false, /*UpperCase=*/true); MCOp = MCOperand::createExpr(EVMCImmMCExpr::create(*Str, Ctx)); } } break; diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp index 9ac4b2bf78e0..701859aca559 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp @@ -47,8 +47,14 @@ void EVMInstPrinter::printRegName(raw_ostream &OS, MCRegister RegNo) const { void EVMInstPrinter::printInst(const MCInst *MI, uint64_t Address, StringRef Annot, const MCSubtargetInfo &STI, raw_ostream &O) { - // Print the instruction (this uses the AsmStrings from the .td files). - printInstruction(MI, Address, O); + // Print the instruction manually instead of using the TableGen-generated + // printInstruction() to allow custom formatting of the mnemonic. + O << "\t"; + std::pair MnemonicInfo = getMnemonic(MI); + // Add padding to the mnemonic so that it is 16 characters long. + O << left_justify(MnemonicInfo.first, /*Width=*/16); + for (unsigned I = 0; I < MI->getNumOperands(); ++I) + printOperand(MI, I, O); // Print any additional variadic operands. const MCInstrDesc &Desc = MII.get(MI->getOpcode()); @@ -82,7 +88,7 @@ void EVMInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, if (Op.isReg()) { printRegName(O, Op.getReg()); } else if (Op.isImm()) { - O << Op.getImm(); + O << format_hex(Op.getImm(), 0, /*Upper=*/true); } else { Op.getExpr()->print(O, &MAI); } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h index 0a4df279354b..29248f1ca35e 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCAsmInfo.h @@ -18,10 +18,16 @@ namespace llvm { class Triple; -class EVMMCAsmInfo final : public MCAsmInfoELF { +// Inherit from MCAsmInfo instead of MCAsmInfoELF to enable overriding +// getNonexecutableStackSection(), as the base class functionality is minimal +// in this context. +class EVMMCAsmInfo final : public MCAsmInfo { public: explicit EVMMCAsmInfo(const Triple &TT); bool shouldOmitSectionDirective(StringRef) const override; + MCSection *getNonexecutableStackSection(MCContext &Ctx) const override { + return nullptr; + } }; } // namespace llvm diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp index 586d6612eaa8..d51b566a5c4d 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -106,7 +106,7 @@ unsigned EVMMCCodeEmitter::getMachineOpValue(const MCInst &MI, auto Kind = MO.getExpr()->getKind(); if (Kind == MCExpr::ExprKind::Target) { const auto *CImmExp = cast(MO.getExpr()); - Op = APInt(Op.getBitWidth(), CImmExp->getString(), /*radix=*/10); + Op = APInt(Op.getBitWidth(), CImmExp->getString(), /*radix=*/16); } else if (Kind == MCExpr::ExprKind::SymbolRef) { const auto *RefExpr = cast(MO.getExpr()); MCSymbolRefExpr::VariantKind Kind = RefExpr->getKind(); diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp index 2931f9b45b26..f2530def9dc1 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCExpr.cpp @@ -23,5 +23,5 @@ const EVMCImmMCExpr *EVMCImmMCExpr::create(const StringRef &Data, } void EVMCImmMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { - OS << Data; + OS << "0x" << Data; } diff --git a/llvm/test/CodeGen/EVM/aa-eval.ll b/llvm/test/CodeGen/EVM/aa-eval.ll index a59e89e91306..1479f7c6f883 100644 --- a/llvm/test/CodeGen/EVM/aa-eval.ll +++ b/llvm/test/CodeGen/EVM/aa-eval.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: opt -passes=aa-eval -aa-pipeline=basic-aa,evm-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" diff --git a/llvm/test/CodeGen/EVM/add.ll b/llvm/test/CodeGen/EVM/add.ll index 61d06295fa78..12f7aed61c23 100644 --- a/llvm/test/CodeGen/EVM/add.ll +++ b/llvm/test/CodeGen/EVM/add.ll @@ -20,7 +20,7 @@ define i256 @addrri(i256 %rs1) nounwind { ; CHECK-LABEL: addrri: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH9 18446744073709551616 +; CHECK-NEXT: PUSH9 0x10000000000000000 ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -33,7 +33,7 @@ define i256 @subrri(i256 %rs1) nounwind { ; CHECK-LABEL: subrri: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/aext.ll b/llvm/test/CodeGen/EVM/aext.ll index e3996638aebb..6231c1580511 100644 --- a/llvm/test/CodeGen/EVM/aext.ll +++ b/llvm/test/CodeGen/EVM/aext.ll @@ -8,7 +8,7 @@ define i8 @aexti8(i8 %rs1) nounwind { ; CHECK-LABEL: aexti8: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll index 33db114b34ee..37384d3a2e70 100644 --- a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -15,92 +15,92 @@ define i256 @bitreversetest(i256 %v) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SHR -; CHECK-NEXT: PUSH2 65280 +; CHECK-NEXT: PUSH2 0xFF00 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH3 16711680 +; CHECK-NEXT: PUSH3 0xFF0000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH4 4278190080 +; CHECK-NEXT: PUSH4 0xFF000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: PUSH1 0xC8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH5 1095216660480 +; CHECK-NEXT: PUSH5 0xFF00000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: PUSH1 0xB8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH6 280375465082880 +; CHECK-NEXT: PUSH6 0xFF0000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH7 71776119061217280 +; CHECK-NEXT: PUSH7 0xFF000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: PUSH1 0x98 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH8 18374686479671623680 +; CHECK-NEXT: PUSH8 0xFF00000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: PUSH1 0x88 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH9 4703919738795935662080 +; CHECK-NEXT: PUSH9 0xFF0000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH10 1204203453131759529492480 +; CHECK-NEXT: PUSH10 0xFF000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: PUSH1 0x68 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH11 308276084001730439550074880 +; CHECK-NEXT: PUSH11 0xFF00000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: PUSH1 0x58 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH12 78918677504442992524819169280 +; CHECK-NEXT: PUSH12 0xFF0000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: PUSH1 0x48 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH13 20203181441137406086353707335680 +; CHECK-NEXT: PUSH13 0xFF000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: PUSH1 0x38 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH14 5172014448931175958106549077934080 +; CHECK-NEXT: PUSH14 0xFF00000000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: PUSH1 0x28 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH15 1324035698926381045275276563951124480 +; CHECK-NEXT: PUSH15 0xFF0000000000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH16 338953138925153547590470800371487866880 +; CHECK-NEXT: PUSH16 0xFF000000000000000000000000000000 ; CHECK-NEXT: DUP6 -; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR @@ -108,133 +108,133 @@ define i256 @bitreversetest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH17 86772003564839308183160524895100893921280 +; CHECK-NEXT: PUSH17 0xFF00000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH18 22213632912598862894889094373145828843847680 +; CHECK-NEXT: PUSH18 0xFF0000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH19 5686690025625308901091608159525332184025006080 +; CHECK-NEXT: PUSH19 0xFF000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: PUSH1 0x28 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH20 1455792646560079078679451688838485039110401556480 +; CHECK-NEXT: PUSH20 0xFF00000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: PUSH1 0x38 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH21 372682917519380244141939632342652170012262798458880 +; CHECK-NEXT: PUSH21 0xFF0000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: PUSH1 0x48 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH22 95406826884961342500336545879718955523139276405473280 +; CHECK-NEXT: PUSH22 0xFF000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: PUSH1 0x58 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH23 24424147682550103680086155745208052613923654759801159680 +; CHECK-NEXT: PUSH23 0xFF00000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: PUSH1 0x68 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH24 6252581806732826542102055870773261469164455618509096878080 +; CHECK-NEXT: PUSH24 0xFF0000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH25 1600660942523603594778126302917954936106100638338328800788480 +; CHECK-NEXT: PUSH25 0xFF000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: PUSH1 0x88 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH26 409769201286042520263200333546996463643161763414612173001850880 +; CHECK-NEXT: PUSH26 0xFF00000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: PUSH1 0x98 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH27 104900915529226885187379285388031094692649411434140716288473825280 +; CHECK-NEXT: PUSH27 0xFF0000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH28 26854634375482082607969097059335960241318249327140023369849299271680 +; CHECK-NEXT: PUSH28 0xFF000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: PUSH1 0xB8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH29 6874786400123413147640088847190005821777471827747845982681420613550080 +; CHECK-NEXT: PUSH29 0xFF00000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: PUSH1 0xC8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH30 1759945318431593765795862744880641490375032787903448571566443677068820480 +; CHECK-NEXT: PUSH30 0xFF0000000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH31 450546001518488004043740862689444221536008393703282834321009581329618042880 +; CHECK-NEXT: PUSH31 0xFF000000000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SHL ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH32 6811299366900952671974763824040465167839410862684739061144563765171360567055 +; CHECK-NEXT: PUSH32 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP2 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP2 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH32 38597363079105398474523661669562635951089994888546854679819194669304376546645 +; CHECK-NEXT: PUSH32 0x5555555555555555555555555555555555555555555555555555555555555555 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP2 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR @@ -250,92 +250,92 @@ define i256 @bswaptest(i256 %v) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SHR -; CHECK-NEXT: PUSH2 65280 +; CHECK-NEXT: PUSH2 0xFF00 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH3 16711680 +; CHECK-NEXT: PUSH3 0xFF0000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH4 4278190080 +; CHECK-NEXT: PUSH4 0xFF000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: PUSH1 0xC8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH5 1095216660480 +; CHECK-NEXT: PUSH5 0xFF00000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: PUSH1 0xB8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH6 280375465082880 +; CHECK-NEXT: PUSH6 0xFF0000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH7 71776119061217280 +; CHECK-NEXT: PUSH7 0xFF000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: PUSH1 0x98 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH8 18374686479671623680 +; CHECK-NEXT: PUSH8 0xFF00000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: PUSH1 0x88 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH9 4703919738795935662080 +; CHECK-NEXT: PUSH9 0xFF0000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH10 1204203453131759529492480 +; CHECK-NEXT: PUSH10 0xFF000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: PUSH1 0x68 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH11 308276084001730439550074880 +; CHECK-NEXT: PUSH11 0xFF00000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: PUSH1 0x58 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH12 78918677504442992524819169280 +; CHECK-NEXT: PUSH12 0xFF0000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: PUSH1 0x48 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH13 20203181441137406086353707335680 +; CHECK-NEXT: PUSH13 0xFF000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: PUSH1 0x38 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH14 5172014448931175958106549077934080 +; CHECK-NEXT: PUSH14 0xFF00000000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: PUSH1 0x28 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH15 1324035698926381045275276563951124480 +; CHECK-NEXT: PUSH15 0xFF0000000000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH16 338953138925153547590470800371487866880 +; CHECK-NEXT: PUSH16 0xFF000000000000000000000000000000 ; CHECK-NEXT: DUP6 -; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: OR @@ -343,97 +343,97 @@ define i256 @bswaptest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH17 86772003564839308183160524895100893921280 +; CHECK-NEXT: PUSH17 0xFF00000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH18 22213632912598862894889094373145828843847680 +; CHECK-NEXT: PUSH18 0xFF0000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 24 +; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH19 5686690025625308901091608159525332184025006080 +; CHECK-NEXT: PUSH19 0xFF000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 40 +; CHECK-NEXT: PUSH1 0x28 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH20 1455792646560079078679451688838485039110401556480 +; CHECK-NEXT: PUSH20 0xFF00000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 56 +; CHECK-NEXT: PUSH1 0x38 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH21 372682917519380244141939632342652170012262798458880 +; CHECK-NEXT: PUSH21 0xFF0000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 72 +; CHECK-NEXT: PUSH1 0x48 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH22 95406826884961342500336545879718955523139276405473280 +; CHECK-NEXT: PUSH22 0xFF000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 88 +; CHECK-NEXT: PUSH1 0x58 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH23 24424147682550103680086155745208052613923654759801159680 +; CHECK-NEXT: PUSH23 0xFF00000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 104 +; CHECK-NEXT: PUSH1 0x68 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH24 6252581806732826542102055870773261469164455618509096878080 +; CHECK-NEXT: PUSH24 0xFF0000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP5 -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH25 1600660942523603594778126302917954936106100638338328800788480 +; CHECK-NEXT: PUSH25 0xFF000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 136 +; CHECK-NEXT: PUSH1 0x88 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH26 409769201286042520263200333546996463643161763414612173001850880 +; CHECK-NEXT: PUSH26 0xFF00000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 152 +; CHECK-NEXT: PUSH1 0x98 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH27 104900915529226885187379285388031094692649411434140716288473825280 +; CHECK-NEXT: PUSH27 0xFF0000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 168 +; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH28 26854634375482082607969097059335960241318249327140023369849299271680 +; CHECK-NEXT: PUSH28 0xFF000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 184 +; CHECK-NEXT: PUSH1 0xB8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH29 6874786400123413147640088847190005821777471827747845982681420613550080 +; CHECK-NEXT: PUSH29 0xFF00000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 200 +; CHECK-NEXT: PUSH1 0xC8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH30 1759945318431593765795862744880641490375032787903448571566443677068820480 +; CHECK-NEXT: PUSH30 0xFF0000000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH31 450546001518488004043740862689444221536008393703282834321009581329618042880 +; CHECK-NEXT: PUSH31 0xFF000000000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 232 +; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SHL ; CHECK-NEXT: OR ; CHECK-NEXT: OR @@ -451,44 +451,44 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-LABEL: ctpoptest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 255 -; CHECK-NEXT: PUSH32 454086624460063511464984254936031011189294057512315937409637584344757371137 -; CHECK-NEXT: PUSH16 20016609818878733144904388672456953615 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP8 -; CHECK-NEXT: PUSH1 129 +; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: DUP8 -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP7 -; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ; CHECK-NEXT: AND ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 @@ -496,22 +496,22 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP7 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP4 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: ADD @@ -527,76 +527,76 @@ define i256 @ctlztest(i256 %v) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 8 +; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 16 +; CHECK-NEXT: PUSH1 0x10 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH1 0x20 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 64 +; CHECK-NEXT: PUSH1 0x40 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 255 -; CHECK-NEXT: PUSH32 454086624460063511464984254936031011189294057512315937409637584344757371137 -; CHECK-NEXT: PUSH16 20016609818878733144904388672456953615 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP8 -; CHECK-NEXT: PUSH1 129 +; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: DUP8 -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP7 -; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 -; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 @@ -604,22 +604,22 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP7 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP4 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: ADD @@ -634,50 +634,50 @@ define i256 @cttztest(i256 %v) { ; CHECK-LABEL: cttztest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: DUP2 ; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: NOT ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH1 255 -; CHECK-NEXT: PUSH32 454086624460063511464984254936031011189294057512315937409637584344757371137 -; CHECK-NEXT: PUSH16 20016609818878733144904388672456953615 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP8 -; CHECK-NEXT: PUSH1 129 +; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: DUP8 -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP7 -; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 -; CHECK-NEXT: PUSH16 113427455640312821154458202477256070485 +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 23158417847463239084714197001737581570653996933128112807891516801582625927987 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP2 @@ -685,22 +685,22 @@ define i256 @cttztest(i256 %v) { ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP7 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP4 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 120 +; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: ADD diff --git a/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll b/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll index 859a92bfa4f5..36194c8312df 100644 --- a/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll +++ b/llvm/test/CodeGen/EVM/bps-compress-stack-bug.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: llc < %s ; calldata_struct_array_reencode test reduced with bugpoint. diff --git a/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll b/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll index 3377869f2596..5e6ed84bea78 100644 --- a/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll +++ b/llvm/test/CodeGen/EVM/bps-stack-solver-bug.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: llc < %s ; calldata_struct_array_reencode test reduced with bugpoint. diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index 081b87bbc45c..3b79560bf1c8 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -65,7 +65,7 @@ define i256 @loop(i256 %p1) nounwind { ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: DUP4 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP4 diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll index d732d9aa3948..1cd4640b1e5d 100644 --- a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -16,13 +16,13 @@ define i256 @test(i256 %arg) { ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: PUSH1 10 +; CHECK-NEXT: PUSH1 0xA ; CHECK-NEXT: PUSH4 @.BB0_3 ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB0_2: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: PUSH1 20 +; CHECK-NEXT: PUSH1 0x14 ; CHECK-NEXT: .BB0_3: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 @@ -32,7 +32,7 @@ define i256 @test(i256 %arg) { ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.4: ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: PUSH1 0x5 ; CHECK-NEXT: .BB0_5: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 diff --git a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll index aa0514a49970..1ca75b3e86f0 100644 --- a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll +++ b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll @@ -10,9 +10,9 @@ define i256 @test1(i256 %x) { ; CHECK-LABEL: test1: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH1 0x20 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: PUSH1 0x5 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -29,9 +29,9 @@ define i256 @test2(i256 %x) { ; CHECK-LABEL: test2: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH1 0x20 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: PUSH1 0x5 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -48,9 +48,9 @@ define i256 @test3(i256 %x, i256 %a) { ; CHECK-LABEL: test3: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 244 +; CHECK-NEXT: PUSH1 0xF4 ; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: SAR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 @@ -69,7 +69,7 @@ define i256 @test4(i256 %x) { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -86,7 +86,7 @@ define i256 @test5(i256 %x) { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -102,7 +102,7 @@ define i256 @test6(i256 %x, i256 %a) { ; CHECK-LABEL: test6: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: SAR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 @@ -119,9 +119,9 @@ define i256 @test7(i256 %x) { ; CHECK-LABEL: test7: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 254 +; CHECK-NEXT: PUSH1 0xFE ; CHECK-NEXT: SHR ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 @@ -139,7 +139,7 @@ define i256 @test8(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SGT -; CHECK-NEXT: PUSH1 5 +; CHECK-NEXT: PUSH1 0x5 ; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/icmp.ll b/llvm/test/CodeGen/EVM/icmp.ll index dd8f85ff6784..ee533dcea709 100644 --- a/llvm/test/CodeGen/EVM/icmp.ll +++ b/llvm/test/CodeGen/EVM/icmp.ll @@ -24,7 +24,7 @@ define i256 @icmp_big_imm_eq(i256 %a) nounwind { ; CHECK-LABEL: icmp_big_imm_eq: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH32 43576122634770472758325941782982599838796957244005075818703754470792663924736 +; CHECK-NEXT: PUSH32 0x6057361D00000000000000000000000000000000000000000000000000000000 ; CHECK-NEXT: EQ ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll index 9a62b4d1287b..d3ac55a6e495 100644 --- a/llvm/test/CodeGen/EVM/load-narrowing-disable.ll +++ b/llvm/test/CodeGen/EVM/load-narrowing-disable.ll @@ -8,7 +8,7 @@ define i1 @simplify_setcc(ptr addrspace(1) %glob) { ; CHECK-LABEL: simplify_setcc: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 2 +; CHECK-NEXT: PUSH1 0x2 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: MLOAD ; CHECK-NEXT: AND diff --git a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll index 22bd43eae755..512c15f41955 100644 --- a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll +++ b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll @@ -10,7 +10,7 @@ define i256 @test1(i256 %arg) { ; CHECK-LABEL: test1: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 10 +; CHECK-NEXT: PUSH1 0xA ; CHECK-NEXT: EQ ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI @@ -44,7 +44,7 @@ define i256 @test2(i256 %arg) { ; CHECK-LABEL: test2: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 10 +; CHECK-NEXT: PUSH1 0xA ; CHECK-NEXT: EQ ; CHECK-NEXT: PUSH4 @.BB1_2 ; CHECK-NEXT: JUMPI diff --git a/llvm/test/CodeGen/EVM/memintrinsics.ll b/llvm/test/CodeGen/EVM/memintrinsics.ll index 000034c7c730..e697d7f645b0 100644 --- a/llvm/test/CodeGen/EVM/memintrinsics.ll +++ b/llvm/test/CodeGen/EVM/memintrinsics.ll @@ -88,7 +88,7 @@ define fastcc void @calldata_to_heap_csize(ptr addrspace(1) %dest, ptr addrspace ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 42 +; CHECK-NEXT: PUSH1 0x2A ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: CALLDATACOPY ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll index 0c772d95f2a9..1ca8f3e539cd 100644 --- a/llvm/test/CodeGen/EVM/mul.ll +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -20,7 +20,7 @@ define i256 @mulrri(i256 %rs1) nounwind { ; CHECK-LABEL: mulrri: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH3 500000 +; CHECK-NEXT: PUSH3 0x7A120 ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: SUB ; CHECK-NEXT: MUL diff --git a/llvm/test/CodeGen/EVM/sext.ll b/llvm/test/CodeGen/EVM/sext.ll index dae54e28b9c4..3d0350ab82c6 100644 --- a/llvm/test/CodeGen/EVM/sext.ll +++ b/llvm/test/CodeGen/EVM/sext.ll @@ -8,7 +8,7 @@ define i256 @sexti1(i1 %rs1) nounwind { ; CHECK-LABEL: sexti1: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: SUB @@ -36,7 +36,7 @@ define i256 @sexti16(i16 %rs1) nounwind { ; CHECK-LABEL: sexti16: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SIGNEXTEND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -49,7 +49,7 @@ define i256 @sexti32(i32 %rs1) nounwind { ; CHECK-LABEL: sexti32: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: PUSH1 0x3 ; CHECK-NEXT: SIGNEXTEND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -62,7 +62,7 @@ define i256 @sexti64(i64 %rs1) nounwind { ; CHECK-LABEL: sexti64: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 7 +; CHECK-NEXT: PUSH1 0x7 ; CHECK-NEXT: SIGNEXTEND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -75,7 +75,7 @@ define i256 @sexti128(i128 %rs1) nounwind { ; CHECK-LABEL: sexti128: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 15 +; CHECK-NEXT: PUSH1 0xF ; CHECK-NEXT: SIGNEXTEND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -89,9 +89,9 @@ define i256 @sexti40(i40 %rs1) nounwind { ; CHECK-LABEL: sexti40: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 216 +; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/signextload.ll b/llvm/test/CodeGen/EVM/signextload.ll index ee664eba2d94..30c6bce33e08 100644 --- a/llvm/test/CodeGen/EVM/signextload.ll +++ b/llvm/test/CodeGen/EVM/signextload.ll @@ -9,7 +9,7 @@ define i256 @load_signexti8(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -24,7 +24,7 @@ define i256 @load_signexti16(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 240 +; CHECK-NEXT: PUSH1 0xF0 ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -39,7 +39,7 @@ define i256 @load_signexti32(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: PUSH1 0xE0 ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -54,7 +54,7 @@ define i256 @load_signexti64(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 192 +; CHECK-NEXT: PUSH1 0xC0 ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -69,7 +69,7 @@ define i256 @load_signexti128(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SAR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/stack-ops-commutable.ll b/llvm/test/CodeGen/EVM/stack-ops-commutable.ll index 05fa5ec4b4f8..851274ffd6f2 100644 --- a/llvm/test/CodeGen/EVM/stack-ops-commutable.ll +++ b/llvm/test/CodeGen/EVM/stack-ops-commutable.ll @@ -206,7 +206,7 @@ define void @first_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { ; CHECK-NEXT: DUP1 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP2 @@ -228,7 +228,7 @@ define i256 @first_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { ; CHECK-NEXT: DUP1 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP2 @@ -247,7 +247,7 @@ define void @second_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: SWAP4 ; CHECK-NEXT: POP @@ -269,7 +269,7 @@ define i256 @second_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: SWAP4 ; CHECK-NEXT: POP @@ -351,13 +351,13 @@ define void @commutable_not_in_function_entry() noreturn { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: CALLDATALOAD -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: .BB22_1: ; %header ; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: PUSH1 0x3 ; CHECK-NEXT: SIGNEXTEND ; CHECK-NEXT: SLT ; CHECK-NEXT: PUSH4 @.BB22_3 @@ -365,7 +365,7 @@ define void @commutable_not_in_function_entry() noreturn { ; CHECK-NEXT: ; %bb.2: ; %do ; CHECK-NEXT: ; in Loop: Header=BB22_1 Depth=1 ; CHECK-NEXT: DUP2 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: MUL ; CHECK-NEXT: SWAP2 @@ -375,13 +375,13 @@ define void @commutable_not_in_function_entry() noreturn { ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB22_3: ; %exit ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 4294967295 +; CHECK-NEXT: PUSH4 0xFFFFFFFF ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP ; CHECK-NEXT: AND ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: MSTORE -; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH1 0x20 ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN diff --git a/llvm/test/CodeGen/EVM/stack-ops.ll b/llvm/test/CodeGen/EVM/stack-ops.ll index a5256b83d006..e401dc15df3c 100644 --- a/llvm/test/CodeGen/EVM/stack-ops.ll +++ b/llvm/test/CodeGen/EVM/stack-ops.ll @@ -154,7 +154,7 @@ define void @first_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: SUB @@ -176,7 +176,7 @@ define i256 @first_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: SUB @@ -199,7 +199,7 @@ define void @second_arg_alive_with_junk(i256 %a1, i256 %a2, i256 %a3) noreturn { ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: POP ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP2 @@ -222,7 +222,7 @@ define i256 @second_arg_alive_no_junk(i256 %a1, i256 %a2, i256 %a3) nounwind { ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: POP ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 4 +; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP2 diff --git a/llvm/test/CodeGen/EVM/sub.ll b/llvm/test/CodeGen/EVM/sub.ll index 0dba7bd56bab..9d4be1cd3b92 100644 --- a/llvm/test/CodeGen/EVM/sub.ll +++ b/llvm/test/CodeGen/EVM/sub.ll @@ -20,7 +20,7 @@ define i256 @addrri(i256 %rs1) nounwind { ; CHECK-LABEL: addrri: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 3 +; CHECK-NEXT: PUSH1 0x3 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 diff --git a/llvm/test/CodeGen/EVM/truncstore.ll b/llvm/test/CodeGen/EVM/truncstore.ll index 0a7928b5923b..ffe38b654569 100644 --- a/llvm/test/CodeGen/EVM/truncstore.ll +++ b/llvm/test/CodeGen/EVM/truncstore.ll @@ -22,12 +22,12 @@ define void @storei32(i32 %val, ptr addrspace(1) %glob) nounwind { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP2 ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH1 0x20 ; CHECK-NEXT: SHR -; CHECK-NEXT: PUSH1 32 +; CHECK-NEXT: PUSH1 0x20 ; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: PUSH1 0xE0 ; CHECK-NEXT: SHL ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 diff --git a/llvm/test/CodeGen/EVM/unused_function_arguments.ll b/llvm/test/CodeGen/EVM/unused_function_arguments.ll index b9e818046e6d..a28d1bc37496 100644 --- a/llvm/test/CodeGen/EVM/unused_function_arguments.ll +++ b/llvm/test/CodeGen/EVM/unused_function_arguments.ll @@ -41,9 +41,9 @@ define i256 @bar() nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH4 @.FUNC_RET0 -; CHECK-NEXT: PUSH1 3 -; CHECK-NEXT: PUSH1 2 -; CHECK-NEXT: PUSH1 1 +; CHECK-NEXT: PUSH1 0x3 +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: PUSH4 @foo ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET0: diff --git a/llvm/test/CodeGen/EVM/zero_any_extload.ll b/llvm/test/CodeGen/EVM/zero_any_extload.ll index 1f24642ece45..938bc74fee7c 100644 --- a/llvm/test/CodeGen/EVM/zero_any_extload.ll +++ b/llvm/test/CodeGen/EVM/zero_any_extload.ll @@ -9,7 +9,7 @@ define i8 @load_anyext_i8(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -23,7 +23,7 @@ define i16 @load_anyext_i16(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 240 +; CHECK-NEXT: PUSH1 0xF0 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -37,7 +37,7 @@ define i32 @load_anyext_i32(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: PUSH1 0xE0 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -51,7 +51,7 @@ define i64 @load_anyext_i64(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 192 +; CHECK-NEXT: PUSH1 0xC0 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -65,7 +65,7 @@ define i128 @load_anyext_i128(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -79,7 +79,7 @@ define i256 @load_zeroext_i8(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 248 +; CHECK-NEXT: PUSH1 0xF8 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -94,7 +94,7 @@ define i256 @load_zeroext_i16(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 240 +; CHECK-NEXT: PUSH1 0xF0 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -109,7 +109,7 @@ define i256 @load_zeroext_i32(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 224 +; CHECK-NEXT: PUSH1 0xE0 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -124,7 +124,7 @@ define i256 @load_zeroext_i64(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 192 +; CHECK-NEXT: PUSH1 0xC0 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -139,7 +139,7 @@ define i256 @load_zeroext_i128(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 128 +; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/zext.ll b/llvm/test/CodeGen/EVM/zext.ll index 3e03939ab3ee..11bf90173d33 100644 --- a/llvm/test/CodeGen/EVM/zext.ll +++ b/llvm/test/CodeGen/EVM/zext.ll @@ -8,7 +8,7 @@ define i256 @zexti8(i8 %rs1) nounwind { ; CHECK-LABEL: zexti8: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 255 +; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -21,7 +21,7 @@ define i256 @zexti16(i16 %rs1) nounwind { ; CHECK-LABEL: zexti16: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH2 65535 +; CHECK-NEXT: PUSH2 0xFFFF ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -34,7 +34,7 @@ define i256 @zexti32(i32 %rs1) nounwind { ; CHECK-LABEL: zexti32: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 4294967295 +; CHECK-NEXT: PUSH4 0xFFFFFFFF ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -47,7 +47,7 @@ define i256 @zexti64(i64 %rs1) nounwind { ; CHECK-LABEL: zexti64: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH8 18446744073709551615 +; CHECK-NEXT: PUSH8 0xFFFFFFFFFFFFFFFF ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -60,7 +60,7 @@ define i256 @zexti128(i128 %rs1) nounwind { ; CHECK-LABEL: zexti128: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH16 340282366920938463463374607431768211455 +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ; CHECK-NEXT: AND ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/MC/EVM/imm-hex-output.ll b/llvm/test/MC/EVM/imm-hex-output.ll new file mode 100644 index 000000000000..4b0fa1f908f6 --- /dev/null +++ b/llvm/test/MC/EVM/imm-hex-output.ll @@ -0,0 +1,21 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc --mtriple=evm %s -o - | FileCheck %s + +; CHECK-NOT: .note.GNU-stack + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @foo() { +; CHECK-LABEL: foo: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0x40 +; CHECK-NEXT: RETURN + tail call void @llvm.evm.return( ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), i256 -26959946667150639794667015087019630673637144422540572481103610249216) + unreachable +} From 5268d7c4f1e4fbc77311d7cfc8a91b23b4e98c19 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 2 May 2025 01:34:00 +0200 Subject: [PATCH 126/203] [EVM] Don't add padding for instructions that consist solely of mnemonics --- llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp index 701859aca559..5f4bc3b3a147 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMInstPrinter.cpp @@ -52,7 +52,10 @@ void EVMInstPrinter::printInst(const MCInst *MI, uint64_t Address, O << "\t"; std::pair MnemonicInfo = getMnemonic(MI); // Add padding to the mnemonic so that it is 16 characters long. - O << left_justify(MnemonicInfo.first, /*Width=*/16); + if (MI->getNumOperands() > 0) + O << left_justify(MnemonicInfo.first, /*Width=*/16); + else + O << MnemonicInfo.first; for (unsigned I = 0; I < MI->getNumOperands(); ++I) printOperand(MI, I, O); From 7a4eb3452fa5bca4089f018854bd1a34fca6ea15 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Mon, 19 May 2025 15:26:13 +0200 Subject: [PATCH 127/203] [EVM] Restore inference of the 'nounwind' function attribute Also mark several LIT test as XFAIL for EVM. --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 2 +- llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll | 1 - llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll | 1 - llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll | 1 - llvm/test/Transforms/FunctionAttrs/argmemonly.ll | 1 - llvm/test/Transforms/FunctionAttrs/atomic.ll | 1 - llvm/test/Transforms/FunctionAttrs/convergent.ll | 1 - llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll | 1 - llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll | 1 - llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll | 1 - llvm/test/Transforms/FunctionAttrs/nocapture.ll | 1 - llvm/test/Transforms/FunctionAttrs/nonnull.ll | 1 - llvm/test/Transforms/FunctionAttrs/norecurse.ll | 1 - llvm/test/Transforms/FunctionAttrs/nosync.ll | 1 - llvm/test/Transforms/FunctionAttrs/nounwind.ll | 1 - llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll | 1 - llvm/test/Transforms/FunctionAttrs/optnone.ll | 1 - llvm/test/Transforms/FunctionAttrs/read-write-scc.ll | 1 - llvm/test/Transforms/FunctionAttrs/readattrs.ll | 1 - llvm/test/Transforms/FunctionAttrs/stats.ll | 1 - llvm/test/Transforms/FunctionAttrs/willreturn.ll | 1 - llvm/test/Transforms/FunctionAttrs/writeonly.ll | 1 - llvm/test/Transforms/IndVarSimplify/pr38855.ll | 1 - llvm/test/Transforms/Inline/cgscc-update.ll | 1 - llvm/test/Transforms/PhaseOrdering/func-attrs.ll | 1 - 25 files changed, 1 insertion(+), 25 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index c8865162ae5b..7b419d0f098b 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -1611,7 +1611,7 @@ static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, SmallSet &Changed) { AttributeInferer AI; - if (false && !DisableNoUnwindInference) + if (!DisableNoUnwindInference) // Request to infer nounwind attribute for all the functions in the SCC if // every callsite within the SCC is not throwing (except for calls to // functions within the SCC). Note that nounwind attribute suffers from diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll index 5ed9fad1cd6c..23d6c5ae1fdb 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll +++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} define i32 @a() { ; CHECK: Function Attrs: nofree nosync nounwind memory(none) diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll index c5b46e334293..ee8437e8c0f1 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll +++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} @x = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll index 041d6c0eddaa..7a97498b8f32 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll +++ b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} @s = external constant i8 diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll index 739ea5d9de69..ea6392714bf6 100644 --- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2 ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}} @g = global i32 20 diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 85a60d81cf36..8635f2bbdc49 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s -; XFAIL: target=evm{{.*}} ; Atomic load/store to local doesn't affect whether a function is ; readnone/readonly. diff --git a/llvm/test/Transforms/FunctionAttrs/convergent.ll b/llvm/test/Transforms/FunctionAttrs/convergent.ll index eed57bdc2793..a0f4c07e4337 100644 --- a/llvm/test/Transforms/FunctionAttrs/convergent.ll +++ b/llvm/test/Transforms/FunctionAttrs/convergent.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} define i32 @nonleaf() convergent { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) diff --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll index f9c98e4b007c..7e246c482431 100644 --- a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -S -o - -passes=function-attrs < %s | FileCheck %s -; XFAIL: target=evm{{.*}} ; Verify we remove argmemonly/inaccessiblememonly/inaccessiblemem_or_argmemonly ; function attributes when we derive readnone. diff --git a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll index ef7a1b73e677..0f087e1a05f7 100644 --- a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll +++ b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -S < %s -passes=function-attrs | FileCheck %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} declare void @llvm.sideeffect() diff --git a/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll b/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll index e3d6e4b0bcdb..bb9ef9156794 100644 --- a/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll +++ b/llvm/test/Transforms/FunctionAttrs/make-buffer-rsrc.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}} ;; target triple = "amdgcn-amd-amdhsa" target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-ni:7:8" diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll index 6110f325f00d..7df6132ac6a3 100644 --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2 ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} @g = global ptr null ; [#uses=1] diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll index c6405b8dd182..4432c4f3c541 100644 --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=COMMON,FNATTRS ; RUN: opt -S -passes=attributor-light %s | FileCheck %s --check-prefixes=COMMON,ATTRIBUTOR -; XFAIL: target=evm{{.*}} target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll index 443fcd406e1b..a902974fed28 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}} define i32 @leaf() { diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll index 7fa7bbcb7021..de5398f17ce5 100644 --- a/llvm/test/Transforms/FunctionAttrs/nosync.ll +++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s -; XFAIL: target=evm{{.*}} target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll index def097a0ba2f..afa9ae31b5fb 100644 --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}} ; TEST 1 define i32 @foo1() { diff --git a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll index e92efc2b7ce5..c1aa14b88f0a 100644 --- a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -S -passes=function-attrs < %s | FileCheck %s -; XFAIL: target=evm{{.*}} define void @f() { ; CHECK: Function Attrs: nofree nosync nounwind diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll index 366acb180936..aab1ec1d2954 100644 --- a/llvm/test/Transforms/FunctionAttrs/optnone.ll +++ b/llvm/test/Transforms/FunctionAttrs/optnone.ll @@ -1,5 +1,4 @@ ; RUN: opt < %s -passes=function-attrs -S | FileCheck %s -; XFAIL: target=evm{{.*}} @x = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll index c3bc3cd956e7..be61990fd627 100644 --- a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -S -passes=function-attrs < %s | FileCheck %s -; XFAIL: target=evm{{.*}} @i = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll index 9cd4ba4fd7ad..39513976f90d 100644 --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=function-attrs -S | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt < %s -passes=attributor-light -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s ; RUN: opt < %s -passes=attributor-light-cgscc -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR-CGSCC %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} @x = global i32 0 diff --git a/llvm/test/Transforms/FunctionAttrs/stats.ll b/llvm/test/Transforms/FunctionAttrs/stats.ll index 73bb933b725d..5f007b4078ff 100644 --- a/llvm/test/Transforms/FunctionAttrs/stats.ll +++ b/llvm/test/Transforms/FunctionAttrs/stats.ll @@ -1,5 +1,4 @@ ; RUN: opt -passes=function-attrs -stats -disable-output %s 2>&1 | FileCheck %s -; XFAIL: target=evm{{.*}} ; REQUIRES: asserts diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll index 30789579b37e..70926345ce27 100644 --- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -passes=function-attrs -S %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} define void @mustprogress_readnone() mustprogress { ; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none) diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll index 54f8057fb248..de2d5e223894 100644 --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -1,7 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} define void @nouses-argworn-funrn(ptr writeonly %.aaa) { ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) diff --git a/llvm/test/Transforms/IndVarSimplify/pr38855.ll b/llvm/test/Transforms/IndVarSimplify/pr38855.ll index f87d3fa7bf59..0ebad14fe519 100644 --- a/llvm/test/Transforms/IndVarSimplify/pr38855.ll +++ b/llvm/test/Transforms/IndVarSimplify/pr38855.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -disable-nounwind-inference=false -passes=inline,function-attrs,indvars < %s | FileCheck %s -; XFAIL: target=evm{{.*}} ; Check that the invalidation happens correctly and the test does not crash. define void @f2() { diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll index 1122d34b1a58..f1121fa88a4b 100644 --- a/llvm/test/Transforms/Inline/cgscc-update.ll +++ b/llvm/test/Transforms/Inline/cgscc-update.ll @@ -1,5 +1,4 @@ ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs,inline)' -S | FileCheck %s -; XFAIL: target=evm{{.*}} ; This test runs the inliner and the function attribute deduction. It ensures ; that when the inliner mutates the call graph it correctly updates the CGSCC ; iteration so that we can compute refined function attributes. In this way it diff --git a/llvm/test/Transforms/PhaseOrdering/func-attrs.ll b/llvm/test/Transforms/PhaseOrdering/func-attrs.ll index c6baf27522c9..187e32afe00a 100644 --- a/llvm/test/Transforms/PhaseOrdering/func-attrs.ll +++ b/llvm/test/Transforms/PhaseOrdering/func-attrs.ll @@ -1,6 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes ; RUN: opt -O2 -S < %s | FileCheck %s -; XFAIL: target=evm{{.*}}, target=evm{{.*}} declare void @g() From 1154e3b4ff76088b171e78d2314d673addc0a585 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 20 May 2025 18:08:37 +0200 Subject: [PATCH 128/203] [EVM] Rename SHA3 mnemonic to KECCAK256 --- llvm/lib/Target/EVM/EVMInstrInfo.td | 4 ++-- llvm/test/CodeGen/EVM/intrinsic.ll | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index fbba20f55ee8..0506d09d85dc 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -366,10 +366,10 @@ defm NOT "NOT", "$dst, $src", 0x19, 3>; let mayLoad = 1 in -defm SHA3 +defm KECCAK256 : I<(outs GPR:$dst), (ins GPR:$offset, GPR:$size), [(set GPR:$dst, (int_evm_sha3 GPR:$offset, GPR:$size))], - "SHA3", "$dst, $offset, $size", 0x20, 30>; + "KECCAK256", "$dst, $offset, $size", 0x20, 30>; defm BYTE : I<(outs GPR:$dst), (ins GPR:$idx, GPR:$val), diff --git a/llvm/test/CodeGen/EVM/intrinsic.ll b/llvm/test/CodeGen/EVM/intrinsic.ll index c14059843005..f30f77ae881c 100644 --- a/llvm/test/CodeGen/EVM/intrinsic.ll +++ b/llvm/test/CodeGen/EVM/intrinsic.ll @@ -128,7 +128,7 @@ define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { ; CHECK-LABEL: sha3: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SHA3 +; CHECK-NEXT: KECCAK256 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP From e0bbc5353dbb90e94b01ce20bf1945919e93cc75 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 9 May 2025 01:10:24 +0200 Subject: [PATCH 129/203] [EVM] Add a C-API to retrieve linker symbol offsets from an ELF file These are linker symbol offsets in a generated bytecode. --- lld/include/lld-c/LLDAsLibraryC.h | 10 +++++++ lld/lld-c/LLDAsLibraryC.cpp | 45 +++++++++++++++++++++++++++++++ lld/unittests/EVM/LLDTest.cpp | 8 +++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/lld/include/lld-c/LLDAsLibraryC.h b/lld/include/lld-c/LLDAsLibraryC.h index ed8f5b3b1b4d..ec3144ca14b5 100644 --- a/lld/include/lld-c/LLDAsLibraryC.h +++ b/lld/include/lld-c/LLDAsLibraryC.h @@ -123,10 +123,20 @@ uint64_t LLVMGetImmutablesEVM(LLVMMemoryBufferRef inBuffer, char ***immutableIDs, uint64_t **immutableOffsets); +/** Returns an array of offsets of the linker symbol relocations form the + * ELF object file provided in \p inBuffer. */ +uint64_t LLVMGetSymbolOffsetsEVM(LLVMMemoryBufferRef inBuffer, + const char *symbolName, + uint64_t **symbolOffsets); + /** Disposes of the immutable names and their offsets returned by * 'LLVMGetImmutablesEVM'. */ void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, uint64_t numOfImmutables); + +/** Releases the array of linker symbol offsets. */ +void LLVMDisposeSymbolOffsetsEVM(uint64_t *offsets); + 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 f681e3205cc6..20311d08cb50 100644 --- a/lld/lld-c/LLDAsLibraryC.cpp +++ b/lld/lld-c/LLDAsLibraryC.cpp @@ -599,6 +599,49 @@ static void getUndefinedNonRefSymbols(LLVMMemoryBufferRef inBuffer, } } +/// Returns an array of offsets for the linker symbol relocations. +/// These are the symbol offsets in the generated bytecode. +uint64_t LLVMGetSymbolOffsetsEVM(LLVMMemoryBufferRef inBuffer, + const char *symbolName, + uint64_t **symbolOffsets) { + std::unique_ptr inBinary = + cantFail(createBinary(unwrap(inBuffer)->getMemBufferRef())); + auto *elfFile = static_cast(inBinary.get()); + + SmallVector Offsets; + // The relocation of a linker symbol is expressed as a sequence of + // five consecutive 32-bit relocations. We need only the first one. + std::string subSymName = getLinkerSubSymbolName(symbolName, 0); + for (const ELFSectionRef relocsSec : elfFile->sections()) { + // Find the relocation section. It should be .rela.text. + if (relocsSec.relocations().empty() || + !cantFail(relocsSec.getRelocatedSection())->isText()) + continue; + + // Check relocations of the specified type. + for (const ELFRelocationRef rel : relocsSec.relocations()) { + if (rel.getType() != ELF::R_EVM_DATA) + continue; + + elf_symbol_iterator sym = rel.getSymbol(); + uint32_t symFlags = cantFail(sym->getFlags()); + StringRef symName = cantFail(sym->getName()); + if ((sym->getOther() == llvm::ELF::STO_EVM_REFERENCE_SYMBOL) && + (symFlags & object::SymbolRef::SF_Undefined) && + (symName == subSymName)) + Offsets.push_back(rel.getOffset()); + } + } + + *symbolOffsets = reinterpret_cast( + std::malloc(Offsets.size() * sizeof(uint64_t))); + + sort(Offsets); + copy(Offsets, *symbolOffsets); + + return Offsets.size(); +} + /// Performs the following steps, which are based on Ethereum's /// Assembly::assemble() logic: /// - Concatenates the .text sections of input ELF files referenced @@ -815,3 +858,5 @@ void LLVMDisposeImmutablesEVM(char **immutableIDs, uint64_t *immutableOffsets, std::free(immutableIDs); std::free(immutableOffsets); } + +void LLVMDisposeSymbolOffsetsEVM(uint64_t *offsets) { std::free(offsets); } diff --git a/lld/unittests/EVM/LLDTest.cpp b/lld/unittests/EVM/LLDTest.cpp index 933fb3d5529d..78d71e2969db 100644 --- a/lld/unittests/EVM/LLDTest.cpp +++ b/lld/unittests/EVM/LLDTest.cpp @@ -23,7 +23,6 @@ using namespace llvm; -#include static std::string expand(const char *Path) { llvm::SmallString<256> ThisPath; ThisPath.append(getenv("LLD_SRC_DIR")); @@ -291,6 +290,13 @@ TEST_F(LLDCTest, Assembly) { {R_deploy_obj, R_deployed_assemble, A_assembly, A_assembly_deployed}, {"R_107", "R_107_deployed", "A_38", "A_38.A_38_deployed"}); + // Get linker symbols offsets + uint64_t *SymOffsets = nullptr; + uint64_t NumOffsets = + LLVMGetSymbolOffsetsEVM(R_assembly, LinkerSymbol[1], &SymOffsets); + EXPECT_TRUE(NumOffsets == 3); + LLVMDisposeSymbolOffsetsEVM(SymOffsets); + // Linking with no linker symbols. LLVMMemoryBufferRef TmpAssembly = link(R_assembly, "R", nullptr, nullptr, 0); EXPECT_TRUE(LLVMIsELFEVM(TmpAssembly)); From a5e82da9624a469b56c17e233a7f1d006c74ec45 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 23 May 2025 18:39:24 +0200 Subject: [PATCH 130/203] [EVM] Disable tail duplication optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases, tail duplication can combine a loop’s header with its latch. When this happens, MachineLoopInfo may fail to recognize the loop during stackification. This is a problem because, in EVMStackSolver::runPropagation, we add an empty exit stack for loop latches during the first run (if the header hasn’t been visited yet) to prevent infinite loops. If the loop isn’t detected, we will run into an infinite loop situation. This is a workaround and #822 should fix it in a proper way. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 5 ++- .../CodeGen/EVM/bps-inifinite-loop-bug.ll | 38 +++++++++++++++++++ llvm/test/CodeGen/EVM/br.ll | 4 +- .../EVM/machine-sink-cheap-instructions.ll | 8 +++- 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 20a01fc7a61e..98d913147a75 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -164,7 +164,9 @@ namespace { class EVMPassConfig final : public TargetPassConfig { public: EVMPassConfig(EVMTargetMachine &TM, PassManagerBase &PM) - : TargetPassConfig(TM, PM) {} + : TargetPassConfig(TM, PM) { + disablePass(&EarlyTailDuplicateID); + } EVMTargetMachine &getEVMTargetMachine() const { return getTM(); @@ -232,6 +234,7 @@ void EVMPassConfig::addPostRegAlloc() { disablePass(&LiveDebugValuesID); disablePass(&PatchableFunctionID); disablePass(&ShrinkWrapID); + disablePass(&TailDuplicateID); // TODO: This pass is disabled in WebAssembly, as it hurts code size because // it can generate irreducible control flow. Check if this also true for EVM? diff --git a/llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll b/llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll new file mode 100644 index 000000000000..3af71adca821 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-inifinite-loop-bug.ll @@ -0,0 +1,38 @@ +; RUN: llc -O3 < %s + +; This test case is reduced with llvm-reduce. +; Before the fix, we had an infinite loop in EVMStackSolver::runPropagation, +; because EarlyTailDuplicate merged header and latch blocks. This caused +; MachineLoopInfo not to detect the loop correctly, thus running into an +; infinite loop. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Function Attrs: nounwind willreturn memory(none) +declare i256 @llvm.evm.signextend(i256, i256) #0 + +define void @test(i1 %comparison_result6, i256 %mulmod1446) { +entry: + br i1 %comparison_result6, label %"block_rt_2/0", label %shift_right_non_overflow + +"block_rt_2/0": ; preds = %entry + unreachable + +"block_rt_66/0": ; preds = %division_join1531, %shift_right_non_overflow + br i1 %division_is_divider_zero1455, label %division_join1453, label %division_join1531 + +shift_right_non_overflow: ; preds = %entry + %division_is_divider_zero1455 = icmp eq i256 %mulmod1446, 0 + br label %"block_rt_66/0" + +division_join1453: ; preds = %"block_rt_66/0" + %signextend1463 = tail call i256 @llvm.evm.signextend(i256 0, i256 0) + br label %division_join1531 + +division_join1531: ; preds = %division_join1453, %"block_rt_66/0" + store i256 0, ptr addrspace(1) null, align 4294967296 + br label %"block_rt_66/0" +} + +attributes #0 = { nounwind willreturn memory(none) } diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index 3b79560bf1c8..e51d49e3da4d 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -19,11 +19,13 @@ define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP ; CHECK-NEXT: MUL -; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB0_3 ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB0_2: ; %false_bb ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: ADD +; CHECK-NEXT: .BB0_3: ; %end_bb +; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll index 512c15f41955..c805bae8992e 100644 --- a/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll +++ b/llvm/test/CodeGen/EVM/machine-sink-cheap-instructions.ll @@ -16,7 +16,7 @@ define i256 @test1(i256 %arg) { ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB0_3 ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB0_2: ; %bb1 ; CHECK-NEXT: JUMPDEST @@ -25,6 +25,8 @@ define i256 @test1(i256 %arg) { ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: .BB0_3: ; %bb2 +; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP entry: @@ -50,7 +52,7 @@ define i256 @test2(i256 %arg) { ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: ADDRESS -; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB1_3 ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB1_2: ; %bb1 ; CHECK-NEXT: JUMPDEST @@ -59,6 +61,8 @@ define i256 @test2(i256 %arg) { ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET1: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: .BB1_3: ; %bb2 +; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP entry: From 48e0e019ddbd647401dc4ef3180365ea326b0c39 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 10 Jun 2025 16:25:21 +0200 Subject: [PATCH 131/203] [EVM] Update live interval during ADD remat in EVMSingleUseExpression Since EVMSingleUseExpression preserves LiveIntervals analysis, it is important to ensure that the live intervals are correctly updated when rematerializing instructions. In this case, during the ADD rematerialization, new instruction is created and the live interval of base register is not updated. As a result, we are hitting assertion during stackification. Signed-off-by: Vladimir Radosavljevic --- .../lib/Target/EVM/EVMSingleUseExpression.cpp | 16 +++++++- llvm/test/CodeGen/EVM/sue-add-remat-bug.ll | 39 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/sue-add-remat-bug.ll diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index 99cd82bd643a..dbff24f6c86c 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -706,12 +706,24 @@ bool EVMSingleUseExpression::runOnMachineFunction(MachineFunction &MF) { getVRegDef(DefI->getOperand(2).getReg(), DefI, MRI, LIS); assert(DefI1 && DefI2); - MachineInstr *OffsetI = - (DefI1->getOpcode() == EVM::CONST_I256) ? DefI1 : DefI2; + MachineInstr *OffsetI = nullptr; + Register BaseReg; + if (DefI1->getOpcode() == EVM::CONST_I256) { + OffsetI = DefI1; + BaseReg = DefI->getOperand(2).getReg(); + } else { + OffsetI = DefI2; + BaseReg = DefI->getOperand(1).getReg(); + } Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, Insert->getIterator(), LIS, MFI, MRI, TII, TRI); + // Recalculate live interval of the base register, since we created + // a new instruction that uses it. + LIS.removeInterval(BaseReg); + LIS.createAndComputeVirtRegInterval(BaseReg); + MachineOperand &OffsetMO = (DefI1->getOpcode() == EVM::CONST_I256) ? Insert->getOperand(1) : Insert->getOperand(2); diff --git a/llvm/test/CodeGen/EVM/sue-add-remat-bug.ll b/llvm/test/CodeGen/EVM/sue-add-remat-bug.ll new file mode 100644 index 000000000000..9d9f7d09f226 --- /dev/null +++ b/llvm/test/CodeGen/EVM/sue-add-remat-bug.ll @@ -0,0 +1,39 @@ +; RUN: llc -O3 < %s + +; This test case is reduced with llvm-reduce. +; Before the fix, we were hitting an assert during +; stackification in EVMStackModel::getStackSlot. This +; was caused by not updating the live interval of ADD +; operand during rematerialization in EVMSingleUseExpression pass. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +; Function Attrs: nounwind willreturn memory(argmem: read) +declare i256 @llvm.evm.sha3(ptr addrspace(1), i256) #0 + +; Function Attrs: nounwind willreturn +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) #1 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #2 + +define void @__entry() { +entry: + %abi_decode_bytest_bytest_bytest_bytes_call406 = call fastcc { i256, i256, i256, i256 } @abi_decode_bytest_bytest_bytest_bytes() + %abi_decode_bytest_bytest_bytest_bytes_call406.fca.0.extract = extractvalue { i256, i256, i256, i256 } %abi_decode_bytest_bytest_bytest_bytes_call406, 0 + %abi_decode_bytest_bytest_bytest_bytes_call406.fca.2.extract = extractvalue { i256, i256, i256, i256 } %abi_decode_bytest_bytest_bytest_bytes_call406, 2 + %addition_result419 = add i256 %abi_decode_bytest_bytest_bytest_bytes_call406.fca.2.extract, 1 + %keccak256_input_offset_pointer = inttoptr i256 %addition_result419 to ptr addrspace(1) + %keccak256 = tail call i256 @llvm.evm.sha3(ptr addrspace(1) %keccak256_input_offset_pointer, i256 0) + tail call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) null, ptr addrspace(1) %keccak256_input_offset_pointer, i256 0, i1 false) + %call_input_offset_pointer829 = inttoptr i256 %abi_decode_bytest_bytest_bytest_bytes_call406.fca.0.extract to ptr addrspace(1) + %call831 = tail call i256 @llvm.evm.call(i256 0, i256 0, i256 0, ptr addrspace(1) %call_input_offset_pointer829, i256 0, ptr addrspace(1) null, i256 0) + ret void +} + +declare fastcc { i256, i256, i256, i256 } @abi_decode_bytest_bytest_bytest_bytes() + +attributes #0 = { nounwind willreturn memory(argmem: read) } +attributes #1 = { nounwind willreturn } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } From 43676c697ecf804599f41f5845b427a54128e670 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 5 Jun 2025 11:35:27 +0200 Subject: [PATCH 132/203] [EVM][BPS] Make EVMStackSolver to report stack-too-deep errors After EVMStackSolver finished stack propagation, it now checks all the stacks built for stack-too-deep and report them as fatal errors. The following transformations are checked and reported: * the MBB's entry stack to the entry stack of the first MI, * an exit stack of an MI to an entry stack of the next one, * the exit stack of the last MI to the MBB exit stack, * the MBB's entry stack to the MBB's exit if the BB is empty. Note that tests added for the first 3 cases (the last one was hard to isolate from the previous and I gave up on it). --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 78 +++++-- llvm/lib/Target/EVM/EVMStackSolver.h | 11 +- llvm/test/CodeGen/EVM/stack-too-deep-1.ll | 201 ++++++++++++++++++ llvm/test/CodeGen/EVM/stack-too-deep-2.ll | 244 ++++++++++++++++++++++ llvm/test/CodeGen/EVM/stack-too-deep-3.ll | 125 +++++++++++ 5 files changed, 640 insertions(+), 19 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/stack-too-deep-1.ll create mode 100644 llvm/test/CodeGen/EVM/stack-too-deep-2.ll create mode 100644 llvm/test/CodeGen/EVM/stack-too-deep-3.ll diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 5cab3dd3964f..a7014e5837b7 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -212,11 +212,46 @@ void EVMStackSolver::run() { dbgs() << "************* Stack *************\n"; dump(dbgs()); }); + checkPropagationErrors(); } -std::pair -EVMStackSolver::propagateThroughMI(const Stack &ExitStack, - const MachineInstr &MI, bool CompressStack) { +void EVMStackSolver::checkPropagationErrors() { + for (const MachineBasicBlock &MBB : MF) { + const Stack &EntryMBB = StackModel.getMBBEntryStack(&MBB); + const Stack &ExitMBB = StackModel.getMBBExitStack(&MBB); + const auto InstRange = StackModel.instructionsToProcess(&MBB); + + auto checkStackTransformation = [this](const Stack &From, const Stack &To) { + if (!calculateStackTransformCost(From, To, StackModel.stackDepthLimit())) + report_fatal_error(Twine("EVMStackSolver cannot transform ") + + From.toString() + " to " + To.toString() + + ": stack too deep.\n"); + }; + + // Check transformation from the MBB's entry to exit stack if BB is empty. + if (InstRange.empty()) { + checkStackTransformation(EntryMBB, ExitMBB); + continue; + } + + // Check transformation from the MBB's entry to the entry of the first MI + // on the first iteration and then do the same from the exit stack of + // the previous MI to the entry stack of the next MI. + Stack ExitPrev = EntryMBB; + for (const auto &MI : InstRange) { + checkStackTransformation(ExitPrev, StackModel.getInstEntryStack(&MI)); + ExitPrev = getMIExitStack(&MI); + } + + // Check transformation from the exit stack of the last MI to + // the MBB's exit stack. + checkStackTransformation(ExitPrev, ExitMBB); + } +} + +Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, + const MachineInstr &MI, + bool CompressStack) { LLVM_DEBUG({ dbgs() << "\tMI: "; MI.dump(); @@ -233,13 +268,6 @@ EVMStackSolver::propagateThroughMI(const Stack &ExitStack, assert(!MI.definesRegister(RegSlot->getReg(), /*TRI=*/nullptr)); #endif // NDEBUG - // Check the exit stack from the MI can be smoothly transformed - // to the entry stack of the next MI. - Stack AfterMI = BeforeMI; - AfterMI.append(MIDefs); - bool Err = !calculateStackTransformCost(AfterMI, ExitStack, - StackModel.stackDepthLimit()); - BeforeMI.append(StackModel.getMIInput(MI)); // Store computed stack to StackModel. insertInstEntryStack(&MI, BeforeMI); @@ -261,7 +289,16 @@ EVMStackSolver::propagateThroughMI(const Stack &ExitStack, break; } - return {BeforeMI, Err}; + return BeforeMI; +} + +Stack EVMStackSolver::getMIExitStack(const MachineInstr *MI) { + const Stack &MIInput = StackModel.getMIInput(*MI); + Stack MIEntry = StackModel.getInstEntryStack(MI); + assert(MIInput.size() <= MIEntry.size()); + MIEntry.resize(MIEntry.size() - MIInput.size()); + MIEntry.append(StackModel.getSlotsForInstructionDefs(MI)); + return MIEntry; } Stack EVMStackSolver::propagateThroughMBB(const Stack &ExitStack, @@ -276,18 +313,27 @@ Stack EVMStackSolver::propagateThroughMBB(const Stack &ExitStack, << "):\n"; }); for (const auto &MI : StackModel.reverseInstructionsToProcess(MBB)) { - auto [BeforeMI, Err] = propagateThroughMI(CurrentStack, MI, CompressStack); - CurrentStack = std::move(BeforeMI); - - if (!CompressStack && Err) { + Stack EntryMI = propagateThroughMI(CurrentStack, MI, CompressStack); + + // Check the exit stack of the MI can be transformed to the entry stack + // of the next MI, or rerun the propagation with compression. + // It also checks that the last MIs exit stack can be tranformed to + // the MBBs exit stack, but it doesn't do that for MBBs entry stack to + // the first MI entry stack transformation to avoid false positives + // (we rewrite the MBBs entry stack just after the function finished). + if (!CompressStack && + !calculateStackTransformCost(getMIExitStack(&MI), CurrentStack, + StackModel.stackDepthLimit())) { LLVM_DEBUG({ dbgs() << "\terror: stack-too-deep detected, trying to rerun with " - "Compressstack=true.\n"; + "CompressStack=true.\n"; }); return propagateThroughMBB(ExitStack, MBB, /*CompressStack*/ true); } + CurrentStack = EntryMI; } + return CurrentStack; } diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h index 716e461dde18..655b5d5532ff 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.h +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -45,9 +45,8 @@ class EVMStackSolver { /// minimal. /// Side effect: the computed entry stack is stored in StackModel. /// \param CompressStack: remove duplicates and rematerializable slots. - std::pair propagateThroughMI(const Stack &ExitStack, - const MachineInstr &MI, - bool CompressStack = false); + Stack propagateThroughMI(const Stack &ExitStack, const MachineInstr &MI, + bool CompressStack = false); /// Given \p ExitStack, compute the stack at the entry of \p MBB. /// \param CompressStack: remove duplicates and rematerializable slots. @@ -55,6 +54,12 @@ class EVMStackSolver { const MachineBasicBlock *MBB, bool CompressStack = false); + // Build the exit stack of the given \p MI. + Stack getMIExitStack(const MachineInstr *MI); + + // Check and report for stack-too-deep errors. + void checkPropagationErrors(); + /// Main algorithm walking the graph from entry to exit and propagating stack /// states back to the entries. Iteratively reruns itself along backward jumps /// until the state is stabilized. diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-1.ll b/llvm/test/CodeGen/EVM/stack-too-deep-1.ll new file mode 100644 index 000000000000..8accc23febdb --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-too-deep-1.ll @@ -0,0 +1,201 @@ +; RUN: not --crash llc < %s -o /dev/null 2>&1 | FileCheck %s + +; Test that EVMStackSolver can catch errors when transformation from +; the BBs entry stack to the entry stack of the first MI is not possible. + +; The stack built for the 'bad' BB is +; +; 20.block_rt_88/7.outer: +; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %29 %33 ] +; +; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 7 21 ] +; EQ +; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 %72 ] +; +; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 %72 ] + +; CHECK: EVMStackSolver cannot transform +; CHECK-SAME: [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %29 %33 ] +; CHECK-SAME: to +; CHECK-SAME: [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 7 21 ] +; CHECK-SAME: : stack too deep. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define dso_local fastcc void @main() unnamed_addr { +entry: + br label %"block_rt_19/0" + +"block_rt_19/0": ; preds = %entry + %addition_result2158 = add nuw nsw i256 1, 20 + %and_result2674 = and i256 %addition_result2158, 255 + %trunc = trunc i256 %addition_result2158 to i8 + %comparison_result3799.not = icmp eq i256 %and_result2674, 3 + %comparison_result6184.not = icmp eq i256 %and_result2674, 24 + %comparison_result7522.not = icmp eq i256 %and_result2674, 26 + %comparison_result9235.not = icmp eq i256 %and_result2674, 28 + br label %"block_rt_44/7" + +"block_rt_43/7": ; preds = %remainder_join12933, %"block_rt_54/7.thread", %"block_rt_44/7" + unreachable + +"block_rt_44/7": ; preds = %"block_rt_19/0" + switch i8 %trunc, label %"block_rt_51/7" [ + i8 1, label %"block_rt_43/7" + i8 21, label %conditional_rt_49_join_block2921 + i8 11, label %"block_rt_46/7" + ] + +"block_rt_46/7": ; preds = %"block_rt_51/7", %"block_rt_44/7" + unreachable + +"block_rt_51/7": ; preds = %"block_rt_57/7", %"block_rt_44/7" + %stack_var_010.1 = phi i256 [ %multiplication_result1217, %"block_rt_57/7" ], [ 1, %"block_rt_44/7" ] + switch i8 %trunc, label %"block_rt_54/7.thread" [ + i8 22, label %"block_rt_46/7" + i8 33, label %"block_rt_54/7" + ] + +"block_rt_54/7": ; preds = %"block_rt_51/7" + %comparison_result3562 = icmp ugt i256 %stack_var_010.1, 1 + unreachable + +"block_rt_56/7.outer": ; preds = %"block_rt_56/7.preheader", %"block_rt_70/7" + %stack_var_011.8.ph = phi i256 [ 0, %"block_rt_56/7.preheader" ], [ %addition_result2182, %"block_rt_70/7" ] + br label %"block_rt_56/7" + +"block_rt_56/7": ; preds = %"block_rt_64/7", %"block_rt_56/7.outer" + %stack_var_011.8 = phi i256 [ %addition_result2182, %"block_rt_64/7" ], [ %stack_var_011.8.ph, %"block_rt_56/7.outer" ] + %and_result2179 = and i256 %stack_var_011.8, 255 + %addition_result2182 = add nuw nsw i256 %and_result2179, 2 + br label %"block_rt_64/7" + +"block_rt_57/7": ; preds = %"block_rt_64/7" + %multiplication_result1217 = shl nuw nsw i256 %stack_var_010.1, 1 + br label %"block_rt_51/7" + +"block_rt_64/7": ; preds = %"block_rt_56/7" + switch i8 %trunc, label %conditional_rt_68_join_block5874 [ + i8 12, label %"block_rt_56/7" + i8 23, label %"block_rt_57/7" + ] + +"block_rt_70/7": ; preds = %conditional_rt_68_join_block5874 + %comparison_result6061 = icmp ugt i256 %and_result5861, 2 + %or.cond = or i1 %comparison_result6184.not, %comparison_result6061 + br i1 %or.cond, label %"block_rt_56/7.outer", label %"block_rt_73/7" + +"block_rt_73/7": ; preds = %"block_rt_80/7", %"block_rt_70/7" + %stack_var_013.2 = phi i256 [ %addition_result6585, %"block_rt_80/7" ], [ 10, %"block_rt_70/7" ] + %and_result6465 = and i256 %stack_var_013.2, 255 + br i1 false, label %"block_rt_123/8", label %conditional_rt_74_join_block6475 + +"block_rt_80/7": ; preds = %"block_rt_86/7" + %addition_result6585 = add nuw nsw i256 %stack_var_013.2, 1 + br label %"block_rt_73/7" + +"block_rt_86/7": ; preds = %remainder_join12901 + br i1 %comparison_result7522.not, label %"block_rt_80/7", label %"block_rt_88/7.outer" + +"block_rt_88/7.outer": ; preds = %"block_rt_101/7", %"block_rt_86/7" + %stack_var_008.19.ph = phi i256 [ poison, %"block_rt_86/7" ], [ %stack_var_008.20, %"block_rt_101/7" ] + %stack_var_015.1.ph = phi i256 [ 10, %"block_rt_86/7" ], [ %addition_result2206, %"block_rt_101/7" ] + %and_result7774 = and i256 %stack_var_015.1.ph, 255 + switch i8 %trunc, label %"block_rt_92/7" [ + i8 7, label %conditional_rt_90_join_block7798 + i8 27, label %"block_rt_131/7.backedge" + ] + +"block_rt_89/7": ; preds = %"block_rt_95/7" + unreachable + +"block_rt_131/7.outer": ; preds = %conditional_rt_74_join_block6475 + br label %"block_rt_131/7" + +"block_rt_92/7": ; preds = %"block_rt_88/7.outer" + %addition_result2206 = add nuw nsw i256 %and_result7774, 1 + br label %"block_rt_95/7" + +"block_rt_95/7": ; preds = %"block_rt_92/7" + %comparison_result8476.not = icmp eq i256 %addition_result2206, 16 + br i1 %comparison_result8476.not, label %"block_rt_89/7", label %"block_rt_96/7" + +"block_rt_96/7": ; preds = %"block_rt_95/7" + switch i8 %trunc, label %"block_rt_101/7.preheader" [ + i8 31, label %"block_rt_97/7" + i8 32, label %"block_rt_99/7" + ] + +"block_rt_97/7": ; preds = %"block_rt_96/7" + unreachable + +"block_rt_99/7": ; preds = %"block_rt_96/7" + unreachable + +"block_rt_101/7.preheader": ; preds = %"block_rt_96/7" + br label %"block_rt_101/7" + +"block_rt_101/7": ; preds = %"block_rt_103/7", %"block_rt_101/7.preheader" + %stack_var_008.20 = phi i256 [ %stack_var_008.21, %"block_rt_103/7" ], [ %stack_var_008.19.ph, %"block_rt_101/7.preheader" ] + %or.cond20853 = or i1 %comparison_result9235.not, false + br i1 %or.cond20853, label %"block_rt_88/7.outer", label %remainder_join12933 + +"block_rt_103/7": ; preds = %remainder_join12933, %"block_rt_108/7" + %stack_var_008.21 = phi i256 [ %addition_result10002, %"block_rt_108/7" ], [ %stack_var_008.20, %remainder_join12933 ] + br label %"block_rt_101/7" + +"block_rt_108/7": ; preds = %remainder_join12933 + %and_result9999 = and i256 %stack_var_008.20, 18446744073709551615 + %addition_result10002 = add nuw nsw i256 %and_result9999, 1 + br label %"block_rt_103/7" + +"block_rt_123/8": ; preds = %conditional_rt_74_join_block6475, %"block_rt_73/7" + %addition_result12201 = add nuw nsw i256 %and_result5861, 1 + br label %conditional_rt_68_join_block5874 + +"block_rt_131/7": ; preds = %"block_rt_131/7.backedge", %"block_rt_131/7.outer" + %stack_var_014.1 = phi i256 [ 7, %"block_rt_131/7.outer" ], [ %subtraction_result7209, %"block_rt_131/7.backedge" ] + %and_result7206 = and i256 %stack_var_014.1, 255 + %subtraction_result7209 = add nsw i256 %and_result7206, -1 + br label %remainder_join12901 + +conditional_rt_49_join_block2921: ; preds = %"block_rt_44/7" + unreachable + +"block_rt_54/7.thread": ; preds = %"block_rt_51/7" + br i1 %comparison_result3799.not, label %"block_rt_43/7", label %"block_rt_56/7.preheader" + +"block_rt_56/7.preheader": ; preds = %"block_rt_54/7.thread" + br label %"block_rt_56/7.outer" + +conditional_rt_68_join_block5874: ; preds = %"block_rt_123/8", %"block_rt_64/7" + %stack_var_012.2 = phi i256 [ %addition_result12201, %"block_rt_123/8" ], [ 1, %"block_rt_64/7" ] + %and_result5861 = and i256 %stack_var_012.2, 255 + br label %"block_rt_70/7" + +conditional_rt_74_join_block6475: ; preds = %"block_rt_73/7" + switch i8 %trunc, label %"block_rt_131/7.outer" [ + i8 5, label %conditional_rt_76_join_block6491 + i8 25, label %"block_rt_123/8" + ] + +conditional_rt_76_join_block6491: ; preds = %conditional_rt_74_join_block6475 + unreachable + +conditional_rt_90_join_block7798: ; preds = %"block_rt_88/7.outer" + unreachable + +remainder_join12901: ; preds = %"block_rt_131/7" + %remainder_result_non_zero12904 = and i256 %subtraction_result7209, 1 + br i1 poison, label %"block_rt_131/7.backedge", label %"block_rt_86/7" + +"block_rt_131/7.backedge": ; preds = %remainder_join12901, %"block_rt_88/7.outer" + br label %"block_rt_131/7" + +remainder_join12933: ; preds = %"block_rt_101/7" + switch i8 %trunc, label %"block_rt_108/7" [ + i8 8, label %"block_rt_43/7" + i8 13, label %"block_rt_103/7" + ] +} diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-2.ll b/llvm/test/CodeGen/EVM/stack-too-deep-2.ll new file mode 100644 index 000000000000..7afc440e0851 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-too-deep-2.ll @@ -0,0 +1,244 @@ +; RUN: not --crash llc < %s -o /dev/null 2>&1 | FileCheck %s + +; Test that EVMStackSolver can catch errors when transformation from +; the BBs entry stack to the BBs exit stack (empty BB) is not possible. + +; The stack built for the 'bad' BB is +; +; 69.: +; [ RET %137 %8 %0 %105 %131 %154 %126 %18 %26 %28 %20 %81 %7 %19 %85 %3 %137 %16 %58 %63 ] +; +; [ RET %137 %20 %16 %0 %105 %131 %154 %126 %18 %26 %58 %137 %7 %19 %85 %81 %63 %3 %8 1 255 %28 ] + +; CHECK: EVMStackSolver cannot transform +; CHECK-SAME: [ RET %137 %8 %0 %105 %131 %154 %126 %18 %26 %28 %20 %81 %7 %19 %85 %3 %137 %16 %58 %63 ] +; CHECK-SAME: to +; CHECK-SAME: [ RET %137 %20 %16 %0 %105 %131 %154 %126 %18 %26 %58 %137 %7 %19 %85 %81 %63 %3 %8 1 255 %28 ] +; CHECK-SAME: : stack too deep. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @checked_mul_uint8(i256) + +define private fastcc i256 @fun_test_462(i256 %0) unnamed_addr { +entry: + %and_result3 = and i256 %0, 255 + %trunc = trunc i256 %0 to i8 + %comparison_result38 = icmp eq i256 %and_result3, 22 + %comparison_result45 = icmp eq i256 %and_result3, 33 + %comparison_result68 = icmp eq i256 %and_result3, 3 + %comparison_result113 = icmp eq i256 %and_result3, 4 + %comparison_result126 = icmp eq i256 %and_result3, 24 + %comparison_result187 = icmp eq i256 %and_result3, 26 + %comparison_result237 = icmp eq i256 %and_result3, 31 + %comparison_result255 = icmp eq i256 %and_result3, 32 + %comparison_result282 = icmp eq i256 %and_result3, 28 + %comparison_result330 = icmp eq i256 %and_result3, 6 + br label %for_condition + +return.loopexit572.split.loop.exit599: ; preds = %if_join42 + %var_cnt.2.mux.le = select i1 %spec.select, i256 %var_cnt.2, i256 30 + br label %return + +return: ; preds = %if_join295, %for_join195, %for_body193, %for_body132, %if_join110, %checked_mul_uint8_1420.exit, %for_body, %for_condition, %return.loopexit572.split.loop.exit599 + %return_pointer.0 = phi i256 [ 10, %checked_mul_uint8_1420.exit ], [ %var_cnt.2.mux.le, %return.loopexit572.split.loop.exit599 ], [ 80, %if_join295 ], [ 70, %for_body193 ], [ 60, %for_join195 ], [ 50, %for_body132 ], [ 40, %if_join110 ], [ 0, %for_body ], [ %var_cnt.0, %for_condition ] + ret i256 %return_pointer.0 + +for_condition: ; preds = %for_increment, %entry + %var_i.0 = phi i256 [ 0, %entry ], [ %addition_result348, %for_increment ] + %var_cnt.0 = phi i256 [ 0, %entry ], [ %var_cnt.1, %for_increment ] + %comparison_result = icmp ult i256 %var_i.0, 2 + br i1 %comparison_result, label %for_body, label %return + +for_body: ; preds = %for_condition + switch i8 %trunc, label %for_condition22 [ + i8 1, label %checked_mul_uint8_1420.exit + i8 21, label %return + i8 11, label %for_increment + ] + +for_increment: ; preds = %for_condition22, %for_body + %var_cnt.1 = phi i256 [ %var_cnt.0, %for_body ], [ %var_cnt.2, %for_condition22 ] + %addition_result348 = add nuw nsw i256 %var_i.0, 1 + br label %for_condition + +checked_mul_uint8_1420.exit: ; preds = %for_body + br label %return + +for_condition22: ; preds = %for_join65, %for_body + %var_j.0 = phi i256 [ %checked_mul_uint8_call, %for_join65 ], [ 1, %for_body ] + %var_cnt.2 = phi i256 [ %var_cnt.3.lcssa541, %for_join65 ], [ %var_cnt.0, %for_body ] + %comparison_result29 = icmp ugt i256 %var_j.0, 3 + %or.cond = or i1 %comparison_result38, %comparison_result29 + br i1 %or.cond, label %for_increment, label %if_join42 + +if_join42: ; preds = %for_condition22 + %comparison_result55 = icmp ugt i256 %var_j.0, 1 + %spec.select = and i1 %comparison_result45, %comparison_result55 + %brmerge = or i1 %spec.select, %comparison_result68 + br i1 %brmerge, label %return.loopexit572.split.loop.exit599, label %for_condition62.outer + +for_condition62.outer: ; preds = %if_join117, %if_join42 + %var_p.0.ph = phi i256 [ 0, %if_join42 ], [ %addition_result.i, %if_join117 ] + %var_cnt.3.ph = phi i256 [ %var_cnt.2, %if_join42 ], [ %var_cnt.5, %if_join117 ] + br label %for_condition62 + +for_condition62: ; preds = %if_join84, %for_condition62.outer + %var_p.0 = phi i256 [ %addition_result.i, %if_join84 ], [ %var_p.0.ph, %for_condition62.outer ] + %and_result.i410 = and i256 %var_p.0, 255 + %comparison_result.i = icmp ugt i256 %and_result.i410, 253 + br i1 %comparison_result.i, label %shift_left_non_overflow.i411, label %checked_add_uint8.exit + +for_join65: ; preds = %if_join84, %checked_add_uint8.exit + %var_cnt.3.lcssa541 = phi i256 [ %var_cnt.2, %if_join84 ], [ %var_cnt.3.ph, %checked_add_uint8.exit ] + %checked_mul_uint8_call = tail call fastcc i256 @checked_mul_uint8(i256 %var_j.0) + br label %for_condition22 + +shift_left_non_overflow.i411: ; preds = %if_join309, %for_condition62 + unreachable + +checked_add_uint8.exit: ; preds = %for_condition62 + %addition_result.i = add nuw nsw i256 %and_result.i410, 2 + %and_result78 = and i256 %addition_result.i, 7 + %comparison_result80 = icmp eq i256 %and_result78, 0 + br i1 %comparison_result80, label %for_join65, label %if_join84 + +if_join84: ; preds = %checked_add_uint8.exit + %trunc2 = trunc i256 %and_result78 to i8 + switch i8 %trunc2, label %if_join110 [ + i8 12, label %for_condition62 + i8 23, label %for_join65 + ] + +if_join110: ; preds = %increment_uint8.exit, %if_join84 + %var_h.0 = phi i256 [ %addition_result.i414, %increment_uint8.exit ], [ 1, %if_join84 ] + %var_cnt.5 = phi i256 [ %var_cnt.6.lcssa, %increment_uint8.exit ], [ %var_cnt.3.ph, %if_join84 ] + br i1 %comparison_result113, label %return, label %if_join117 + +if_join117: ; preds = %if_join110 + %comparison_result119 = icmp ugt i256 %var_h.0, 2 + %or.cond401 = or i1 %comparison_result126, %comparison_result119 + br i1 %or.cond401, label %for_condition62.outer, label %for_condition131 + +for_condition131: ; preds = %for_join159, %if_join117 + %var_k.0 = phi i256 [ %addition_result336, %for_join159 ], [ 10, %if_join117 ] + %var_cnt.6 = phi i256 [ %var_cnt.7.ph645, %for_join159 ], [ %var_cnt.5, %if_join117 ] + %comparison_result137 = icmp ult i256 %var_k.0, 12 + br i1 %comparison_result137, label %for_body132, label %increment_uint8.exit + +for_body132: ; preds = %for_condition131 + %trunc3 = trunc i256 %var_cnt.6 to i8 + switch i8 %trunc3, label %if_join167.outer [ + i8 5, label %return + i8 25, label %increment_uint8.exit + ] + +increment_uint8.exit: ; preds = %for_body132, %for_condition131 + %var_cnt.6.lcssa = phi i256 [ %var_cnt.6, %for_condition131 ], [ %var_cnt.5, %for_body132 ] + %addition_result.i414 = add nuw nsw i256 %var_h.0, 1 + br label %if_join110 + +for_join159: ; preds = %if_join184, %if_join167 + %addition_result336 = add nuw nsw i256 %var_k.0, 1 + br label %for_condition131 + +if_join167.outer: ; preds = %if_join167.outer.backedge, %for_body132 + %var_x.0.ph = phi i256 [ %addition_result, %if_join167.outer.backedge ], [ 7, %for_body132 ] + %var_cnt.7.ph645 = phi i256 [ %var_cnt.7.ph645.be, %if_join167.outer.backedge ], [ %var_cnt.6, %for_body132 ] + br label %if_join167 + +if_join167: ; preds = %if_join175, %if_join167.outer + %var_x.0 = phi i256 [ %addition_result, %if_join175 ], [ %var_x.0.ph, %if_join167.outer ] + %and_result161 = and i256 %var_x.0, 255 + %addition_result = add nsw i256 %and_result161, -1 + %comparison_result171 = icmp eq i256 %addition_result, 0 + br i1 %comparison_result171, label %for_join159, label %if_join175 + +if_join175: ; preds = %if_join167 + %and_result177 = and i256 %addition_result, 1 + %comparison_result180 = icmp eq i256 %and_result177, 0 + br i1 %comparison_result180, label %if_join167, label %if_join184 + +if_join184: ; preds = %if_join175 + br i1 %comparison_result187, label %for_join159, label %for_condition192.outer + +for_condition192.outer: ; preds = %for_condition271, %if_join184 + %var_y.0.ph = phi i256 [ 10, %if_join184 ], [ %addition_result.i427, %for_condition271 ] + %var_cnt.9.ph = phi i256 [ %var_cnt.7.ph645, %if_join184 ], [ %var_cnt.11, %for_condition271 ] + br label %for_condition192 + +for_condition192: ; preds = %for_condition192.backedge, %for_condition192.outer + %var_y.0 = phi i256 [ %var_y.0.ph, %for_condition192.outer ], [ %addition_result.i427, %for_condition192.backedge ] + %comparison_result198 = icmp ult i256 %var_y.0, 17 + br i1 %comparison_result198, label %for_body193, label %for_join195 + +for_body193: ; preds = %for_condition192 + switch i8 %trunc, label %checked_add_t_uint8.exit [ + i8 7, label %return + i8 27, label %if_join167.outer.backedge + ] + +for_join195: ; preds = %if_join228, %for_condition192 + br i1 %comparison_result330, label %return, label %if_join167.outer.backedge + +if_join167.outer.backedge: ; preds = %if_join252, %for_join195, %for_body193 + %var_cnt.7.ph645.be = phi i256 [ %var_cnt.9.ph, %for_join195 ], [ %var_cnt.7.ph645, %for_body193 ], [ %var_cnt.9.ph, %if_join252 ] + br label %if_join167.outer + +checked_add_t_uint8.exit: ; preds = %for_body193 + %addition_result.i427 = add nuw nsw i256 %var_y.0, 1 + %remainder_result_non_zero.lhs.trunc = trunc nuw i256 %addition_result.i427 to i8 + %remainder_result_non_zero429 = urem i8 %remainder_result_non_zero.lhs.trunc, 3 + %comparison_result224 = icmp eq i8 %remainder_result_non_zero429, 0 + br i1 %comparison_result224, label %for_condition192.backedge, label %if_join228 + +if_join228: ; preds = %checked_add_t_uint8.exit + %comparison_result230 = icmp eq i256 %addition_result.i427, 16 + br i1 %comparison_result230, label %for_join195, label %if_join234 + +if_join234: ; preds = %if_join228 + %comparison_result247 = icmp ugt i256 %var_y.0, 10 + %spec.select402 = and i1 %comparison_result237, %comparison_result247 + br i1 %spec.select402, label %for_condition192.backedge, label %if_join252 + +for_condition192.backedge: ; preds = %if_join234, %checked_add_t_uint8.exit + br label %for_condition192 + +if_join252: ; preds = %if_join234 + %comparison_result265 = icmp ugt i256 %var_y.0, 12 + %spec.select403 = and i1 %comparison_result255, %comparison_result265 + br i1 %spec.select403, label %if_join167.outer.backedge, label %for_condition271 + +for_condition271: ; preds = %for_increment273, %if_join252 + %var_l.0 = phi i256 [ %addition_result326, %for_increment273 ], [ 0, %if_join252 ] + %var_cnt.11 = phi i256 [ %var_cnt.12, %for_increment273 ], [ %var_cnt.9.ph, %if_join252 ] + %comparison_result277 = icmp ugt i256 %var_l.0, 3 + %or.cond404 = or i1 %comparison_result282, %comparison_result277 + br i1 %or.cond404, label %for_condition192.outer, label %if_join286 + +for_increment273: ; preds = %if_join318, %if_join295, %if_join286 + %var_cnt.12 = phi i256 [ %var_cnt.11, %if_join286 ], [ %addition_result312, %if_join318 ], [ %var_cnt.11, %if_join295 ] + %addition_result326 = add nuw nsw i256 %var_l.0, 1 + br label %for_condition271 + +if_join286: ; preds = %for_condition271 + %and_result288 = and i256 %var_l.0, 1 + %comparison_result291 = icmp eq i256 %and_result288, 0 + br i1 %comparison_result291, label %for_increment273, label %if_join295 + +if_join295: ; preds = %if_join286 + switch i8 %trunc, label %if_join309 [ + i8 8, label %return + i8 13, label %for_increment273 + ] + +if_join309: ; preds = %if_join295 + %and_result311 = and i256 %var_cnt.11, 18446744073709551615 + %comparison_result314 = icmp eq i256 %and_result311, 18446744073709551615 + br i1 %comparison_result314, label %shift_left_non_overflow.i411, label %if_join318 + +if_join318: ; preds = %if_join309 + %addition_result312 = add nuw nsw i256 %and_result311, 1 + br label %for_increment273 +} diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-3.ll b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll new file mode 100644 index 000000000000..245741b8ded6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll @@ -0,0 +1,125 @@ +; RUN: not --crash llc < %s -o /dev/null 2>&1 | FileCheck %s + +; Test that EVMStackSolver can catch errors when transformation from +; an MIs exit stack to an entry stack of the next MI is not possible. + +; The stack built for the 'bad' BB is +; +; 2.block_rt_158/3: +; [ %19 %64 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 ] +; +; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %64 %19 ] +; CALLDATALOAD +; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %64 %4 ] +; +; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %4 5 ] +; SHL +; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %5 ] +; +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %19 %5 0 ] +; SUB +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %19 %46 ] +; +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %46 32 %19 ] +; ADD +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %46 %71 ] +; +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %46 %71 ] +; GT +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %47 ] +; +; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %47 ] + +; CHECK: EVMStackSolver cannot transform +; CHECK-SAME: [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %5 ] +; CHECK-SAME: to +; CHECK-SAME: [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %19 %5 0 ] +; CHECK-SAME: : stack too deep. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #0 + +define dso_local fastcc void @main() unnamed_addr { +entry: + br i1 poison, label %"block_rt_7/0", label %"block_rt_2/0" + +"block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_158/3", %entry + unreachable + +"block_rt_7/0": ; preds = %entry + %calldata_load_result2781 = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %addition_result2798 = add nuw nsw i256 %calldata_load_result2781, 4 + %calldataload_pointer2604 = inttoptr i256 %addition_result2798 to ptr addrspace(2) + %calldata_load_result2605 = load i256, ptr addrspace(2) %calldataload_pointer2604, align 1 + %addition_result2659 = add nuw nsw i256 %calldata_load_result2781, 36 + %addition_result1186 = add nsw i256 0, -99 + %subtraction_result1906 = add nsw i256 0, -31 + br label %conditional_rt_187_join_block + +"block_rt_158/3": ; preds = %conditional_rt_181_join_block + %addition_result3239 = add i256 0, %addition_result3756 + %calldataload_pointer3244 = inttoptr i256 %addition_result3239 to ptr addrspace(2) + %calldata_load_result3245 = load i256, ptr addrspace(2) %calldataload_pointer3244, align 1 + %addition_result3251 = add i256 %addition_result3239, 32 + %shift_left_non_overflow_result3390 = shl nuw nsw i256 %calldata_load_result3245, 5 + %subtraction_result3395 = sub i256 0, %shift_left_non_overflow_result3390 + %comparison_result3399.not = icmp sgt i256 %addition_result3251, %subtraction_result3395 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + +"block_rt_160/3": ; preds = %"block_rt_158/3" + %memory_store_pointer3530 = inttoptr i256 %stack_var_021.06950 to ptr addrspace(1) + %addition_result3535 = add i256 %stack_var_021.0.in6947, 96 + %memory_store_pointer3538 = inttoptr i256 %addition_result3535 to ptr addrspace(1) + store i256 %calldata_load_result3245, ptr addrspace(1) %memory_store_pointer3538, align 1 + %addition_result3694 = add i256 %shift_left_non_overflow_result3390, %stack_var_021.06950 + %addition_result3972 = add i256 %stack_var_022.06948, 32 + %addition_result3978 = add i256 %stack_var_017.26946, 32 + %stack_var_021.0 = add i256 %addition_result3694, 64 + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + +"block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4058 = add i256 %stack_var_012.36954, 32 + %addition_result4064 = add i256 %stack_var_011.36953, 32 + %addition_result4044 = add nuw i256 %stack_var_013.36955, 1 + %comparison_result4003.not = icmp ult i256 %addition_result4044, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + +"block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + %addition_result451 = add i256 %stack_var_021.0, 64 + %mcopy_destination455 = inttoptr i256 %addition_result451 to ptr addrspace(1) + tail call void @llvm.memmove.p1.p1.i256(ptr addrspace(1) align 1 %mcopy_destination455, ptr addrspace(1) align 1 poison, i256 poison, i1 false) + unreachable + +"block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = add i256 0, %addition_result2659 + br label %conditional_rt_181_join_block + +conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ poison, %"block_rt_188/0" ], [ %stack_var_021.0, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_022.06948 = phi i256 [ %addition_result4054, %"block_rt_188/0" ], [ %addition_result3972, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ %stack_var_010.26952, %"block_rt_188/0" ], [ %addition_result3694, %"block_rt_160/3" ] + %stack_var_017.26946 = phi i256 [ %stack_var_010.26952, %"block_rt_188/0" ], [ %addition_result3978, %"block_rt_160/3" ] + %subtraction_result3918 = sub i256 %stack_var_021.06950, %stack_var_010.26952 + %memory_store_pointer3922 = inttoptr i256 %stack_var_017.26946 to ptr addrspace(1) + store i256 %subtraction_result3918, ptr addrspace(1) %memory_store_pointer3922, align 1 + %calldataload_pointer1898 = inttoptr i256 %stack_var_022.06948 to ptr addrspace(2) + %addition_result3756 = add i256 0, %addition_result4054 + %addition_result1811 = sub i256 %subtraction_result1906, %addition_result3756 + %comparison_result1815.not = icmp slt i256 0, %addition_result1811 + br i1 %comparison_result1815.not, label %"block_rt_158/3", label %"block_rt_2/0" + +conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %"block_rt_7/0" + %stack_var_013.36955 = phi i256 [ 0, %"block_rt_7/0" ], [ %addition_result4044, %"block_rt_181/0" ] + %stack_var_012.36954 = phi i256 [ %addition_result2659, %"block_rt_7/0" ], [ %addition_result4058, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 224, %"block_rt_7/0" ], [ %addition_result4064, %"block_rt_181/0" ] + %stack_var_010.26952 = phi i256 [ poison, %"block_rt_7/0" ], [ %stack_var_021.0, %"block_rt_181/0" ] + %memory_store_pointer4021 = inttoptr i256 %stack_var_011.36953 to ptr addrspace(1) + %calldataload_pointer4024 = inttoptr i256 %stack_var_012.36954 to ptr addrspace(2) + %comparison_result4030.not = icmp slt i256 0, %addition_result1186 + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" +} + +attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } From 35a6616663a30f42e16f836c39c11407a2f17c00 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 16 Jun 2025 13:23:14 +0200 Subject: [PATCH 133/203] [EVM] Add pre-commit tests for Implement constant folding of some intrinsics Signed-off-by: Vladimir Radosavljevic --- .../EVM/constant-folding-earlycse-addmod.ll | 58 +++ .../EVM/constant-folding-earlycse-byte.ll | 387 ++++++++++++++++++ .../EVM/constant-folding-earlycse-exp.ll | 256 ++++++++++++ .../EVM/constant-folding-earlycse-mulmod.ll | 58 +++ .../constant-folding-earlycse-signextend.ll | 357 ++++++++++++++++ 5 files changed, 1116 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll create mode 100644 llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll create mode 100644 llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll create mode 100644 llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll create mode 100644 llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll new file mode 100644 index 000000000000..68869d6101de --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.addmod(i256, i256, i256) + +define i256 @test_addmod1() { +; CHECK-LABEL: define i256 @test_addmod1() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 -1, i256 2, i256 2) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; Params are treated as unsigned values + %res = call i256 @llvm.evm.addmod(i256 -1, i256 2, i256 2) + ret i256 %res +} + +define i256 @test_addmod2() { +; CHECK-LABEL: define i256 @test_addmod2() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 5) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 5) + ret i256 %res +} + +define i256 @test_addmod3() { +; CHECK-LABEL: define i256 @test_addmod3() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 48, i256 undef, i256 5) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.addmod(i256 48, i256 undef, i256 5) + ret i256 %res +} + +define i256 @test_addmod4() { +; CHECK-LABEL: define i256 @test_addmod4() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 undef, i256 48, i256 5) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.addmod(i256 undef, i256 48, i256 5) + ret i256 %res +} + +define i256 @test_addmod5() { +; CHECK-LABEL: define i256 @test_addmod5() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 undef) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 undef) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll new file mode 100644 index 000000000000..b6d27c460c03 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll @@ -0,0 +1,387 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.byte(i256, i256) + +define i256 @test_byte1() { +; CHECK-LABEL: define i256 @test_byte1() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 undef, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 undef, i256 1) + ret i256 %res +} + +define i256 @test_byte2() { +; CHECK-LABEL: define i256 @test_byte2() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 undef) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 undef) + ret i256 %res +} + +define i256 @test_byte3() { +; CHECK-LABEL: define i256 @test_byte3() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 0) + ret i256 %res +} + +define i256 @test_byte4() { +; CHECK-LABEL: define i256 @test_byte4() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 1) + ret i256 %res +} + +define i256 @test_byte5() { +; CHECK-LABEL: define i256 @test_byte5() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 62786337629547936342664354281295019512044052096983040078175507080572122364) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 62786337629547936342664354281295019512044052096983040078175507080572122364) + ret i256 %res +} + +define i256 @test_byte6() { +; CHECK-LABEL: define i256 @test_byte6() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte7() { +; CHECK-LABEL: define i256 @test_byte7() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 -213508454229078891452382036238048110874681386347114622284045643289719458749) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 -213508454229078891452382036238048110874681386347114622284045643289719458749) + ret i256 %res +} + +define i256 @test_byte8() { +; CHECK-LABEL: define i256 @test_byte8() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 0, i256 -1) + ret i256 %res +} + +define i256 @test_byte9() { +; CHECK-LABEL: define i256 @test_byte9() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 0) + ret i256 %res +} + +define i256 @test_byte10() { +; CHECK-LABEL: define i256 @test_byte10() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 1) + ret i256 %res +} + +define i256 @test_byte11() { +; CHECK-LABEL: define i256 @test_byte11() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 15831896390776628077873594548411842773272337831711882241313510853617203623164) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 15831896390776628077873594548411842773272337831711882241313510853617203623164) + ret i256 %res +} + +define i256 @test_byte12() { +; CHECK-LABEL: define i256 @test_byte12() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte13() { +; CHECK-LABEL: define i256 @test_byte13() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 -54279028636447639376701285558971354695195688630741054740805195402843884604349) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 -54279028636447639376701285558971354695195688630741054740805195402843884604349) + ret i256 %res +} + +define i256 @test_byte14() { +; CHECK-LABEL: define i256 @test_byte14() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 1, i256 -1) + ret i256 %res +} + +define i256 @test_byte15() { +; CHECK-LABEL: define i256 @test_byte15() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 0) + ret i256 %res +} + +define i256 @test_byte16() { +; CHECK-LABEL: define i256 @test_byte16() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 1) + ret i256 %res +} + +define i256 @test_byte17() { +; CHECK-LABEL: define i256 @test_byte17() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 16073302433164271703722074696011524995083260326051756269332189763149975074044) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 16073302433164271703722074696011524995083260326051756269332189763149975074044) + ret i256 %res +} + +define i256 @test_byte18() { +; CHECK-LABEL: define i256 @test_byte18() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte19() { +; CHECK-LABEL: define i256 @test_byte19() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 -54658164282644196211809801276940316383917872613704572060086920199132892875709) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 -54658164282644196211809801276940316383917872613704572060086920199132892875709) + ret i256 %res +} + +define i256 @test_byte20() { +; CHECK-LABEL: define i256 @test_byte20() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 17, i256 -1) + ret i256 %res +} + +define i256 @test_byte21() { +; CHECK-LABEL: define i256 @test_byte21() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 0) + ret i256 %res +} + +define i256 @test_byte22() { +; CHECK-LABEL: define i256 @test_byte22() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 1) + ret i256 %res +} + +define i256 @test_byte23() { +; CHECK-LABEL: define i256 @test_byte23() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 16073302433164271703722074696011524995083277336827658260012929812626463325184) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 16073302433164271703722074696011524995083277336827658260012929812626463325184) + ret i256 %res +} + +define i256 @test_byte24() { +; CHECK-LABEL: define i256 @test_byte24() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte25() { +; CHECK-LABEL: define i256 @test_byte25() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 -54658164282644196211809801276940316383918434904861343304715684682168181439489) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 -54658164282644196211809801276940316383918434904861343304715684682168181439489) + ret i256 %res +} + +define i256 @test_byte26() { +; CHECK-LABEL: define i256 @test_byte26() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 31, i256 -1) + ret i256 %res +} + +define i256 @test_byte27() { +; CHECK-LABEL: define i256 @test_byte27() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 0) + ret i256 %res +} + +define i256 @test_byte28() { +; CHECK-LABEL: define i256 @test_byte28() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 1) + ret i256 %res +} + +define i256 @test_byte29() { +; CHECK-LABEL: define i256 @test_byte29() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte30() { +; CHECK-LABEL: define i256 @test_byte30() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 32, i256 -1) + ret i256 %res +} + +define i256 @test_byte31() { +; CHECK-LABEL: define i256 @test_byte31() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 0) + ret i256 %res +} + +define i256 @test_byte32() { +; CHECK-LABEL: define i256 @test_byte32() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 1) + ret i256 %res +} + +define i256 @test_byte33() { +; CHECK-LABEL: define i256 @test_byte33() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte34() { +; CHECK-LABEL: define i256 @test_byte34() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 -1) + ret i256 %res +} + +define i256 @test_byte35() { +; CHECK-LABEL: define i256 @test_byte35() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 0) + ret i256 %res +} + +define i256 @test_byte36() { +; CHECK-LABEL: define i256 @test_byte36() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 1) + ret i256 %res +} + +define i256 @test_byte37() { +; CHECK-LABEL: define i256 @test_byte37() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_byte38() { +; CHECK-LABEL: define i256 @test_byte38() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.byte(i256 -1, i256 -1) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll new file mode 100644 index 000000000000..99a664e89dc0 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll @@ -0,0 +1,256 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.exp(i256, i256) + +define i256 @test_exponent1() { +; CHECK-LABEL: define i256 @test_exponent1() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 10) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 0, i256 10) + ret i256 %res +} + +define i256 @test_exponent2() { +; CHECK-LABEL: define i256 @test_exponent2() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 2, i256 undef) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 2, i256 undef) + ret i256 %res +} + +define i256 @test_exponent3() { +; CHECK-LABEL: define i256 @test_exponent3() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 2, i256 255) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 2, i256 255) + ret i256 %res +} + +define i256 @test_exponent4() { +; CHECK-LABEL: define i256 @test_exponent4() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 307, i256 32) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 307, i256 32) + ret i256 %res +} + +define i256 @test_exponent5() { +; CHECK-LABEL: define i256 @test_exponent5() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 undef, i256 2) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 undef, i256 2) + ret i256 %res +} + +define i256 @test_exponent6() { +; CHECK-LABEL: define i256 @test_exponent6() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 0, i256 0) + ret i256 %res +} + +define i256 @test_exponent7() { +; CHECK-LABEL: define i256 @test_exponent7() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 1, i256 0) + ret i256 %res +} + +define i256 @test_exponent7.1() { +; CHECK-LABEL: define i256 @test_exponent7.1() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 1, i256 1) + ret i256 %res +} + +define i256 @test_exponent8() { +; CHECK-LABEL: define i256 @test_exponent8() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 433478394034343) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 0, i256 433478394034343) + ret i256 %res +} + +define i256 @test_exponent9() { +; CHECK-LABEL: define i256 @test_exponent9() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 121563127839120, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 121563127839120, i256 0) + ret i256 %res +} + +define i256 @test_exponent10() { +; CHECK-LABEL: define i256 @test_exponent10() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 433478394034343) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 1, i256 433478394034343) + ret i256 %res +} + +define i256 @test_exponent11() { +; CHECK-LABEL: define i256 @test_exponent11() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 121563127839120, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 121563127839120, i256 1) + ret i256 %res +} + +define i256 @test_exponent12() { +; CHECK-LABEL: define i256 @test_exponent12() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 21, i256 52) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.exp(i256 21, i256 52) + ret i256 %res +} + +define i256 @test_exponent13() { +; CHECK-LABEL: define i256 @test_exponent13() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 340282366920938463463374607431768211455, i256 2) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 2 -> + ; 0xfffffffffffffffffffffffffffffffe00000000000000000000000000000001 + %res = call i256 @llvm.evm.exp(i256 340282366920938463463374607431768211455, i256 2) + ret i256 %res +} + +define i256 @test_exponent14() { +; CHECK-LABEL: define i256 @test_exponent14() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 65536, i256 16) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0x0000000000000000000000000000000000000000000000000000000000010000 ^ 16 -> 0 + %res = call i256 @llvm.evm.exp(i256 65536, i256 16) + ret i256 %res +} + +define i256 @test_exponent15() { +; CHECK-LABEL: define i256 @test_exponent15() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 21709470740815105492860156599188632070735699051917406219058709325770546741247, i256 -7627257922765187922181218227643122808087153271902696082090171450652228583425) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0x2fff1ffffffffff5ffffff0fffffffff2ffffffafffafffcffff1ff234ffffff ^ + ; 0xef231ffffffffff4f12fff34ffffffff2fffffbbfffafffdffff22f538ffffff -> + ; 46756260758475007021788099943083655901358133181480408838873172916982662561791 + %res = call i256 @llvm.evm.exp(i256 21709470740815105492860156599188632070735699051917406219058709325770546741247, i256 108164831314551007501389766781044785045182831393737867957367412557260901056511) + ret i256 %res +} + +define i256 @test_exponent16() { +; CHECK-LABEL: define i256 @test_exponent16() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0 ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0 + %res = call i256 @llvm.evm.exp(i256 0, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} + +define i256 @test_exponent17() { +; CHECK-LABEL: define i256 @test_exponent17() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ 0 - > 1 + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 0) + ret i256 %res +} + +define i256 @test_exponent18() { +; CHECK-LABEL: define i256 @test_exponent18() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 1 ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 1 + %res = call i256 @llvm.evm.exp(i256 1, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} + +define i256 @test_exponent19() { +; CHECK-LABEL: define i256 @test_exponent19() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ 1 - > + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 1) + ret i256 %res +} + +define i256 @test_exponent20() { +; CHECK-LABEL: define i256 @test_exponent20() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 7437834752357434334343423343443375834785783474, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 7437834752357434334343423343443375834785783474 ^ + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - > 0 + %res = call i256 @llvm.evm.exp(i256 7437834752357434334343423343443375834785783474, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} + +define i256 @test_exponent21() { +; CHECK-LABEL: define i256 @test_exponent21() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 23784273472384723848213821342323233223) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ + ; 23784273472384723848213821342323233223 - > + ; 115792089237316195423570985008687907853269984665640564039457584007913129639935 + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 23784273472384723848213821342323233223) + ret i256 %res +} + +define i256 @test_exponent22() { +; CHECK-LABEL: define i256 @test_exponent22() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> + ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + %res = call i256 @llvm.evm.exp(i256 115792089237316195423570985008687907853269984665640564039457584007913129639935, i256 115792089237316195423570985008687907853269984665640564039457584007913129639935) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll new file mode 100644 index 000000000000..e537ac276196 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.mulmod(i256, i256, i256) + +define i256 @test_mulmod1() { +; CHECK-LABEL: define i256 @test_mulmod1() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 -2, i256 -2, i256 3) +; CHECK-NEXT: ret i256 [[RES]] +; + + ; Params are treated as unsigned values + %res = call i256 @llvm.evm.mulmod(i256 -2, i256 -2, i256 3) + ret i256 %res +} + +define i256 @test_mulmod2() { +; CHECK-LABEL: define i256 @test_mulmod2() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 7) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 7) + ret i256 %res +} + +define i256 @test_mulmod3() { +; CHECK-LABEL: define i256 @test_mulmod3() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 undef, i256 17, i256 7) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.mulmod(i256 undef, i256 17, i256 7) + ret i256 %res +} + +define i256 @test_mulmod4() { +; CHECK-LABEL: define i256 @test_mulmod4() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 17, i256 undef, i256 7) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.mulmod(i256 17, i256 undef, i256 7) + ret i256 %res +} + +define i256 @test_mulmod5() { +; CHECK-LABEL: define i256 @test_mulmod5() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 undef) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 undef) + ret i256 %res +} diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll new file mode 100644 index 000000000000..6b0eac93a431 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll @@ -0,0 +1,357 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=early-cse -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.signextend(i256, i256) + +define i256 @test_signextend1() { +; CHECK-LABEL: define i256 @test_signextend1() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 255) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 255) + ret i256 %res +} + +define i256 @test_signextend2() { +; CHECK-LABEL: define i256 @test_signextend2() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 32767) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 32767) + ret i256 %res +} + +define i256 @test_signextend3() { +; CHECK-LABEL: define i256 @test_signextend3() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 undef, i256 32767) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 undef, i256 32767) + ret i256 %res +} + +define i256 @test_signextend4() { +; CHECK-LABEL: define i256 @test_signextend4() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 undef) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 undef) + ret i256 %res +} + +define i256 @test_signextend5() { +; CHECK-LABEL: define i256 @test_signextend5() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 32, i256 undef) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 32, i256 undef) + ret i256 %res +} + +define i256 @test_signextend6() { +; CHECK-LABEL: define i256 @test_signextend6() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 0) + ret i256 %res +} + +define i256 @test_signextend7() { +; CHECK-LABEL: define i256 @test_signextend7() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 1) + ret i256 %res +} + +define i256 @test_signextend8() { +; CHECK-LABEL: define i256 @test_signextend8() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend9() { +; CHECK-LABEL: define i256 @test_signextend9() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 -1) + ret i256 %res +} + +define i256 @test_signextend10() { +; CHECK-LABEL: define i256 @test_signextend10() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 0) + ret i256 %res +} + +define i256 @test_signextend11() { +; CHECK-LABEL: define i256 @test_signextend11() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 1) + ret i256 %res +} + +define i256 @test_signextend12() { +; CHECK-LABEL: define i256 @test_signextend12() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533094467) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533094467) + ret i256 %res +} + +define i256 @test_signextend13() { +; CHECK-LABEL: define i256 @test_signextend13() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 1, i256 -1) + ret i256 %res +} + +define i256 @test_signextend14() { +; CHECK-LABEL: define i256 @test_signextend14() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 17, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 17, i256 0) + ret i256 %res +} + +define i256 @test_signextend15() { +; CHECK-LABEL: define i256 @test_signextend15() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 4, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 4, i256 1) + ret i256 %res +} + +define i256 @test_signextend16() { +; CHECK-LABEL: define i256 @test_signextend16() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 22, i256 9111590707254702215554908033119796504552448929051039916) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 22, i256 9111590707254702215554908033119796504552448929051039916) + ret i256 %res +} + +define i256 @test_signextend17() { +; CHECK-LABEL: define i256 @test_signextend17() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 17, i256 25236770892255973364820642850062731514599596) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 17, i256 25236770892255973364820642850062731514599596) + ret i256 %res +} + +define i256 @test_signextend18() { +; CHECK-LABEL: define i256 @test_signextend18() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 22, i256 16774068411584146507346643168871342422646144539969050796) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 22, i256 16774068411584146507346643168871342422646144539969050796) + ret i256 %res +} + +define i256 @test_signextend19() { +; CHECK-LABEL: define i256 @test_signextend19() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 17, i256 93509753590725542020426220953279705230436762145237986916280948908) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 17, i256 93509753590725542020426220953279705230436762145237986916280948908) + ret i256 %res +} + +define i256 @test_signextend20() { +; CHECK-LABEL: define i256 @test_signextend20() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 25, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 25, i256 -1) + ret i256 %res +} + +define i256 @test_signextend21() { +; CHECK-LABEL: define i256 @test_signextend21() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 0) + ret i256 %res +} + +define i256 @test_signextend22() { +; CHECK-LABEL: define i256 @test_signextend22() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 1) + ret i256 %res +} + +define i256 @test_signextend23() { +; CHECK-LABEL: define i256 @test_signextend23() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 -49173855447680950519990795082874703144781591387221730505838393986436314155965) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 66618233789635244903580189925813204708488393278418833533619190021476815483971) + ret i256 %res +} + +define i256 @test_signextend24() { +; CHECK-LABEL: define i256 @test_signextend24() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 255, i256 -1) + ret i256 %res +} + +define i256 @test_signextend25() { +; CHECK-LABEL: define i256 @test_signextend25() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 256, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 256, i256 0) + ret i256 %res +} + +define i256 @test_signextend26() { +; CHECK-LABEL: define i256 @test_signextend26() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 256, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 256, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend27() { +; CHECK-LABEL: define i256 @test_signextend27() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 256, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 256, i256 -1) + ret i256 %res +} + +define i256 @test_signextend28() { +; CHECK-LABEL: define i256 @test_signextend28() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 1) + ret i256 %res +} + +define i256 @test_signextend29() { +; CHECK-LABEL: define i256 @test_signextend29() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend30() { +; CHECK-LABEL: define i256 @test_signextend30() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 -1) + ret i256 %res +} + +define i256 @test_signextend31() { +; CHECK-LABEL: define i256 @test_signextend31() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 0) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 0) + ret i256 %res +} + +define i256 @test_signextend32() { +; CHECK-LABEL: define i256 @test_signextend32() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 1) + ret i256 %res +} + +define i256 @test_signextend33() { +; CHECK-LABEL: define i256 @test_signextend33() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) + ret i256 %res +} + +define i256 @test_signextend34() { +; CHECK-LABEL: define i256 @test_signextend34() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 -1) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 -1, i256 -1) + ret i256 %res +} + +define i256 @test_signextend35() { +; CHECK-LABEL: define i256 @test_signextend35() { +; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 33023) +; CHECK-NEXT: ret i256 [[RES]] +; + + %res = call i256 @llvm.evm.signextend(i256 0, i256 33023) + ret i256 %res +} From a6517ae5ae6fc4ff773b1ebda13e54732ae0e10c Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 16 Jun 2025 13:05:04 +0200 Subject: [PATCH 134/203] [EVM] Implement constant folding of some intrinsics This patch implements constant folding of addmod, mulmod, exp, signextend and byte interinsics. It uses the same implementation as for EraVM, just for intrinsics, and not the stdlib calls. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Analysis/ConstantFolding.cpp | 184 ++++++++++++++++++ .../EVM/constant-folding-earlycse-addmod.ll | 15 +- .../EVM/constant-folding-earlycse-byte.ll | 114 ++++------- .../EVM/constant-folding-earlycse-exp.ll | 69 +++---- .../EVM/constant-folding-earlycse-mulmod.ll | 15 +- .../constant-folding-earlycse-signextend.ll | 105 ++++------ 6 files changed, 290 insertions(+), 212 deletions(-) diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 1c86e2948023..a03f82d682be 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -45,6 +45,9 @@ #include "llvm/IR/IntrinsicsAArch64.h" #include "llvm/IR/IntrinsicsAMDGPU.h" #include "llvm/IR/IntrinsicsARM.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +// EVM local end #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/IntrinsicsX86.h" #include "llvm/IR/Operator.h" @@ -1551,6 +1554,13 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) { case Intrinsic::arm_mve_vctp32: case Intrinsic::arm_mve_vctp64: case Intrinsic::aarch64_sve_convert_from_svbool: + // EVM local begin + case Intrinsic::evm_addmod: + case Intrinsic::evm_mulmod: + case Intrinsic::evm_byte: + case Intrinsic::evm_exp: + case Intrinsic::evm_signextend: + // EVM local end // WebAssembly float semantics are always known case Intrinsic::wasm_trunc_signed: case Intrinsic::wasm_trunc_unsigned: @@ -2577,6 +2587,91 @@ static Constant *evaluateCompare(const APFloat &Op1, const APFloat &Op2, return nullptr; } +// EVM local begin +/// Extending length of two’s complement signed integer. +/// NumByte: the size in bytes minus one of the integer to sign extend. +/// Val: the integer being extended. +static Constant *ConstantFoldSignextendCall(Type *Ty, const APInt &NumByte, + const APInt &Val) { + unsigned BitWidth = Ty->getIntegerBitWidth(); + + // Signextend operation returns original value if the extension + // overflows the type width. + if (NumByte.uge(APInt(BitWidth, (BitWidth / 8) - 1))) + return ConstantInt::get(Ty, Val); + + const APInt NumBits = NumByte * 8 + 7; + const APInt NumBitInv = APInt(BitWidth, BitWidth) - NumBits; + const APInt SignMask = APInt(BitWidth, 1).shl(NumBits); + const APInt ValMask = APInt::getAllOnes(BitWidth).lshr(NumBitInv); + const APInt Ext1 = APInt::getAllOnes(BitWidth).shl(NumBits); + const APInt SignVal = SignMask & Val; + const APInt ValClean = Val & ValMask; + const APInt Sext = SignVal.isZero() ? APInt::getZero(BitWidth) : Ext1; + const APInt Result = Sext | ValClean; + return ConstantInt::get(Ty, Result); +} + +/// Exponential operation. +/// Calculates: +/// (base ** exp) (mod 2**256) +/// This is an implementation of the binary exponentiation algorithm. +static Constant *ConstantFoldExpCall(Type *Ty, const APInt &Base, + const APInt &Exp) { + const unsigned BitWidth = Ty->getIntegerBitWidth(); + if (Base == 0 && Exp != 0) + return Constant::getNullValue(Ty); + if (Base == 1 || (Base == 0 && Exp == 0)) + return ConstantInt::get(Ty, APInt(BitWidth, 1)); + if (Base == 2 && Exp.ule(255)) + return ConstantInt::get(Ty, APInt(BitWidth, 1).shl(Exp)); + + // The target algorithm is an implementation of the following C code, + // where m = 2 ** 256 and 'long long' type is replaced with i256 one. + // long long binpow(long long a, long long b, long long m) { + // a %= m; + // long long res = 1; + // while (b > 0) { + // if (b & 1) + // res = res * a % m; + // a = a * a % m; + // b >>= 1; + // } + // return res; + // } + + // Perform calculations in double bit-width, as we need to + // multiply. + const unsigned ExtBitWidth = BitWidth * 2; + const APInt ModExt = APInt(ExtBitWidth, 1).shl(BitWidth); + APInt ResExt(ExtBitWidth, 1); + APInt BaseExt = Base.zext(ExtBitWidth); + APInt CurrExp = Exp; + while (CurrExp.ugt(0)) { + if (CurrExp[0]) + ResExt = (ResExt * BaseExt).urem(ModExt); + + BaseExt = (BaseExt * BaseExt).urem(ModExt); + CurrExp.lshrInPlace(1); + } + return ConstantInt::get(Ty, ResExt.trunc(BitWidth)); +} + +/// Retrieve single byte from i256 word. +/// Returns: +/// (Val << ByteIdx * 8) >> 248. +/// For the Nth byte, we count from the left +/// (i.e. N=0 would be the most significant in big endian), +/// 0, if ByteIdx > 255. +static Constant *ConstantFoldByteCall(Type *Ty, const APInt &ByteIdx, + const APInt &Val) { + // Please, note the case ByteIdx > 31 is properly handled by the shl + // implementation, see the comments for ConstantFoldSHRCall. + unsigned BitWidth = Ty->getIntegerBitWidth(); + return ConstantInt::get(Ty, Val.shl(ByteIdx * 8).lshr(BitWidth - 8)); +} +// EVM local end + static Constant *ConstantFoldLibCall2(StringRef Name, Type *Ty, ArrayRef Operands, const TargetLibraryInfo *TLI) { @@ -2646,6 +2741,33 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty, const CallBase *Call) { assert(Operands.size() == 2 && "Wrong number of operands."); + // EVM local begin + if (Operands[0]->getType()->isIntegerTy() && + Operands[1]->getType()->isIntegerTy()) { + const APInt *C0 = nullptr, *C1 = nullptr; + if (!getConstIntOrUndef(Operands[0], C0) || + !getConstIntOrUndef(Operands[1], C1)) + return nullptr; + + if (IntrinsicID == Intrinsic::evm_signextend || + IntrinsicID == Intrinsic::evm_exp || + IntrinsicID == Intrinsic::evm_byte) { + if (isa(Operands[0]) || isa(Operands[1]) || + !C0 || !C1) + return PoisonValue::get(Ty); + + assert(Ty->getIntegerBitWidth() == 256); + + if (IntrinsicID == Intrinsic::evm_signextend) + return ConstantFoldSignextendCall(Ty, *C0, *C1); + if (IntrinsicID == Intrinsic::evm_exp) + return ConstantFoldExpCall(Ty, *C0, *C1); + if (IntrinsicID == Intrinsic::evm_byte) + return ConstantFoldByteCall(Ty, *C0, *C1); + } + } + // EVM local end + if (Ty->isFloatingPointTy()) { // TODO: We should have undef handling for all of the FP intrinsics that // are attempted to be folded in this function. @@ -3097,6 +3219,46 @@ static Constant *ConstantFoldAMDGCNPermIntrinsic(ArrayRef Operands, return ConstantInt::get(Ty, Val); } +// EVM local begin +/// Unsigned modulo addition operation. +/// Returns: +/// 0, if Mod == 0 +/// (Arg1 + Arg2) (mod Mod), if Mod != 0 +/// All intermediate calculations of this operation are not subject +/// to the 2 ** 256 modulo. +static Constant *ConstantFoldAddModCall(Type *Ty, const APInt &Arg1, + const APInt &Arg2, const APInt &Mod) { + const unsigned BitWidth = Ty->getIntegerBitWidth(); + if (Mod.isZero()) + return Constant::getNullValue(Ty); + + // Sum representation of two N-bit values takes up to N + 1 bits. + const unsigned ExtBitWidth = BitWidth + 1; + const APInt Sum = Arg1.zext(ExtBitWidth) + Arg2.zext(ExtBitWidth); + const APInt Rem = Sum.urem(Mod.zext(ExtBitWidth)); + return ConstantInt::get(Ty, Rem.trunc(BitWidth)); +} + +/// Unsigned modulo multiplication operation. +/// Returns: +/// 0, if Mod == 0 +/// (Arg1 * Arg2) (mod Mod), if Mod != 0 +/// All intermediate calculations of this operation are not subject +/// to the 2**256 modulo. +static Constant *ConstantFoldMulModCall(Type *Ty, const APInt &Arg1, + const APInt &Arg2, const APInt &Mod) { + const unsigned BitWidth = Ty->getIntegerBitWidth(); + if (Mod.isZero()) + return Constant::getNullValue(Ty); + + // Multiplication representation of two N-bit values takes up to 2 * N bits. + const unsigned ExtBitWidth = BitWidth * 2; + const APInt Product = Arg1.zext(ExtBitWidth) * Arg2.zext(ExtBitWidth); + const APInt Rem = Product.urem(Mod.zext(ExtBitWidth)); + return ConstantInt::get(Ty, Rem.trunc(BitWidth)); +} +// EVM local end + static Constant *ConstantFoldScalarCall3(StringRef Name, Intrinsic::ID IntrinsicID, Type *Ty, @@ -3231,6 +3393,28 @@ static Constant *ConstantFoldScalarCall3(StringRef Name, if (IntrinsicID == Intrinsic::amdgcn_perm) return ConstantFoldAMDGCNPermIntrinsic(Operands, Ty); + // EVM local begin + if (IntrinsicID == Intrinsic::evm_addmod || + IntrinsicID == Intrinsic::evm_mulmod) { + const APInt *C0, *C1, *C2; + if (!getConstIntOrUndef(Operands[0], C0) || + !getConstIntOrUndef(Operands[1], C1) || + !getConstIntOrUndef(Operands[2], C2)) + return nullptr; + + if (isa(Operands[0]) || isa(Operands[1]) || + isa(Operands[2]) || !C0 || !C1 || !C2) + return PoisonValue::get(Ty); + + assert(Ty->getIntegerBitWidth() == 256); + + if (IntrinsicID == Intrinsic::evm_addmod) + return ConstantFoldAddModCall(Ty, *C0, *C1, *C2); + if (IntrinsicID == Intrinsic::evm_mulmod) + return ConstantFoldMulModCall(Ty, *C0, *C1, *C2); + } + // EVM local end + return nullptr; } diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll index 68869d6101de..f62917139629 100644 --- a/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-addmod.ll @@ -8,8 +8,7 @@ declare i256 @llvm.evm.addmod(i256, i256, i256) define i256 @test_addmod1() { ; CHECK-LABEL: define i256 @test_addmod1() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 -1, i256 2, i256 2) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; ; Params are treated as unsigned values @@ -19,8 +18,7 @@ define i256 @test_addmod1() { define i256 @test_addmod2() { ; CHECK-LABEL: define i256 @test_addmod2() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 5) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 4 ; %res = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 5) @@ -29,8 +27,7 @@ define i256 @test_addmod2() { define i256 @test_addmod3() { ; CHECK-LABEL: define i256 @test_addmod3() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 48, i256 undef, i256 5) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.addmod(i256 48, i256 undef, i256 5) @@ -39,8 +36,7 @@ define i256 @test_addmod3() { define i256 @test_addmod4() { ; CHECK-LABEL: define i256 @test_addmod4() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 undef, i256 48, i256 5) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.addmod(i256 undef, i256 48, i256 5) @@ -49,8 +45,7 @@ define i256 @test_addmod4() { define i256 @test_addmod5() { ; CHECK-LABEL: define i256 @test_addmod5() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 undef) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.addmod(i256 48, i256 1, i256 undef) diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll index b6d27c460c03..3b79d6d241af 100644 --- a/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-byte.ll @@ -8,8 +8,7 @@ declare i256 @llvm.evm.byte(i256, i256) define i256 @test_byte1() { ; CHECK-LABEL: define i256 @test_byte1() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 undef, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.byte(i256 undef, i256 1) @@ -18,8 +17,7 @@ define i256 @test_byte1() { define i256 @test_byte2() { ; CHECK-LABEL: define i256 @test_byte2() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 undef) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.byte(i256 1, i256 undef) @@ -28,8 +26,7 @@ define i256 @test_byte2() { define i256 @test_byte3() { ; CHECK-LABEL: define i256 @test_byte3() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 0, i256 0) @@ -38,8 +35,7 @@ define i256 @test_byte3() { define i256 @test_byte4() { ; CHECK-LABEL: define i256 @test_byte4() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 0, i256 1) @@ -48,8 +44,7 @@ define i256 @test_byte4() { define i256 @test_byte5() { ; CHECK-LABEL: define i256 @test_byte5() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 62786337629547936342664354281295019512044052096983040078175507080572122364) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 0, i256 62786337629547936342664354281295019512044052096983040078175507080572122364) @@ -58,8 +53,7 @@ define i256 @test_byte5() { define i256 @test_byte6() { ; CHECK-LABEL: define i256 @test_byte6() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 83 ; %res = call i256 @llvm.evm.byte(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -68,8 +62,7 @@ define i256 @test_byte6() { define i256 @test_byte7() { ; CHECK-LABEL: define i256 @test_byte7() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 -213508454229078891452382036238048110874681386347114622284045643289719458749) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 0, i256 -213508454229078891452382036238048110874681386347114622284045643289719458749) @@ -78,8 +71,7 @@ define i256 @test_byte7() { define i256 @test_byte8() { ; CHECK-LABEL: define i256 @test_byte8() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 0, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 0, i256 -1) @@ -88,8 +80,7 @@ define i256 @test_byte8() { define i256 @test_byte9() { ; CHECK-LABEL: define i256 @test_byte9() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 1, i256 0) @@ -98,8 +89,7 @@ define i256 @test_byte9() { define i256 @test_byte10() { ; CHECK-LABEL: define i256 @test_byte10() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 1, i256 1) @@ -108,8 +98,7 @@ define i256 @test_byte10() { define i256 @test_byte11() { ; CHECK-LABEL: define i256 @test_byte11() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 15831896390776628077873594548411842773272337831711882241313510853617203623164) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 1, i256 15831896390776628077873594548411842773272337831711882241313510853617203623164) @@ -118,8 +107,7 @@ define i256 @test_byte11() { define i256 @test_byte12() { ; CHECK-LABEL: define i256 @test_byte12() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 72 ; %res = call i256 @llvm.evm.byte(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -128,8 +116,7 @@ define i256 @test_byte12() { define i256 @test_byte13() { ; CHECK-LABEL: define i256 @test_byte13() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 -54279028636447639376701285558971354695195688630741054740805195402843884604349) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 1, i256 -54279028636447639376701285558971354695195688630741054740805195402843884604349) @@ -138,8 +125,7 @@ define i256 @test_byte13() { define i256 @test_byte14() { ; CHECK-LABEL: define i256 @test_byte14() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 1, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 1, i256 -1) @@ -148,8 +134,7 @@ define i256 @test_byte14() { define i256 @test_byte15() { ; CHECK-LABEL: define i256 @test_byte15() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 17, i256 0) @@ -158,8 +143,7 @@ define i256 @test_byte15() { define i256 @test_byte16() { ; CHECK-LABEL: define i256 @test_byte16() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 17, i256 1) @@ -168,8 +152,7 @@ define i256 @test_byte16() { define i256 @test_byte17() { ; CHECK-LABEL: define i256 @test_byte17() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 16073302433164271703722074696011524995083260326051756269332189763149975074044) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 17, i256 16073302433164271703722074696011524995083260326051756269332189763149975074044) @@ -178,8 +161,7 @@ define i256 @test_byte17() { define i256 @test_byte18() { ; CHECK-LABEL: define i256 @test_byte18() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 205 ; %res = call i256 @llvm.evm.byte(i256 17, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -188,8 +170,7 @@ define i256 @test_byte18() { define i256 @test_byte19() { ; CHECK-LABEL: define i256 @test_byte19() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 -54658164282644196211809801276940316383917872613704572060086920199132892875709) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 17, i256 -54658164282644196211809801276940316383917872613704572060086920199132892875709) @@ -198,8 +179,7 @@ define i256 @test_byte19() { define i256 @test_byte20() { ; CHECK-LABEL: define i256 @test_byte20() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 17, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 17, i256 -1) @@ -208,8 +188,7 @@ define i256 @test_byte20() { define i256 @test_byte21() { ; CHECK-LABEL: define i256 @test_byte21() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 31, i256 0) @@ -218,8 +197,7 @@ define i256 @test_byte21() { define i256 @test_byte22() { ; CHECK-LABEL: define i256 @test_byte22() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.byte(i256 31, i256 1) @@ -228,8 +206,7 @@ define i256 @test_byte22() { define i256 @test_byte23() { ; CHECK-LABEL: define i256 @test_byte23() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 16073302433164271703722074696011524995083277336827658260012929812626463325184) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 31, i256 16073302433164271703722074696011524995083277336827658260012929812626463325184) @@ -238,8 +215,7 @@ define i256 @test_byte23() { define i256 @test_byte24() { ; CHECK-LABEL: define i256 @test_byte24() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 67 ; %res = call i256 @llvm.evm.byte(i256 31, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -248,8 +224,7 @@ define i256 @test_byte24() { define i256 @test_byte25() { ; CHECK-LABEL: define i256 @test_byte25() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 -54658164282644196211809801276940316383918434904861343304715684682168181439489) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 31, i256 -54658164282644196211809801276940316383918434904861343304715684682168181439489) @@ -258,8 +233,7 @@ define i256 @test_byte25() { define i256 @test_byte26() { ; CHECK-LABEL: define i256 @test_byte26() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 31, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 255 ; %res = call i256 @llvm.evm.byte(i256 31, i256 -1) @@ -268,8 +242,7 @@ define i256 @test_byte26() { define i256 @test_byte27() { ; CHECK-LABEL: define i256 @test_byte27() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 32, i256 0) @@ -278,8 +251,7 @@ define i256 @test_byte27() { define i256 @test_byte28() { ; CHECK-LABEL: define i256 @test_byte28() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 32, i256 1) @@ -288,8 +260,7 @@ define i256 @test_byte28() { define i256 @test_byte29() { ; CHECK-LABEL: define i256 @test_byte29() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 32, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -298,8 +269,7 @@ define i256 @test_byte29() { define i256 @test_byte30() { ; CHECK-LABEL: define i256 @test_byte30() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 32, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 32, i256 -1) @@ -308,8 +278,7 @@ define i256 @test_byte30() { define i256 @test_byte31() { ; CHECK-LABEL: define i256 @test_byte31() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 0) @@ -318,8 +287,7 @@ define i256 @test_byte31() { define i256 @test_byte32() { ; CHECK-LABEL: define i256 @test_byte32() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 1) @@ -328,8 +296,7 @@ define i256 @test_byte32() { define i256 @test_byte33() { ; CHECK-LABEL: define i256 @test_byte33() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -338,8 +305,7 @@ define i256 @test_byte33() { define i256 @test_byte34() { ; CHECK-LABEL: define i256 @test_byte34() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 35242523534534534233424343343443, i256 -1) @@ -348,8 +314,7 @@ define i256 @test_byte34() { define i256 @test_byte35() { ; CHECK-LABEL: define i256 @test_byte35() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 -1, i256 0) @@ -358,8 +323,7 @@ define i256 @test_byte35() { define i256 @test_byte36() { ; CHECK-LABEL: define i256 @test_byte36() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 -1, i256 1) @@ -368,8 +332,7 @@ define i256 @test_byte36() { define i256 @test_byte37() { ; CHECK-LABEL: define i256 @test_byte37() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -378,8 +341,7 @@ define i256 @test_byte37() { define i256 @test_byte38() { ; CHECK-LABEL: define i256 @test_byte38() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.byte(i256 -1, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.byte(i256 -1, i256 -1) diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll index 99a664e89dc0..e73d09038cb9 100644 --- a/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-exp.ll @@ -8,8 +8,7 @@ declare i256 @llvm.evm.exp(i256, i256) define i256 @test_exponent1() { ; CHECK-LABEL: define i256 @test_exponent1() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 10) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.exp(i256 0, i256 10) @@ -18,8 +17,7 @@ define i256 @test_exponent1() { define i256 @test_exponent2() { ; CHECK-LABEL: define i256 @test_exponent2() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 2, i256 undef) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.exp(i256 2, i256 undef) @@ -28,8 +26,7 @@ define i256 @test_exponent2() { define i256 @test_exponent3() { ; CHECK-LABEL: define i256 @test_exponent3() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 2, i256 255) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -57896044618658097711785492504343953926634992332820282019728792003956564819968 ; %res = call i256 @llvm.evm.exp(i256 2, i256 255) @@ -38,8 +35,7 @@ define i256 @test_exponent3() { define i256 @test_exponent4() { ; CHECK-LABEL: define i256 @test_exponent4() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 307, i256 32) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -26400738010602378953627016196889292963087978848325315750873680393886838386559 ; %res = call i256 @llvm.evm.exp(i256 307, i256 32) @@ -48,8 +44,7 @@ define i256 @test_exponent4() { define i256 @test_exponent5() { ; CHECK-LABEL: define i256 @test_exponent5() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 undef, i256 2) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.exp(i256 undef, i256 2) @@ -58,8 +53,7 @@ define i256 @test_exponent5() { define i256 @test_exponent6() { ; CHECK-LABEL: define i256 @test_exponent6() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.exp(i256 0, i256 0) @@ -68,8 +62,7 @@ define i256 @test_exponent6() { define i256 @test_exponent7() { ; CHECK-LABEL: define i256 @test_exponent7() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.exp(i256 1, i256 0) @@ -78,8 +71,7 @@ define i256 @test_exponent7() { define i256 @test_exponent7.1() { ; CHECK-LABEL: define i256 @test_exponent7.1() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.exp(i256 1, i256 1) @@ -88,8 +80,7 @@ define i256 @test_exponent7.1() { define i256 @test_exponent8() { ; CHECK-LABEL: define i256 @test_exponent8() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 433478394034343) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.exp(i256 0, i256 433478394034343) @@ -98,8 +89,7 @@ define i256 @test_exponent8() { define i256 @test_exponent9() { ; CHECK-LABEL: define i256 @test_exponent9() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 121563127839120, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.exp(i256 121563127839120, i256 0) @@ -108,8 +98,7 @@ define i256 @test_exponent9() { define i256 @test_exponent10() { ; CHECK-LABEL: define i256 @test_exponent10() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 433478394034343) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.exp(i256 1, i256 433478394034343) @@ -118,8 +107,7 @@ define i256 @test_exponent10() { define i256 @test_exponent11() { ; CHECK-LABEL: define i256 @test_exponent11() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 121563127839120, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 121563127839120 ; %res = call i256 @llvm.evm.exp(i256 121563127839120, i256 1) @@ -128,8 +116,7 @@ define i256 @test_exponent11() { define i256 @test_exponent12() { ; CHECK-LABEL: define i256 @test_exponent12() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 21, i256 52) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 569381465857367090636427305760163241950353347303833610101782245331441 ; %res = call i256 @llvm.evm.exp(i256 21, i256 52) @@ -138,8 +125,7 @@ define i256 @test_exponent12() { define i256 @test_exponent13() { ; CHECK-LABEL: define i256 @test_exponent13() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 340282366920938463463374607431768211455, i256 2) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -680564733841876926926749214863536422911 ; ; 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 2 -> @@ -150,8 +136,7 @@ define i256 @test_exponent13() { define i256 @test_exponent14() { ; CHECK-LABEL: define i256 @test_exponent14() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 65536, i256 16) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; ; 0x0000000000000000000000000000000000000000000000000000000000010000 ^ 16 -> 0 @@ -161,8 +146,7 @@ define i256 @test_exponent14() { define i256 @test_exponent15() { ; CHECK-LABEL: define i256 @test_exponent15() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 21709470740815105492860156599188632070735699051917406219058709325770546741247, i256 -7627257922765187922181218227643122808087153271902696082090171450652228583425) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 46756260758475007021788099943083655901358133181480408838873172916982662561791 ; ; 0x2fff1ffffffffff5ffffff0fffffffff2ffffffafffafffcffff1ff234ffffff ^ @@ -174,8 +158,7 @@ define i256 @test_exponent15() { define i256 @test_exponent16() { ; CHECK-LABEL: define i256 @test_exponent16() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 0, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; ; 0 ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0 @@ -185,8 +168,7 @@ define i256 @test_exponent16() { define i256 @test_exponent17() { ; CHECK-LABEL: define i256 @test_exponent17() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ 0 - > 1 @@ -196,8 +178,7 @@ define i256 @test_exponent17() { define i256 @test_exponent18() { ; CHECK-LABEL: define i256 @test_exponent18() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 1, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; ; 1 ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 1 @@ -207,8 +188,7 @@ define i256 @test_exponent18() { define i256 @test_exponent19() { ; CHECK-LABEL: define i256 @test_exponent19() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ 1 - > @@ -219,8 +199,7 @@ define i256 @test_exponent19() { define i256 @test_exponent20() { ; CHECK-LABEL: define i256 @test_exponent20() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 7437834752357434334343423343443375834785783474, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; ; 7437834752357434334343423343443375834785783474 ^ @@ -231,8 +210,7 @@ define i256 @test_exponent20() { define i256 @test_exponent21() { ; CHECK-LABEL: define i256 @test_exponent21() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 23784273472384723848213821342323233223) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ @@ -244,8 +222,7 @@ define i256 @test_exponent21() { define i256 @test_exponent22() { ; CHECK-LABEL: define i256 @test_exponent22() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.exp(i256 -1, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; ; 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ^ diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll index e537ac276196..02acd728b2e9 100644 --- a/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-mulmod.ll @@ -8,8 +8,7 @@ declare i256 @llvm.evm.mulmod(i256, i256, i256) define i256 @test_mulmod1() { ; CHECK-LABEL: define i256 @test_mulmod1() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 -2, i256 -2, i256 3) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; ; Params are treated as unsigned values @@ -19,8 +18,7 @@ define i256 @test_mulmod1() { define i256 @test_mulmod2() { ; CHECK-LABEL: define i256 @test_mulmod2() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 7) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 2 ; %res = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 7) @@ -29,8 +27,7 @@ define i256 @test_mulmod2() { define i256 @test_mulmod3() { ; CHECK-LABEL: define i256 @test_mulmod3() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 undef, i256 17, i256 7) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.mulmod(i256 undef, i256 17, i256 7) @@ -39,8 +36,7 @@ define i256 @test_mulmod3() { define i256 @test_mulmod4() { ; CHECK-LABEL: define i256 @test_mulmod4() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 17, i256 undef, i256 7) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.mulmod(i256 17, i256 undef, i256 7) @@ -49,8 +45,7 @@ define i256 @test_mulmod4() { define i256 @test_mulmod5() { ; CHECK-LABEL: define i256 @test_mulmod5() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 undef) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.mulmod(i256 3, i256 17, i256 undef) diff --git a/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll b/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll index 6b0eac93a431..2e4bff24a548 100644 --- a/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll +++ b/llvm/test/CodeGen/EVM/constant-folding-earlycse-signextend.ll @@ -8,8 +8,7 @@ declare i256 @llvm.evm.signextend(i256, i256) define i256 @test_signextend1() { ; CHECK-LABEL: define i256 @test_signextend1() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 255) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 0, i256 255) @@ -18,8 +17,7 @@ define i256 @test_signextend1() { define i256 @test_signextend2() { ; CHECK-LABEL: define i256 @test_signextend2() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 32767) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 32767 ; %res = call i256 @llvm.evm.signextend(i256 1, i256 32767) @@ -28,8 +26,7 @@ define i256 @test_signextend2() { define i256 @test_signextend3() { ; CHECK-LABEL: define i256 @test_signextend3() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 undef, i256 32767) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.signextend(i256 undef, i256 32767) @@ -38,8 +35,7 @@ define i256 @test_signextend3() { define i256 @test_signextend4() { ; CHECK-LABEL: define i256 @test_signextend4() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 undef) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.signextend(i256 1, i256 undef) @@ -48,8 +44,7 @@ define i256 @test_signextend4() { define i256 @test_signextend5() { ; CHECK-LABEL: define i256 @test_signextend5() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 32, i256 undef) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 poison ; %res = call i256 @llvm.evm.signextend(i256 32, i256 undef) @@ -58,8 +53,7 @@ define i256 @test_signextend5() { define i256 @test_signextend6() { ; CHECK-LABEL: define i256 @test_signextend6() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.signextend(i256 0, i256 0) @@ -68,8 +62,7 @@ define i256 @test_signextend6() { define i256 @test_signextend7() { ; CHECK-LABEL: define i256 @test_signextend7() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.signextend(i256 0, i256 1) @@ -78,8 +71,7 @@ define i256 @test_signextend7() { define i256 @test_signextend8() { ; CHECK-LABEL: define i256 @test_signextend8() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 67 ; %res = call i256 @llvm.evm.signextend(i256 0, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -88,8 +80,7 @@ define i256 @test_signextend8() { define i256 @test_signextend9() { ; CHECK-LABEL: define i256 @test_signextend9() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 0, i256 -1) @@ -98,8 +89,7 @@ define i256 @test_signextend9() { define i256 @test_signextend10() { ; CHECK-LABEL: define i256 @test_signextend10() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.signextend(i256 1, i256 0) @@ -108,8 +98,7 @@ define i256 @test_signextend10() { define i256 @test_signextend11() { ; CHECK-LABEL: define i256 @test_signextend11() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.signextend(i256 1, i256 1) @@ -118,8 +107,7 @@ define i256 @test_signextend11() { define i256 @test_signextend12() { ; CHECK-LABEL: define i256 @test_signextend12() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533094467) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -31677 ; %res = call i256 @llvm.evm.signextend(i256 1, i256 37670211480306196047687443673641227745170897112008692523754794019498533094467) @@ -128,8 +116,7 @@ define i256 @test_signextend12() { define i256 @test_signextend13() { ; CHECK-LABEL: define i256 @test_signextend13() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 1, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 1, i256 -1) @@ -138,8 +125,7 @@ define i256 @test_signextend13() { define i256 @test_signextend14() { ; CHECK-LABEL: define i256 @test_signextend14() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 17, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.signextend(i256 17, i256 0) @@ -148,8 +134,7 @@ define i256 @test_signextend14() { define i256 @test_signextend15() { ; CHECK-LABEL: define i256 @test_signextend15() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 4, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.signextend(i256 4, i256 1) @@ -158,8 +143,7 @@ define i256 @test_signextend15() { define i256 @test_signextend16() { ; CHECK-LABEL: define i256 @test_signextend16() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 22, i256 9111590707254702215554908033119796504552448929051039916) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 9111590707254702215554908033119796504552448929051039916 ; %res = call i256 @llvm.evm.signextend(i256 22, i256 9111590707254702215554908033119796504552448929051039916) @@ -168,8 +152,7 @@ define i256 @test_signextend16() { define i256 @test_signextend17() { ; CHECK-LABEL: define i256 @test_signextend17() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 17, i256 25236770892255973364820642850062731514599596) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 2936025693725350223284924577414370008619180 ; %res = call i256 @llvm.evm.signextend(i256 17, i256 25236770892255973364820642850062731514599596) @@ -178,8 +161,7 @@ define i256 @test_signextend17() { define i256 @test_signextend18() { ; CHECK-LABEL: define i256 @test_signextend18() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 22, i256 16774068411584146507346643168871342422646144539969050796) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -7745860242270075226386909265533604515253681414968584020 ; %res = call i256 @llvm.evm.signextend(i256 22, i256 16774068411584146507346643168871342422646144539969050796) @@ -188,8 +170,7 @@ define i256 @test_signextend18() { define i256 @test_signextend19() { ; CHECK-LABEL: define i256 @test_signextend19() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 17, i256 93509753590725542020426220953279705230436762145237986916280948908) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -5426753755723633454790969774828765556123476 ; %res = call i256 @llvm.evm.signextend(i256 17, i256 93509753590725542020426220953279705230436762145237986916280948908) @@ -198,8 +179,7 @@ define i256 @test_signextend19() { define i256 @test_signextend20() { ; CHECK-LABEL: define i256 @test_signextend20() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 25, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 25, i256 -1) @@ -208,8 +188,7 @@ define i256 @test_signextend20() { define i256 @test_signextend21() { ; CHECK-LABEL: define i256 @test_signextend21() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.signextend(i256 255, i256 0) @@ -218,8 +197,7 @@ define i256 @test_signextend21() { define i256 @test_signextend22() { ; CHECK-LABEL: define i256 @test_signextend22() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.signextend(i256 255, i256 1) @@ -228,8 +206,7 @@ define i256 @test_signextend22() { define i256 @test_signextend23() { ; CHECK-LABEL: define i256 @test_signextend23() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 -49173855447680950519990795082874703144781591387221730505838393986436314155965) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -49173855447680950519990795082874703144781591387221730505838393986436314155965 ; %res = call i256 @llvm.evm.signextend(i256 255, i256 66618233789635244903580189925813204708488393278418833533619190021476815483971) @@ -238,8 +215,7 @@ define i256 @test_signextend23() { define i256 @test_signextend24() { ; CHECK-LABEL: define i256 @test_signextend24() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 255, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 255, i256 -1) @@ -248,8 +224,7 @@ define i256 @test_signextend24() { define i256 @test_signextend25() { ; CHECK-LABEL: define i256 @test_signextend25() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 256, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.signextend(i256 256, i256 0) @@ -258,8 +233,7 @@ define i256 @test_signextend25() { define i256 @test_signextend26() { ; CHECK-LABEL: define i256 @test_signextend26() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 256, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 37670211480306196047687443673641227745170897112008692523754794019498533073987 ; %res = call i256 @llvm.evm.signextend(i256 256, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -268,8 +242,7 @@ define i256 @test_signextend26() { define i256 @test_signextend27() { ; CHECK-LABEL: define i256 @test_signextend27() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 256, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 256, i256 -1) @@ -278,8 +251,7 @@ define i256 @test_signextend27() { define i256 @test_signextend28() { ; CHECK-LABEL: define i256 @test_signextend28() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 1) @@ -288,8 +260,7 @@ define i256 @test_signextend28() { define i256 @test_signextend29() { ; CHECK-LABEL: define i256 @test_signextend29() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 37670211480306196047687443673641227745170897112008692523754794019498533073987 ; %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -298,8 +269,7 @@ define i256 @test_signextend29() { define i256 @test_signextend30() { ; CHECK-LABEL: define i256 @test_signextend30() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 35242523534534534233424343343443, i256 -1) @@ -308,8 +278,7 @@ define i256 @test_signextend30() { define i256 @test_signextend31() { ; CHECK-LABEL: define i256 @test_signextend31() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 0) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 0 ; %res = call i256 @llvm.evm.signextend(i256 -1, i256 0) @@ -318,8 +287,7 @@ define i256 @test_signextend31() { define i256 @test_signextend32() { ; CHECK-LABEL: define i256 @test_signextend32() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 1 ; %res = call i256 @llvm.evm.signextend(i256 -1, i256 1) @@ -328,8 +296,7 @@ define i256 @test_signextend32() { define i256 @test_signextend33() { ; CHECK-LABEL: define i256 @test_signextend33() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 37670211480306196047687443673641227745170897112008692523754794019498533073987 ; %res = call i256 @llvm.evm.signextend(i256 -1, i256 37670211480306196047687443673641227745170897112008692523754794019498533073987) @@ -338,8 +305,7 @@ define i256 @test_signextend33() { define i256 @test_signextend34() { ; CHECK-LABEL: define i256 @test_signextend34() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 -1, i256 -1) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 -1, i256 -1) @@ -348,8 +314,7 @@ define i256 @test_signextend34() { define i256 @test_signextend35() { ; CHECK-LABEL: define i256 @test_signextend35() { -; CHECK-NEXT: [[RES:%.*]] = call i256 @llvm.evm.signextend(i256 0, i256 33023) -; CHECK-NEXT: ret i256 [[RES]] +; CHECK-NEXT: ret i256 -1 ; %res = call i256 @llvm.evm.signextend(i256 0, i256 33023) From df14b3c75f734eb09adae1e46b92abc9d07ec63c Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic <129192835+vladimirradosavljevic@users.noreply.github.com> Date: Tue, 24 Jun 2025 10:03:12 +0200 Subject: [PATCH 135/203] [EVM] Add support for spills and reloads (#828) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to add spills and reloads support, following has implemented: 1. The stackification process is now iterative, with a configurable maximum number of iterations. Spill candidates are identified and spilled in subsequent iterations until stack-too-deep errors are resolved or the iteration limit is reached. When they are resolved only then we are emitting instructions. 2. The heuristic for selecting which registers to spill and reload is based on LLVM's weight calculation. When an unreachable slot is encountered, all registers from that point to the end are considered, and the register with the lowest weight is chosen. This approach has yielded the best results in our benchmarks so far. 3. To indicate that a register needs to be spilled, an IsSpill data member has been added to RegisterSlot, along with corresponding getter and setter methods. 4. For all spills and reloads, new PUSH_FRAME instruction is introduced which accepts frame index. This is needed because we need to allocate stack size for each function after stackification is done. 5. To minimize the number of stack slots, StackSlotColoring is invoked after stackification. This pass relies on LLVM analyses—also used during register allocation—to assign stack slots with the live intervals of their corresponding registers. 6. EVMFinalizeStackFrames is introduced to calculate stack sizes for each function and to replace frame indices with concrete stack offsets. 3 new options are added to control this support: 1. -evm-max-spill-iterations=uint32_t This option controls how many iterations we want to perform during stackification. If 0, spills and reloads support is disabled. 2. -evm-stack-region-size=uint64_t This option sets the size of the allocated stack region for the whole module. In case allocated stack region is less than the actual total size, error will be produced. 3. -evm-stack-region-offset=uint64_t This options sets the offset where the stack region starts. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/CMakeLists.txt | 2 + llvm/lib/Target/EVM/EVM.h | 11 + .../EVMBackwardPropagationStackification.cpp | 21 +- .../lib/Target/EVM/EVMFinalizeStackFrames.cpp | 191 +++ llvm/lib/Target/EVM/EVMInstrInfo.td | 4 + .../lib/Target/EVM/EVMMachineFunctionInfo.cpp | 6 +- llvm/lib/Target/EVM/EVMMachineFunctionInfo.h | 4 + .../Target/EVM/EVMMarkRecursiveFunctions.cpp | 89 ++ llvm/lib/Target/EVM/EVMStackModel.h | 24 + llvm/lib/Target/EVM/EVMStackSolver.cpp | 311 ++++- llvm/lib/Target/EVM/EVMStackSolver.h | 29 +- .../lib/Target/EVM/EVMStackifyCodeEmitter.cpp | 123 +- llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h | 22 +- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 21 + .../EVM/bps-recursive-func-compress-stack.mir | 232 ++++ .../CodeGen/EVM/bps-recursive-func-fail.mir | 284 +++++ llvm/test/CodeGen/EVM/bps-spills-1.mir | 1072 +++++++++++++++++ llvm/test/CodeGen/EVM/bps-spills-2.mir | 486 ++++++++ llvm/test/CodeGen/EVM/evm-recursive.ll | 39 + .../CodeGen/EVM/finalize-stack-frames.mir | 78 ++ .../test/CodeGen/EVM/machine-function-info.ll | 38 + .../CodeGen/EVM/machine-function-info.mir | 37 + llvm/test/CodeGen/EVM/stack-too-deep-1.ll | 31 +- llvm/test/CodeGen/EVM/stack-too-deep-2.ll | 37 +- llvm/test/CodeGen/EVM/stack-too-deep-3.ll | 47 +- 25 files changed, 3088 insertions(+), 151 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp create mode 100644 llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp create mode 100644 llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir create mode 100644 llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir create mode 100644 llvm/test/CodeGen/EVM/bps-spills-1.mir create mode 100644 llvm/test/CodeGen/EVM/bps-spills-2.mir create mode 100644 llvm/test/CodeGen/EVM/evm-recursive.ll create mode 100644 llvm/test/CodeGen/EVM/finalize-stack-frames.mir create mode 100644 llvm/test/CodeGen/EVM/machine-function-info.ll create mode 100644 llvm/test/CodeGen/EVM/machine-function-info.mir diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 31cc22362125..f4c4bba98d3d 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -42,6 +42,7 @@ add_llvm_target(EVMCodeGen EVMAsmPrinter.cpp EVMBackwardPropagationStackification.cpp EVMCodegenPrepare.cpp + EVMFinalizeStackFrames.cpp EVMFrameLowering.cpp EVMISelDAGToDAG.cpp EVMISelLowering.cpp @@ -50,6 +51,7 @@ add_llvm_target(EVMCodeGen EVMLowerIntrinsics.cpp EVMLowerJumpUnless.cpp EVMMachineFunctionInfo.cpp + EVMMarkRecursiveFunctions.cpp EVMMCInstLower.cpp EVMOptimizeLiveIntervals.cpp EVMRegColoring.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index f8f129f5b97e..f0bacc052896 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -43,6 +43,7 @@ unsigned constexpr SWAP = 3; unsigned constexpr DUP = 3; unsigned constexpr POP = 2; unsigned constexpr PUSH = 3; +unsigned constexpr MLOAD = 3; } // namespace EVMCOST // LLVM IR passes. @@ -66,6 +67,8 @@ FunctionPass *createEVMSplitCriticalEdges(); FunctionPass *createEVMStackify(); FunctionPass *createEVMBPStackification(); FunctionPass *createEVMLowerJumpUnless(); +ModulePass *createEVMFinalizeStackFrames(); +ModulePass *createEVMMarkRecursiveFunctionsPass(); // PassRegistry initialization declarations. void initializeEVMCodegenPreparePass(PassRegistry &); @@ -82,6 +85,8 @@ void initializeEVMBPStackificationPass(PassRegistry &); void initializeEVMAAWrapperPassPass(PassRegistry &); void initializeEVMExternalAAWrapperPass(PassRegistry &); void initializeEVMLowerJumpUnlessPass(PassRegistry &); +void initializeEVMFinalizeStackFramesPass(PassRegistry &); +void initializeEVMMarkRecursiveFunctionsPass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { EVMLinkRuntimePass() = default; @@ -98,5 +103,11 @@ struct EVMSHA3ConstFoldingPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; +struct EVMMarkRecursiveFunctionsPass + : PassInfoMixin { + EVMMarkRecursiveFunctionsPass() = default; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + } // namespace llvm #endif // LLVM_LIB_TARGET_EVM_EVM_H diff --git a/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp b/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp index 179e9768ae14..f28586d73763 100644 --- a/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp +++ b/llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp @@ -18,6 +18,8 @@ #include "EVMStackifyCodeEmitter.h" #include "EVMSubtarget.h" #include "llvm/CodeGen/LiveIntervals.h" +#include "llvm/CodeGen/LiveStacks.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" #include "llvm/CodeGen/MachineLoopInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetInstrInfo.h" @@ -44,6 +46,13 @@ class EVMBPStackification final : public MachineFunctionPass { void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); MachineFunctionPass::getAnalysisUsage(AU); } @@ -60,7 +69,12 @@ char EVMBPStackification::ID = 0; INITIALIZE_PASS_BEGIN(EVMBPStackification, DEBUG_TYPE, "Backward propagation stackification", false, false) +INITIALIZE_PASS_DEPENDENCY(LiveIntervalsWrapperPass) INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(SlotIndexesWrapperPass) +INITIALIZE_PASS_DEPENDENCY(VirtRegMap) +INITIALIZE_PASS_DEPENDENCY(LiveStacks) +INITIALIZE_PASS_DEPENDENCY(MachineBlockFrequencyInfoWrapperPass) INITIALIZE_PASS_END(EVMBPStackification, DEBUG_TYPE, "Backward propagation stackification", false, false) @@ -77,6 +91,9 @@ bool EVMBPStackification::runOnMachineFunction(MachineFunction &MF) { MachineRegisterInfo &MRI = MF.getRegInfo(); auto &LIS = getAnalysis().getLIS(); MachineLoopInfo *MLI = &getAnalysis().getLI(); + auto &VRM = getAnalysis(); + auto &LSS = getAnalysis(); + auto &MBFI = getAnalysis().getMBFI(); // We don't preserve SSA form. MRI.leaveSSA(); @@ -84,8 +101,8 @@ bool EVMBPStackification::runOnMachineFunction(MachineFunction &MF) { assert(MRI.tracksLiveness() && "Stackification expects liveness"); EVMStackModel StackModel(MF, LIS, MF.getSubtarget().stackDepthLimit()); - EVMStackSolver(MF, StackModel, MLI).run(); - EVMStackifyCodeEmitter(StackModel, MF).run(); + EVMStackSolver(MF, StackModel, MLI, VRM, MBFI, LIS).run(); + EVMStackifyCodeEmitter(StackModel, MF, VRM, LSS, LIS).run(); auto *MFI = MF.getInfo(); MFI->setIsStackified(); diff --git a/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp new file mode 100644 index 000000000000..05eab5f45280 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp @@ -0,0 +1,191 @@ +//===----- EVMFinalizeStackFrames.cpp - Finalize stack frames --*- 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 pass calculates stack size for each function and replaces frame indices +// with their offsets. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" + +using namespace llvm; + +#define DEBUG_TYPE "evm-finalize-stack-frames" +#define PASS_NAME "EVM finalize stack frames" + +static cl::opt + StackRegionSize("evm-stack-region-size", cl::Hidden, cl::init(0), + cl::desc("Allocated stack region size")); + +static cl::opt + StackRegionOffset("evm-stack-region-offset", cl::Hidden, + cl::init(std::numeric_limits::max()), + cl::desc("Offset where the stack region starts")); + +namespace { +class EVMFinalizeStackFrames : public ModulePass { +public: + static char ID; + + EVMFinalizeStackFrames() : ModulePass(ID) { + initializeEVMFinalizeStackFramesPass(*PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override; + + StringRef getPassName() const override { return PASS_NAME; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addPreserved(); + AU.setPreservesAll(); + ModulePass::getAnalysisUsage(AU); + } + +private: + /// Calculate the stack allocation offsets for all stack objects. + uint64_t calculateFrameObjectOffsets(MachineFunction &MF) const; + + /// Replace frame indices with their corresponding offsets. + void replaceFrameIndices(MachineFunction &MF, + uint64_t StackRegionStart) const; +}; +} // end anonymous namespace + +char EVMFinalizeStackFrames::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMFinalizeStackFrames, DEBUG_TYPE, PASS_NAME, false, + false) +INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass) +INITIALIZE_PASS_END(EVMFinalizeStackFrames, DEBUG_TYPE, PASS_NAME, false, false) + +ModulePass *llvm::createEVMFinalizeStackFrames() { + return new EVMFinalizeStackFrames(); +} + +uint64_t +EVMFinalizeStackFrames::calculateFrameObjectOffsets(MachineFunction &MF) const { + // Bail out if there are no stack objects. + auto &MFI = MF.getFrameInfo(); + if (!MFI.hasStackObjects()) + return 0; + + // Set the stack offsets for each object. + uint64_t StackSize = 0; + for (int I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) { + if (MFI.isDeadObjectIndex(I)) + continue; + + MFI.setObjectOffset(I, StackSize); + StackSize += MFI.getObjectSize(I); + } + + assert(StackSize % 32 == 0 && "Stack size must be a multiple of 32 bytes"); + return StackSize; +} + +void EVMFinalizeStackFrames::replaceFrameIndices( + MachineFunction &MF, uint64_t StackRegionStart) const { + auto &MFI = MF.getFrameInfo(); + assert(MFI.hasStackObjects() && + "Cannot replace frame indices without stack objects"); + + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : make_early_inc_range(MBB)) { + if (MI.getOpcode() != EVM::PUSH_FRAME) + continue; + + assert(MI.getNumOperands() == 1 && "PUSH_FRAME must have one operand"); + MachineOperand &FIOp = MI.getOperand(0); + assert(FIOp.isFI() && "Expected a frame index operand"); + + // Replace the frame index with the corresponding stack offset. + APInt Offset(256, + StackRegionStart + MFI.getObjectOffset(FIOp.getIndex())); + unsigned PushOpc = EVM::getPUSHOpcode(Offset); + auto NewMI = BuildMI(MBB, MI, MI.getDebugLoc(), + TII->get(EVM::getStackOpcode(PushOpc))); + if (PushOpc != EVM::PUSH0) + NewMI.addCImm(ConstantInt::get(MF.getFunction().getContext(), Offset)); + + MI.eraseFromParent(); + } + } +} + +bool EVMFinalizeStackFrames::runOnModule(Module &M) { + LLVM_DEBUG({ dbgs() << "********** Finalize stack frames **********\n"; }); + + // Check if options for stack region size and offset are set correctly. + if (StackRegionSize.getNumOccurrences()) { + if (!StackRegionOffset.getNumOccurrences()) + report_fatal_error("Stack region offset must be set when stack region " + "size is set. Use --evm-stack-region-offset to set " + "the offset."); + + if (StackRegionOffset % 32 != 0) + report_fatal_error("Stack region offset must be a multiple of 32 bytes."); + } + + uint64_t TotalStackSize = 0; + MachineModuleInfo &MMI = getAnalysis().getMMI(); + SmallVector, 8> ToReplaceFI; + + // Calculate the stack size for each function. + for (Function &F : M) { + MachineFunction *MF = MMI.getMachineFunction(F); + if (!MF) + continue; + + uint64_t StackSize = calculateFrameObjectOffsets(*MF); + if (StackSize == 0) + continue; + + uint64_t StackRegionStart = StackRegionOffset + TotalStackSize; + ToReplaceFI.emplace_back(MF, StackRegionStart); + TotalStackSize += StackSize; + + LLVM_DEBUG({ + dbgs() << "Stack size for function " << MF->getName() + << " is: " << StackSize + << " and starting offset is: " << StackRegionStart << "\n"; + }); + } + LLVM_DEBUG({ dbgs() << "Total stack size: " << TotalStackSize << "\n"; }); + + // Check if it is valid to replace frame indices. + if (TotalStackSize > 0) { + if (TotalStackSize > StackRegionSize) + report_fatal_error("Total stack size: " + Twine(TotalStackSize) + + " is larger than the allocated stack region size: " + + Twine(StackRegionSize)); + + if (StackRegionSize > TotalStackSize) + errs() << "warning: allocated stack region size: " + + Twine(StackRegionSize) + + " is larger than the total stack size: " + + Twine(TotalStackSize) + "\n"; + } + + // Replace frame indices with their offsets. + for (auto &[MF, StackRegionStart] : ToReplaceFI) + replaceFrameIndices(*MF, StackRegionStart); + + return TotalStackSize > 0; +} diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 0506d09d85dc..b56f6a245551 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -815,6 +815,10 @@ def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], true, "", 0, 0> { let isCodeGenOnly = 1; } +def PUSH_FRAME : NI<(outs), (ins i256imm:$imm), [], true, "", 0, 0> { + let isCodeGenOnly = 1; +} + // Define register PUSH* instructions foreach I = {1 - 32} in { def PUSH#I : NI<(outs), (ins i256imm:$imm), [], false, "PUSH"#I#" $imm", diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp index 50db06c257f9..bae24580f174 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.cpp @@ -26,7 +26,9 @@ yaml::EVMMachineFunctionInfo::~EVMMachineFunctionInfo() = default; yaml::EVMMachineFunctionInfo::EVMMachineFunctionInfo( const llvm::EVMMachineFunctionInfo &MFI) - : IsStackified(MFI.getIsStackified()) {} + : IsStackified(MFI.getIsStackified()), + NumberOfParameters(MFI.getNumParams()), + HasPushDeployAddress(MFI.getHasPushDeployAddress()) {} void yaml::EVMMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) { MappingTraits::mapping(YamlIO, *this); @@ -35,4 +37,6 @@ void yaml::EVMMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) { void EVMMachineFunctionInfo::initializeBaseYamlFields( const yaml::EVMMachineFunctionInfo &YamlMFI) { IsStackified = YamlMFI.IsStackified; + NumberOfParameters = YamlMFI.NumberOfParameters; + HasPushDeployAddress = YamlMFI.HasPushDeployAddress; } diff --git a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h index 260f3a08ab4b..80a2e5a09a63 100644 --- a/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h +++ b/llvm/lib/Target/EVM/EVMMachineFunctionInfo.h @@ -25,6 +25,8 @@ namespace yaml { struct EVMMachineFunctionInfo final : public yaml::MachineFunctionInfo { bool IsStackified = false; + unsigned NumberOfParameters = 0; + bool HasPushDeployAddress = false; EVMMachineFunctionInfo() = default; explicit EVMMachineFunctionInfo(const llvm::EVMMachineFunctionInfo &MFI); @@ -36,6 +38,8 @@ struct EVMMachineFunctionInfo final : public yaml::MachineFunctionInfo { template <> struct MappingTraits { static void mapping(IO &YamlIO, EVMMachineFunctionInfo &MFI) { YamlIO.mapOptional("isStackified", MFI.IsStackified, false); + YamlIO.mapOptional("numberOfParameters", MFI.NumberOfParameters, 0); + YamlIO.mapOptional("hasPushDeployAddress", MFI.HasPushDeployAddress, false); } }; } // end namespace yaml diff --git a/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp b/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp new file mode 100644 index 000000000000..aa0cfebce24e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp @@ -0,0 +1,89 @@ +//===-- EVMMarkRecursiveFunctions.cpp - Mark recursive fuctions --*- 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 pass marks all recursive functions in the module with the +// "evm-recursive" attribute by analyzing the call graph. This is needed during +// stackification, since we can't use spills for recursive functions, as we are +// using memory for spills, and not the real stack. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/ADT/SCCIterator.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" + +#define DEBUG_TYPE "evm-mark-recursive-functions" + +using namespace llvm; + +namespace { + +class EVMMarkRecursiveFunctions final : public ModulePass { +public: + static char ID; // Pass ID + EVMMarkRecursiveFunctions() : ModulePass(ID) {} + + StringRef getPassName() const override { + return "EVM mark recursive functions"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override; +}; + +} // end anonymous namespace + +static bool runImpl(CallGraph &CG) { + bool Changed = false; + for (auto SCCI = scc_begin(&CG); !SCCI.isAtEnd(); ++SCCI) { + const auto &SCC = *SCCI; + // Only mark indirect recursive functions (size > 1) or self-recursive + // functions (size == 1 with a cycle). Size can't be zero, since we + // always have at least one node in the SCC. + if (SCC.size() == 1 && !SCCI.hasCycle()) + continue; + + for (const auto *Node : SCC) { + Function *F = Node->getFunction(); + if (!F || F->isDeclaration()) + continue; + F->addFnAttr("evm-recursive"); + Changed = true; + } + } + return Changed; +} + +bool EVMMarkRecursiveFunctions::runOnModule(Module &M) { + return runImpl(getAnalysis().getCallGraph()); +} + +char EVMMarkRecursiveFunctions::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMMarkRecursiveFunctions, DEBUG_TYPE, + "Mark all recursive functions", false, false) +INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_END(EVMMarkRecursiveFunctions, DEBUG_TYPE, + "Mark all recursive functions", false, false) + +ModulePass *llvm::createEVMMarkRecursiveFunctionsPass() { + return new EVMMarkRecursiveFunctions; +} + +PreservedAnalyses +EVMMarkRecursiveFunctionsPass::run(Module &M, ModuleAnalysisManager &AM) { + runImpl(AM.getResult(M)); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h index c71ddbb76e0a..42bf5877444e 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.h +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -20,6 +20,7 @@ #include "MCTargetDesc/EVMMCTargetDesc.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/LiveIntervals.h" #include "llvm/CodeGen/TargetInstrInfo.h" @@ -82,16 +83,24 @@ class LiteralSlot final : public StackSlot { /// A slot containing a register def. class RegisterSlot final : public StackSlot { Register Reg; + bool IsSpill = false; public: explicit RegisterSlot(const Register &R) : StackSlot(SK_Register), Reg(R) {} const Register &getReg() const { return Reg; } + void setIsSpill(bool Spill = true) { IsSpill = Spill; } + bool isSpill() const { return IsSpill; } + bool isRematerializable() const override { return false; } std::string toString() const override { SmallString<64> S; raw_svector_ostream OS(S); + if (IsSpill) + OS << "Spill["; OS << printReg(Reg, nullptr, 0, nullptr); + if (IsSpill) + OS << ']'; return std::string(S.str()); } static bool classof(const StackSlot *S) { @@ -209,6 +218,12 @@ bool isPushOrDupLikeMI(const MachineInstr &MI); bool isLinkerPseudoMI(const MachineInstr &MI); bool isNoReturnCallMI(const MachineInstr &MI); +/// Returns true if the \p Slot is a spill register slot. +inline bool isSpillReg(const StackSlot *Slot) { + const auto *RegSlot = dyn_cast(Slot); + return RegSlot && RegSlot->isSpill(); +} + class EVMStackModel { MachineFunction &MF; const LiveIntervals &LIS; @@ -263,6 +278,15 @@ class EVMStackModel { return Defs; } + void addSpillRegs(const SmallSet &SpillRegs) { + for (const auto &R : SpillRegs) { + auto *RegSlot = RegStorage.at(R).get(); + assert(!RegSlot->isSpill() && + "Register slot has already been marked as spill"); + RegSlot->setIsSpill(); + } + } + // Get or create a requested stack slot. StackSlot *getStackSlot(const MachineOperand &MO) const; LiteralSlot *getLiteralSlot(const APInt &V) const { diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index a7014e5837b7..6334d3a38404 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -14,6 +14,7 @@ #include "EVMRegisterInfo.h" #include "EVMStackShuffler.h" #include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/CodeGen/CalcSpillWeights.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineLoopInfo.h" #include "llvm/Support/Debug.h" @@ -26,6 +27,11 @@ using namespace llvm; #define DEBUG_TYPE "evm-stack-solver" +static cl::opt MaxSpillIterations( + "evm-max-spill-iterations", cl::Hidden, cl::init(100), + cl::desc("Maximum number of iterations to spill stack slots " + "to avoid stack too deep issues.")); + namespace { /// \return index of \p Item in \p RangeOrContainer or std::nullopt. @@ -65,7 +71,7 @@ Stack calculateStackBeforeInst(const Stack &InstDefs, const Stack &AfterInst, // Number of slots on stack before the instruction excluding its inputs. size_t BeforeInstSize = count_if(AfterInst, [&](const StackSlot *S) { return !is_contained(InstDefs, S) && - !(CompressStack && S->isRematerializable()); + !(CompressStack && (S->isRematerializable() || isSpillReg(S))); }); // To use StackTransformer, the computed stack must be transformable to the @@ -90,7 +96,7 @@ Stack calculateStackBeforeInst(const Stack &InstDefs, const Stack &AfterInst, auto canSkipSlot = [&InstDefs, CompressStack](const StackSlot *Slot) { return count(InstDefs, Slot) || - (CompressStack && Slot->isRematerializable()); + (CompressStack && (Slot->isRematerializable() || isSpillReg(Slot))); }; auto countOccurences = [&canSkipSlot](const StackSlot *Slot, Stack &C, const Stack &T) { @@ -139,6 +145,51 @@ Stack calculateStackBeforeInst(const Stack &InstDefs, const Stack &AfterInst, return BeforeInst; } +/// From a vector of spillable registers, find the cheapest one to spill based +/// on the weights. +Register getRegToSpill(const SmallSetVector &SpillableRegs, + const LiveIntervals &LIS) { + assert(!SpillableRegs.empty() && "SpillableRegs should not be empty"); + + const auto *BestInterval = &LIS.getInterval(SpillableRegs[0]); + for (auto Reg : drop_begin(SpillableRegs)) { + const auto *LI = &LIS.getInterval(Reg); + + // Take this interval only if it has a non-zero weight and + // either BestInterval has zero weight or this interval has a lower + // weight than the current best. + if (LI->weight() != 0.0F && (BestInterval->weight() == 0.0F || + LI->weight() < BestInterval->weight())) + BestInterval = LI; + } + + LLVM_DEBUG({ + for (Register Reg : SpillableRegs) { + dbgs() << "Spill candidate: "; + LIS.getInterval(Reg).dump(); + } + dbgs() << " Best spill candidate: "; + BestInterval->dump(); + dbgs() << '\n'; + }); + return BestInterval->reg(); +} + +/// EVM-specific implementation of weight normalization. +class EVMVirtRegAuxInfo final : public VirtRegAuxInfo { + float normalize(float UseDefFreq, unsigned Size, unsigned NumInstr) override { + // All intervals have a spill weight that is mostly proportional to the + // number of uses, with uses in loops having a bigger weight. + return NumInstr * VirtRegAuxInfo::normalize(UseDefFreq, Size, 1); + } + +public: + EVMVirtRegAuxInfo(MachineFunction &MF, LiveIntervals &LIS, + const VirtRegMap &VRM, const MachineLoopInfo &Loops, + const MachineBlockFrequencyInfo &MBFI) + : VirtRegAuxInfo(MF, LIS, VRM, Loops, MBFI) {} +}; + } // end anonymous namespace BranchInfoTy llvm::getBranchInfo(const MachineBasicBlock *MBB) { @@ -186,9 +237,16 @@ llvm::calculateStackTransformCost(Stack Source, const Stack &Target, auto Rematerialize = [&Gas, &HasError, &Source, StackDepthLimit](const StackSlot *Slot) { - if (Slot->isRematerializable()) + if (isSpillReg(Slot)) { + // Prefer dup for spills and reloads, since it is cheaper. + std::optional Depth = offset(reverse(Source), Slot); + if (Depth && *Depth < StackDepthLimit) + Gas += EVMCOST::DUP; + else + Gas += EVMCOST::PUSH + EVMCOST::MLOAD; + } else if (Slot->isRematerializable()) { Gas += EVMCOST::PUSH; - else { + } else { std::optional Depth = offset(reverse(Source), Slot); if (Depth && *Depth >= StackDepthLimit) HasError = true; @@ -201,54 +259,185 @@ llvm::calculateStackTransformCost(Stack Source, const Stack &Target, return HasError ? std::nullopt : std::optional(Gas); } -EVMStackSolver::EVMStackSolver(const MachineFunction &MF, - EVMStackModel &StackModel, - const MachineLoopInfo *MLI) - : MF(MF), StackModel(StackModel), MLI(MLI) {} +EVMStackSolver::EVMStackSolver(MachineFunction &MF, EVMStackModel &StackModel, + const MachineLoopInfo *MLI, + const VirtRegMap &VRM, + const MachineBlockFrequencyInfo &MBFI, + LiveIntervals &LIS) + : MF(MF), StackModel(StackModel), MLI(MLI), VRM(VRM), MBFI(MBFI), LIS(LIS) { +} -void EVMStackSolver::run() { - runPropagation(); - LLVM_DEBUG({ - dbgs() << "************* Stack *************\n"; - dump(dbgs()); - }); - checkPropagationErrors(); +void EVMStackSolver::calculateSpillWeights() { + if (IsSpillWeightsCalculated) + return; + + EVMVirtRegAuxInfo EVRAI(MF, LIS, VRM, *MLI, MBFI); + EVRAI.calculateSpillWeightsAndHints(); + IsSpillWeightsCalculated = true; } -void EVMStackSolver::checkPropagationErrors() { - for (const MachineBasicBlock &MBB : MF) { - const Stack &EntryMBB = StackModel.getMBBEntryStack(&MBB); - const Stack &ExitMBB = StackModel.getMBBExitStack(&MBB); - const auto InstRange = StackModel.instructionsToProcess(&MBB); - - auto checkStackTransformation = [this](const Stack &From, const Stack &To) { - if (!calculateStackTransformCost(From, To, StackModel.stackDepthLimit())) - report_fatal_error(Twine("EVMStackSolver cannot transform ") + - From.toString() + " to " + To.toString() + - ": stack too deep.\n"); - }; - - // Check transformation from the MBB's entry to exit stack if BB is empty. - if (InstRange.empty()) { - checkStackTransformation(EntryMBB, ExitMBB); +EVMStackSolver::UnreachableSlotVec EVMStackSolver::getUnreachableSlots() const { + UnreachableSlotVec UnreachableSlots; + const unsigned StackDepthLimit = StackModel.stackDepthLimit(); + + // Check if the stack transformation is valid when transforming from + // \p From to \p To. If the transformation is not valid, collect the + // unreachable slots. \p From will be changed in place, but that is + // fine since we are not using it after. + auto checkStackTransformation = + [&UnreachableSlots, StackDepthLimit](Stack &From, const Stack &To) { + calculateStack( + From, To, StackDepthLimit, + // Swap. + [&](unsigned I) { + assert(I > 0 && I < From.size()); + if (I > StackDepthLimit) + UnreachableSlots.push_back({From, From.size() - I - 1}); + }, + // Push or dup. + [&](const StackSlot *Slot) { + if (Slot->isRematerializable() || isSpillReg(Slot)) + return; + + auto SlotIt = llvm::find(llvm::reverse(From), Slot); + assert(SlotIt != From.rend() && + "Slot for duplication not found on the stack."); + unsigned Depth = std::distance(From.rbegin(), SlotIt); + if (Depth >= StackDepthLimit) + UnreachableSlots.push_back({From, From.size() - Depth - 1}); + }, + // Pop. + [&]() {}); + }; + + for (const auto &MBB : MF) { + Stack CurrentStack = StackModel.getMBBEntryStack(&MBB); + + // Check the stack transformation for each instruction. + for (const auto &MI : StackModel.instructionsToProcess(&MBB)) { + checkStackTransformation(CurrentStack, StackModel.getInstEntryStack(&MI)); + CurrentStack = getMIExitStack(&MI); + } + + auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(&MBB); + + // Check the transformation for the exit stack. This mimics logic from + // the EVMStackifyCodeEmitter::run() method, as in some cases this is + // not needed. + switch (BranchTy) { + case EVMInstrInfo::BT_Uncond: + case EVMInstrInfo::BT_NoBranch: + if (!MBB.succ_empty()) + // For loop latches, exit stack is not the same as the entry stack + // of the successor block, so we need to check the transformation + // from the current stack to the entry stack of the successor block. + checkStackTransformation(CurrentStack, + StackModel.getMBBEntryStack(TBB)); + break; + case EVMInstrInfo::BT_None: + if (!MBB.isReturnBlock()) + break; + LLVM_FALLTHROUGH; + case EVMInstrInfo::BT_Cond: + case EVMInstrInfo::BT_CondUncond: + checkStackTransformation(CurrentStack, StackModel.getMBBExitStack(&MBB)); + break; + } + } + return UnreachableSlots; +} + +void EVMStackSolver::run() { + unsigned IterCount = 0; + while (true) { + runPropagation(); + LLVM_DEBUG({ + dbgs() << "************* Stack *************\n"; + dump(dbgs()); + }); + + auto UnreachableSlots = getUnreachableSlots(); + if (UnreachableSlots.empty()) + break; + + if (MF.getFunction().hasFnAttribute("evm-recursive")) { + // For recursive functions we can't use spills to fix the stack too deep + // errors, as we are using memory to spill and not real stack. Report an + // error if this function is recursive and we forced compress stack + // before. + if (ForceCompressStack) + report_fatal_error( + "Stackification failed for '" + MF.getName() + + "' function. It is recursive and has stack too deep errors. " + "Consider refactoring it to use a non-recursive approach."); + + LLVM_DEBUG({ + dbgs() + << "EVMStackSolver: force CompressStack for recursive function.\n"; + }); + + // propagateThroughMBB can't detect stack-too-deep errors for + // transformations from MBB's entry stack to the first instruction entry + // stack. Since a recursive function doesn't support spills and reloads, + // try to compress the stack. + // TODO: This is a little bit agressive, since we can detect MBBs where + // the stack is too deep and compress only them. + ForceCompressStack = true; continue; } - // Check transformation from the MBB's entry to the entry of the first MI - // on the first iteration and then do the same from the exit stack of - // the previous MI to the entry stack of the next MI. - Stack ExitPrev = EntryMBB; - for (const auto &MI : InstRange) { - checkStackTransformation(ExitPrev, StackModel.getInstEntryStack(&MI)); - ExitPrev = getMIExitStack(&MI); + if (++IterCount > MaxSpillIterations) + report_fatal_error("EVMStackSolver: maximum number of spill iterations " + "exceeded."); + + LLVM_DEBUG({ + dbgs() << "Unreachable slots found: " << UnreachableSlots.size() + << ", iteration: " << IterCount << '\n'; + }); + + // We are about to spill registers, so we need to calculate spill + // weights to determine which register to spill. + calculateSpillWeights(); + + SmallSet RegsToSpill; + for (auto &[StackSlots, Idx] : UnreachableSlots) { + LLVM_DEBUG({ + dbgs() << "Unreachable slot: " << StackSlots[Idx]->toString() + << " at index: " << Idx << '\n'; + dbgs() << "Stack with unreachable slot: " << StackSlots.toString() + << '\n'; + }); + + // Collect spillable registers from the stack slots starting from + // the given index. + SmallSetVector SpillableRegs; + for (unsigned I = Idx, E = StackSlots.size(); I < E; ++I) + if (const auto *RegSlot = dyn_cast(StackSlots[I])) + if (!RegSlot->isSpill()) + SpillableRegs.insert(RegSlot->getReg()); + + if (!SpillableRegs.empty()) + RegsToSpill.insert(getRegToSpill(SpillableRegs, LIS)); } - // Check transformation from the exit stack of the last MI to - // the MBB's exit stack. - checkStackTransformation(ExitPrev, ExitMBB); + LLVM_DEBUG( + { dbgs() << "Spilling " << RegsToSpill.size() << " registers\n"; }); + if (RegsToSpill.empty()) + report_fatal_error("EVMStackSolver: no spillable registers found for " + "unreachable slots."); + StackModel.addSpillRegs(RegsToSpill); } } +// Return true if the register is defined or used before MI. +static bool isRegDefOrUsedBefore(const MachineInstr &MI, const Register &Reg) { + return std::any_of(std::next(MachineBasicBlock::const_reverse_iterator(MI)), + MI.getParent()->rend(), [&Reg](const MachineInstr &Instr) { + return Instr.readsRegister(Reg, /*TRI*/ nullptr) || + Instr.definesRegister(Reg, /*TRI=*/nullptr); + }); +} + Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, const MachineInstr &MI, bool CompressStack) { @@ -277,10 +466,16 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, // If the top stack slots can be rematerialized just before MI, remove it // from propagation to reduce pressure. while (!BeforeMI.empty()) { - if (BeforeMI.back()->isRematerializable()) { + const auto *BackSlot = BeforeMI.back(); + // Don't remove register spills if the register is used or defined before + // MI. They are not cheap and ideally we shouldn't do more than one reload + // in the BB. + if (BackSlot->isRematerializable() || + (isSpillReg(BackSlot) && + !isRegDefOrUsedBefore(MI, cast(BackSlot)->getReg()))) { BeforeMI.pop_back(); } else if (auto Offset = - offset(drop_begin(reverse(BeforeMI), 1), BeforeMI.back())) { + offset(drop_begin(reverse(BeforeMI), 1), BackSlot)) { if (*Offset + 2 < StackModel.stackDepthLimit()) BeforeMI.pop_back(); else @@ -292,7 +487,7 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, return BeforeMI; } -Stack EVMStackSolver::getMIExitStack(const MachineInstr *MI) { +Stack EVMStackSolver::getMIExitStack(const MachineInstr *MI) const { const Stack &MIInput = StackModel.getMIInput(*MI); Stack MIEntry = StackModel.getInstEntryStack(MI); assert(MIInput.size() <= MIEntry.size()); @@ -338,6 +533,11 @@ Stack EVMStackSolver::propagateThroughMBB(const Stack &ExitStack, } void EVMStackSolver::runPropagation() { + // Reset StackModel's internal state. + StackModel.getMBBEntryMap().clear(); + StackModel.getMBBExitMap().clear(); + StackModel.getInstEntryMap().clear(); + std::deque Worklist{&MF.front()}; DenseSet Visited; @@ -433,7 +633,8 @@ void EVMStackSolver::runPropagation() { if (ExitStack) { Visited.insert(MBB); insertMBBExitStack(MBB, *ExitStack); - insertMBBEntryStack(MBB, propagateThroughMBB(*ExitStack, MBB)); + insertMBBEntryStack( + MBB, propagateThroughMBB(*ExitStack, MBB, ForceCompressStack)); append_range(Worklist, MBB->predecessors()); } } @@ -499,7 +700,7 @@ void EVMStackSolver::runPropagation() { #ifndef NDEBUG for (const StackSlot *Slot : SuccEntryStack) - assert(Slot->isRematerializable() || + assert(Slot->isRematerializable() || isSpillReg(Slot) || is_contained(NewSuccEntryStack, Slot)); #endif // NDEBUG @@ -559,7 +760,8 @@ Stack EVMStackSolver::combineStack(const Stack &Stack1, const Stack &Stack2) { Stack Candidate; for (const auto *Slot : concat(Stack1Tail, Stack2Tail)) - if (!is_contained(Candidate, Slot) && !Slot->isRematerializable()) + if (!is_contained(Candidate, Slot) && !Slot->isRematerializable() && + !isSpillReg(Slot)) Candidate.push_back(Slot); auto evaluate = [&CommonPrefix, &Stack1, &Stack2, @@ -612,7 +814,7 @@ Stack EVMStackSolver::compressStack(Stack CurStack) { auto ShouldRemove = [&CurStack, this](SmallVector::reverse_iterator I) { size_t RIdx = std::distance(CurStack.rbegin(), I); - if ((*I)->isRematerializable()) + if ((*I)->isRematerializable() || isSpillReg(*I)) return true; if (auto DistanceToCopy = offset(make_range(std::next(I), CurStack.rend()), *I)) @@ -643,16 +845,6 @@ void EVMStackSolver::dump(raw_ostream &OS) { dumpMBB(OS, &MBB); } -static std::string getMIName(const MachineInstr *MI) { - if (MI->getOpcode() == EVM::FCALL) { - const MachineOperand *Callee = MI->explicit_uses().begin(); - return Callee->getGlobal()->getName().str(); - } - const MachineFunction *MF = MI->getParent()->getParent(); - const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); - return TII->getName(MI->getOpcode()).str(); -} - void EVMStackSolver::dumpMBB(raw_ostream &OS, const MachineBasicBlock *MBB) { OS << getMBBId(MBB) << ":\n"; OS << '\t' << StackModel.getMBBEntryStack(MBB).toString() << '\n'; @@ -665,7 +857,8 @@ void EVMStackSolver::dumpMBB(raw_ostream &OS, const MachineBasicBlock *MBB) { OS << '\n'; Stack MIEntry = StackModel.getInstEntryStack(&MI); OS << '\t' << MIEntry.toString() << '\n'; - OS << '\t' << getMIName(&MI) << '\n'; + OS << '\t'; + MI.print(OS); assert(MIInput.size() <= MIEntry.size()); MIEntry.resize(MIEntry.size() - MIInput.size()); MIEntry.append(MIOutput); diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h index 655b5d5532ff..5bd35c232192 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.h +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -35,8 +35,9 @@ BranchInfoTy getBranchInfo(const MachineBasicBlock *MBB); class EVMStackSolver { public: - EVMStackSolver(const MachineFunction &MF, EVMStackModel &StackModel, - const MachineLoopInfo *MLI); + EVMStackSolver(MachineFunction &MF, EVMStackModel &StackModel, + const MachineLoopInfo *MLI, const VirtRegMap &VRM, + const MachineBlockFrequencyInfo &MBFI, LiveIntervals &LIS); void run(); private: @@ -55,10 +56,7 @@ class EVMStackSolver { bool CompressStack = false); // Build the exit stack of the given \p MI. - Stack getMIExitStack(const MachineInstr *MI); - - // Check and report for stack-too-deep errors. - void checkPropagationErrors(); + Stack getMIExitStack(const MachineInstr *MI) const; /// Main algorithm walking the graph from entry to exit and propagating stack /// states back to the entries. Iteratively reruns itself along backward jumps @@ -101,9 +99,26 @@ class EVMStackSolver { StackModel.getInstEntryMap()[MI] = std::move(S); } - const MachineFunction &MF; + using UnreachableSlotVec = SmallVector>; + /// Get all unreachable slots in case of stack too deep issues. + UnreachableSlotVec getUnreachableSlots() const; + + /// Calculate spill weights. This is needed to determine which register to + /// spill when we have multiple spillable registers. + void calculateSpillWeights(); + + MachineFunction &MF; EVMStackModel &StackModel; const MachineLoopInfo *MLI; + const VirtRegMap &VRM; + const MachineBlockFrequencyInfo &MBFI; + LiveIntervals &LIS; + bool IsSpillWeightsCalculated = false; + /// In case of recursive functions, we can't use spills to fix stack too deep + /// issues, as we are using memory to spill and not real stack. If we run into + /// stack too deep issues for recursive functions, we will force compress + /// stack across the whole function to try to fix the issues. + bool ForceCompressStack = false; }; } // end namespace llvm diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp index 06f180ea1da2..89c5cb0a5245 100644 --- a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -14,6 +14,7 @@ #include "EVMStackShuffler.h" #include "EVMStackSolver.h" #include "TargetInfo/EVMTargetInfo.h" +#include "llvm/CodeGen/LiveStacks.h" #include "llvm/MC/MCContext.h" using namespace llvm; @@ -75,6 +76,8 @@ void EVMStackifyCodeEmitter::CodeEmitter::emitInst(const MachineInstr *MI) { } void EVMStackifyCodeEmitter::CodeEmitter::emitSWAP(unsigned Depth) { + assert(StackHeight >= (Depth + 1) && + "Not enough operands on the stack for SWAP"); unsigned Opc = EVM::getSWAPOpcode(Depth); auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::getStackOpcode(Opc))); @@ -82,6 +85,7 @@ void EVMStackifyCodeEmitter::CodeEmitter::emitSWAP(unsigned Depth) { } void EVMStackifyCodeEmitter::CodeEmitter::emitDUP(unsigned Depth) { + assert(StackHeight >= Depth && "Not enough operands on the stack for DUP"); StackHeight += 1; unsigned Opc = EVM::getDUPOpcode(Depth); auto NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), @@ -192,6 +196,66 @@ void EVMStackifyCodeEmitter::CodeEmitter::emitCondJump( verify(NewMI); } +void EVMStackifyCodeEmitter::CodeEmitter::emitReload(Register Reg) { + StackHeight += 1; + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::PUSH_FRAME)) + .addFrameIndex(getStackSlot(Reg)); + verify(NewMI); + NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::MLOAD_S)); + NewMI->setAsmPrinterFlag(MachineInstr::ReloadReuse); + verify(NewMI); +} + +void EVMStackifyCodeEmitter::CodeEmitter::emitSpill(Register Reg, + unsigned DupIdx) { + if (DupIdx == 0) { + assert(StackHeight > 0 && "Expected at least one operand on the stack"); + + // Reduce height if we are not going to duplicate the register. + // In this case, register will be used by the MSTORE instruction + // that is used for spilling. + StackHeight -= 1; + } else { + assert(StackHeight >= DupIdx && + "Not enough operands on the stack for DUP while spilling"); + + // Register is used after spill, so we need to duplicate it. + emitDUP(DupIdx); + + // Since we are going to spill the register, stack height doesn't + // change, so we need to reduce it by 1, as emitDUP increases + // the stack height by that amount. + StackHeight -= 1; + } + + auto NewMI = + BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::PUSH_FRAME)) + .addFrameIndex(getStackSlot(Reg)); + verify(NewMI); + NewMI = BuildMI(*CurMBB, CurMBB->end(), DebugLoc(), TII->get(EVM::MSTORE_S)); + NewMI->setAsmPrinterFlag(MachineInstr::ReloadReuse); + verify(NewMI); +} + +int EVMStackifyCodeEmitter::CodeEmitter::getStackSlot(Register Reg) { + int StackSlot = VRM.getStackSlot(Reg); + if (StackSlot != VirtRegMap::NO_STACK_SLOT) + return StackSlot; + + // Generate a new stack slot for the register and add register's live interval + // to the stack slot. This is the same thing what InlineSpiller does, and this + // is needed to StackSlotColoring afterwards to reduce the stack area. + StackSlot = VRM.assignVirt2StackSlot(Reg); + auto &StackInt = + LSS.getOrCreateInterval(StackSlot, MF.getRegInfo().getRegClass(Reg)); + StackInt.getNextValue(SlotIndex(), LSS.getVNInfoAllocator()); + assert(StackInt.getNumValNums() == 1 && "Bad stack interval values"); + StackInt.MergeSegmentsInAsValue(LIS.getInterval(Reg), + StackInt.getValNumInfo(0)); + return StackSlot; +} + // Verify that a stackified instruction doesn't have registers and dump it. void EVMStackifyCodeEmitter::CodeEmitter::verify(const MachineInstr *MI) const { assert(EVMInstrInfo::isStack(MI) && @@ -263,6 +327,51 @@ void EVMStackifyCodeEmitter::emitMI(const MachineInstr &MI) { CurrentStack.end() - MI.getNumExplicitDefs()); } +void EVMStackifyCodeEmitter::emitSpills(const MachineBasicBlock &MBB, + MachineBasicBlock::const_iterator Start, + const Stack &Defs) { + // Check if we have any spillable registers. + if (find_if(Defs, [](const StackSlot *Slot) { return isSpillReg(Slot); }) == + Defs.end()) + return; + + // In case of a single definition, we can remove it from the stack + // if it is not used after spill. + if (Defs.size() == 1) { + // Find the first instruction from which we can get entry stack. + while (Start != MBB.end() && StackModel.skipMI(*Start)) + ++Start; + + // Find the next target stack, as we need to check if the register + // is used after spill. + const Stack &NextTargetStack = + Start != MBB.end() && !EVMInstrInfo::isStack(&*Start) + ? StackModel.getInstEntryStack(&*Start) + : StackModel.getMBBExitStack(&MBB); + const auto *RegSlot = cast(Defs[0]); + + // Find if if the register is used after spill. If it is we need to + // emit DUP instruction to keep it on the stack. + bool UsedAfter = is_contained(NextTargetStack, RegSlot); + Emitter.emitSpill(RegSlot->getReg(), UsedAfter); + + // Remove the register from the current stack, if it is not used + // after spill. + if (!UsedAfter) + CurrentStack.pop_back(); + } else { + // TODO: In case definition are not used after spill, we can + // remove them from the current stack, and not emit DUP. This + // is more complex when we have multiple definitions, as we + // need to do stack manipulation to keep the stack in sync + // with the target stack. + for (auto [DefIdx, Def] : enumerate(reverse(Defs))) + if (isSpillReg(Def)) + Emitter.emitSpill(cast(Def)->getReg(), DefIdx + 1); + } + assert(Emitter.stackHeight() == CurrentStack.size()); +} + // Checks if it's valid to transition from \p SourceStack to \p TargetStack, // that is \p SourceStack matches each slot in \p TargetStack that is not a // UnusedSlot exactly. @@ -305,7 +414,7 @@ void EVMStackifyCodeEmitter::emitStackPermutations(const Stack &TargetStack) { Emitter.emitDUP(static_cast(Depth + 1)); return; } - if (!Slot->isRematerializable()) { + if (!Slot->isRematerializable() && !isSpillReg(Slot)) { std::string ErrMsg = getUnreachableStackSlotError( MF, CurrentStack, Slot, Depth + 1, /* isSwap */ false); report_fatal_error(ErrMsg.c_str()); @@ -313,13 +422,15 @@ void EVMStackifyCodeEmitter::emitStackPermutations(const Stack &TargetStack) { } // Rematerialize the slot. - assert(Slot->isRematerializable()); + assert(Slot->isRematerializable() || isSpillReg(Slot)); if (const auto *L = dyn_cast(Slot)) { Emitter.emitConstant(L->getValue()); } else if (const auto *S = dyn_cast(Slot)) { Emitter.emitSymbol(S->getMachineInstr(), S->getSymbol()); } else if (const auto *CallRet = dyn_cast(Slot)) { Emitter.emitLabelReference(CallRet->getCall()); + } else if (const auto *Spill = dyn_cast(Slot)) { + Emitter.emitReload(Spill->getReg()); } else { assert(isa(Slot)); // Note: this will always be popped, so we can push anything. @@ -395,6 +506,10 @@ void EVMStackifyCodeEmitter::run() { bool HasReturn = MBB->isReturnBlock(); const MachineInstr *ReturnMI = HasReturn ? &MBB->back() : nullptr; + // Emit the spills for the arguments in the entry block, if needed. + if (MBB == &MF.front()) + emitSpills(*MBB, MBB->begin(), StackModel.getMBBEntryStack(MBB)); + for (const auto &MI : StackModel.instructionsToProcess(MBB)) { // We are done if the MI is in the stack form. if (EVMInstrInfo::isStack(&MI)) @@ -419,6 +534,10 @@ void EVMStackifyCodeEmitter::run() { assert(MI.definesRegister(cast(CurrentStack[I])->getReg(), /*TRI=*/nullptr)); #endif // NDEBUG + + // Emit spills for the instruction definitions, if needed. + emitSpills(*MBB, std::next(MI.getIterator()), + StackModel.getSlotsForInstructionDefs(&MI)); } // Exit the block. diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h index 7177ea706650..532e0e5b99e1 100644 --- a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.h @@ -20,11 +20,13 @@ namespace llvm { class MachineInstr; class MCSymbol; +class LiveStacks; class EVMStackifyCodeEmitter { public: - EVMStackifyCodeEmitter(const EVMStackModel &StackModel, MachineFunction &MF) - : Emitter(MF), StackModel(StackModel), MF(MF) {} + EVMStackifyCodeEmitter(const EVMStackModel &StackModel, MachineFunction &MF, + VirtRegMap &VRM, LiveStacks &LSS, LiveIntervals &LIS) + : Emitter(MF, VRM, LSS, LIS), StackModel(StackModel), MF(MF) {} /// Stackify instructions, starting from the first MF's MBB. void run(); @@ -32,8 +34,10 @@ class EVMStackifyCodeEmitter { private: class CodeEmitter { public: - explicit CodeEmitter(MachineFunction &MF) - : MF(MF), TII(MF.getSubtarget().getInstrInfo()) {} + explicit CodeEmitter(MachineFunction &MF, VirtRegMap &VRM, LiveStacks &LSS, + const LiveIntervals &LIS) + : MF(MF), VRM(VRM), LSS(LSS), LIS(LIS), + TII(MF.getSubtarget().getInstrInfo()) {} size_t stackHeight() const; void enterMBB(MachineBasicBlock *MBB, int Height); void emitInst(const MachineInstr *MI); @@ -48,17 +52,23 @@ class EVMStackifyCodeEmitter { void emitCondJump(const MachineInstr *MI, MachineBasicBlock *Target); void emitUncondJump(const MachineInstr *MI, MachineBasicBlock *Target); void emitLabelReference(const MachineInstr *Call); + void emitReload(Register Reg); + void emitSpill(Register Reg, unsigned DupIdx); /// Remove all the instructions that are not in stack form. void finalize(); private: MachineFunction &MF; + VirtRegMap &VRM; + LiveStacks &LSS; + const LiveIntervals &LIS; const EVMInstrInfo *TII; size_t StackHeight = 0; MachineBasicBlock *CurMBB = nullptr; DenseMap CallReturnSyms; void verify(const MachineInstr *MI) const; + int getStackSlot(Register Reg); }; CodeEmitter Emitter; @@ -78,6 +88,10 @@ class EVMStackifyCodeEmitter { /// Generate code for the instruction. void emitMI(const MachineInstr &MI); + + /// Emit spill instructions for the \p Defs, if needed. + void emitSpills(const MachineBasicBlock &MBB, + MachineBasicBlock::const_iterator Start, const Stack &Defs); }; } // namespace llvm diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 98d913147a75..5fd67d0a1c78 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -61,6 +61,8 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMAAWrapperPassPass(PR); initializeEVMExternalAAWrapperPass(PR); initializeEVMLowerJumpUnlessPass(PR); + initializeEVMFinalizeStackFramesPass(PR); + initializeEVMMarkRecursiveFunctionsPass(PR); } static std::string computeDataLayout() { @@ -140,6 +142,16 @@ void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { FAM.registerPass([] { return EVMAA(); }); }); + PB.registerPipelineParsingCallback( + [](StringRef PassName, ModulePassManager &PM, + ArrayRef) { + if (PassName == "evm-mark-recursive-functions") { + PM.addPass(EVMMarkRecursiveFunctionsPass()); + return true; + } + return false; + }); + PB.registerPipelineParsingCallback( [](StringRef PassName, FunctionPassManager &PM, ArrayRef) { @@ -182,6 +194,7 @@ class EVMPassConfig final : public TargetPassConfig { // No reg alloc bool addRegAssignAndRewriteOptimized() override { return false; } + bool addPreISel() override; void addCodeGenPrepare() override; void addIRPasses() override; bool addGCPasses() override { return false; } @@ -205,6 +218,12 @@ void EVMPassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); } +bool EVMPassConfig::addPreISel() { + TargetPassConfig::addPreISel(); + addPass(createEVMMarkRecursiveFunctionsPass()); + return false; +} + void EVMPassConfig::addCodeGenPrepare() { addPass(createEVMCodegenPreparePass()); TargetPassConfig::addCodeGenPrepare(); @@ -257,6 +276,8 @@ void EVMPassConfig::addPreEmitPass() { addPass(createEVMStackify()); } else { addPass(createEVMBPStackification()); + addPass(&StackSlotColoringID); + addPass(createEVMFinalizeStackFrames()); } // Optimize branch instructions after stackification. This is done again diff --git a/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir b/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir new file mode 100644 index 000000000000..92522b386693 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir @@ -0,0 +1,232 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +# This test case is reduced with llvm-reduce. +# Before the change, the test caused stack too deep failure during +# stackification. Since we can't use spills for recursive functions, +# only option we have is to agressively compress the stack across the +# whole function, to resolve stack too deep issues. + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define fastcc void @_testRemoveAndInsertBack_rt_707(i256 %0, i256 %1, i256 %storage_load_value12845, ptr addrspace(1) %2, i256 %storage_load_value18549, i256 %shift_right_non_overflow_result6002, i256 %and_result11667, ptr addrspace(5) %storage_load_position_pointer14560, ptr addrspace(5) %storage_load_position_pointer19042, i256 %storage_load_value19083, ptr addrspace(5) %storage_load_position_pointer14550, ptr addrspace(5) %storage_load_position_pointer18694, ptr addrspace(1) %3, i256 %memory_load_result22114.pre) #0 { + entry: + store i256 0, ptr addrspace(1) %2, align 16 + store i256 0, ptr addrspace(5) null, align 1 + store i256 %1, ptr addrspace(5) %storage_load_position_pointer14560, align 1 + store i256 %0, ptr addrspace(5) null, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer19042, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer14550, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer18694, align 1 + store i256 0, ptr addrspace(5) %storage_load_position_pointer14560, align 1 + store i256 0, ptr addrspace(1) %3, align 32 + store i256 %storage_load_value12845, ptr addrspace(5) null, align 4294967296 + call fastcc void @_testRemoveAndInsertBack_rt_707(i256 0, i256 0, i256 0, ptr addrspace(1) null, i256 0, i256 0, i256 0, ptr addrspace(5) null, ptr addrspace(5) null, i256 0, ptr addrspace(5) null, ptr addrspace(5) null, ptr addrspace(1) null, i256 0) + ret void + } + + attributes #0 = { "evm-recursive" } + +... +--- +name: _testRemoveAndInsertBack_rt_707 +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: true + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 14 + hasPushDeployAddress: false +body: | + bb.0.entry: + liveins: $arguments, $value_stack + + ; CHECK-LABEL: name: _testRemoveAndInsertBack_rt_707 + ; CHECK: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SSTORE_S + ; CHECK-NEXT: PUSH_LABEL + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PseudoCALL @_testRemoveAndInsertBack_rt_707, + ; CHECK-NEXT: PseudoRET + %3:gpr = ARGUMENT 0, implicit $arguments + %1:gpr = ARGUMENT 1, implicit $arguments + %9:gpr = ARGUMENT 2, implicit $arguments + %7:gpr = ARGUMENT 3, implicit $arguments + %2:gpr = ARGUMENT 7, implicit $arguments + %4:gpr = ARGUMENT 8, implicit $arguments + %5:gpr = ARGUMENT 10, implicit $arguments + %6:gpr = ARGUMENT 11, implicit $arguments + %8:gpr = ARGUMENT 12, implicit $arguments + %0:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %32, %0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(5) null`, align 1, addrspace 5) + SSTORE %2, %1, implicit-def dead $arguments :: (store (s256) into %ir.storage_load_position_pointer14560, align 1, addrspace 5) + %31:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %31, %3, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(5) null`, align 1, addrspace 5) + %30:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %4, %30, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer19042, align 1, addrspace 5) + %29:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %5, %29, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer14550, align 1, addrspace 5) + %28:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %6, %28, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer18694, align 1, addrspace 5) + %27:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %7, %27, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.2, align 16, addrspace 1) + %26:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %2, %26, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.storage_load_position_pointer14560, align 1, addrspace 5) + %25:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %8, %25, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into %ir.3, addrspace 1) + %24:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + SSTORE %24, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(5) null`, align 4294967296, addrspace 5) + %23:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %22:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %21:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %19:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %18:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %17:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %16:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %15:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %14:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %13:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %11:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %10:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + FCALL @_testRemoveAndInsertBack_rt_707, %10, %11, %12, %13, %14, %15, %16, %17, %18, %19, %20, %21, %22, %23, implicit-def dead $arguments, implicit $sp, implicit-def $value_stack, implicit $value_stack + RET implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir b/llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir new file mode 100644 index 000000000000..d7f68954498b --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-recursive-func-fail.mir @@ -0,0 +1,284 @@ +# RUN: not --crash llc -x mir -run-pass=evm-backward-propagation-stackification < %s 2>&1 | FileCheck %s + +# This is the same test as bps-spills-2.mir, just with an "evm-recursive" attribute. +# Since for recursive functions we can't use spills and reloads, purpose of this is +# to test that we issue an error if there are unreachable slots in a recursive +# function and compression didn't help. + +# CHECK: LLVM ERROR: Stackification failed for 'main' function. It is recursive and has stack too deep errors. Consider refactoring it to use a non-recursive approach. + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + define fastcc void @main(i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) "evm-recursive" { + entry: + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 9 + hasPushDeployAddress: false +body: | + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %9:gpr = ARGUMENT 0, implicit $arguments + %10:gpr = ARGUMENT 1, implicit $arguments + %11:gpr = ARGUMENT 2, implicit $arguments + %12:gpr = ARGUMENT 3, implicit $arguments + %13:gpr = ARGUMENT 4, implicit $arguments + %14:gpr = ARGUMENT 5, implicit $arguments + %15:gpr = ARGUMENT 6, implicit $arguments + %16:gpr = ARGUMENT 7, implicit $arguments + %17:gpr = ARGUMENT 8, implicit $arguments + %39:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = AND %39, %17, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %38:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = AND %38, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %20, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %26, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %6, %0, %11, implicit-def dead $arguments + %40:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = AND %5, %40, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %6:gpr = COPY_I256 %10, implicit-def $arguments + JUMP_UNLESS %bb.5, %29, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %31:gpr = ULT %7, %16, implicit-def dead $arguments + %7:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %41:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %41, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %31, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %32, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %5:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %44:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %3:gpr = OR %13, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %24:gpr = GT %3, %44, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = OR %15, %42, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = OR %12, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %4:gpr = COPY_I256 %6, implicit-def $arguments + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %24, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spills-1.mir b/llvm/test/CodeGen/EVM/bps-spills-1.mir new file mode 100644 index 000000000000..be673dbb904d --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spills-1.mir @@ -0,0 +1,1072 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=128 < %s | FileCheck %s + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define fastcc void @main(i255 %trunc22495, i1 %comparison_result7009.not, i8 %trunc, i8 %trunc8) { + entry: + br label %conditional_rt_20_join_block + + "block_rt_46/7": ; preds = %conditional_rt_52_join_block3336, %conditional_rt_45_join_block2670 + %addition_result2803 = or i256 %stack_var_009.022000, 1 + %and_result2660 = and i256 %stack_var_009.022000, 0 + br label %conditional_rt_45_join_block2670 + + "block_rt_59/7.lr.ph.split": ; preds = %conditional_rt_52_join_block3336 + %0 = zext i255 %trunc22495 to i256 + switch i256 %0, label %remainder_join12885.preheader [ + i256 4, label %conditional_rt_52_join_block3336 + i256 6, label %"block_rt_64/7.us21835" + i256 0, label %"block_rt_123/7" + ] + + remainder_join12885.preheader: ; preds = %"block_rt_59/7.lr.ph.split" + br label %remainder_join12885 + + "block_rt_64/7.us21835": ; preds = %"block_rt_59/7.lr.ph.split" + br label %conditional_rt_52_join_block3336 + + "block_rt_58/7": ; preds = %"block_rt_123/8.split", %remainder_join12885 + %and_result2179 = and i256 %addition_result218222482, 1 + %addition_result2182 = or i256 %addition_result218222482, 0 + br label %remainder_join12885 + + "block_rt_73/7.preheader": ; preds = %"block_rt_73/7.preheader.preheader", %"block_rt_123/8.split" + %and_result58612176722479 = phi i256 [ 0, %"block_rt_123/8.split" ], [ 1, %"block_rt_73/7.preheader.preheader" ] + %1 = zext i8 0 to i256 + switch i256 %1, label %conditional_rt_74_join_block6475.preheader [ + i256 1, label %"block_rt_123/7.loopexit1" + i256 0, label %"block_rt_123/8.split" + ] + + conditional_rt_74_join_block6475.preheader: ; preds = %"block_rt_73/7.preheader" + br label %conditional_rt_74_join_block6475 + + "block_rt_80/7.split.split.split": ; preds = %"block_rt_81/7" + %comparison_result6468 = icmp ugt i256 %stack_var_013.221721, 0 + br i1 %comparison_result6468, label %"block_rt_123/8.split", label %conditional_rt_74_join_block6475 + + "block_rt_81/7": ; preds = %conditional_rt_89_join_block7784 + br i1 %comparison_result7009.not, label %"block_rt_80/7.split.split.split", label %remainder_join12901 + + "block_rt_97/7": ; preds = %conditional_rt_89_join_block7784 + ret void + + "block_rt_123/7.loopexit": ; preds = %conditional_rt_89_join_block7784 + br label %"block_rt_123/7" + + "block_rt_123/7.loopexit1": ; preds = %"block_rt_73/7.preheader" + br label %"block_rt_123/7" + + "block_rt_123/7.loopexit3": ; preds = %conditional_rt_45_join_block2670 + br label %"block_rt_123/7" + + "block_rt_123/7": ; preds = %"block_rt_59/7.lr.ph.split", %"block_rt_123/7.loopexit3", %"block_rt_123/7.loopexit1", %"block_rt_123/7.loopexit" + %stack_var_007.0 = phi i256 [ 1, %"block_rt_123/7.loopexit" ], [ 50, %"block_rt_123/7.loopexit1" ], [ 0, %"block_rt_123/7.loopexit3" ], [ 50, %"block_rt_59/7.lr.ph.split" ] + %and_result1807 = and i256 %stack_var_007.0, 1 + %stack_var_004.122011.be = or i256 %stack_var_004.1220116, 1 + br label %conditional_rt_20_join_block + + "block_rt_123/7.thread": ; preds = %conditional_rt_20_join_block + unreachable + + "block_rt_123/8.split": ; preds = %"block_rt_80/7.split.split.split", %"block_rt_73/7.preheader" + %comparison_result6061 = icmp ugt i256 %and_result58612176722479, 0 + br i1 %comparison_result6061, label %"block_rt_58/7", label %"block_rt_73/7.preheader" + + conditional_rt_20_join_block: ; preds = %"block_rt_123/7", %entry + %stack_var_004.1220116 = phi i256 [ %stack_var_004.122011.be, %"block_rt_123/7" ], [ 0, %entry ] + %stack_var_003.122010 = phi i256 [ %and_result1807, %"block_rt_123/7" ], [ 0, %entry ] + %comparison_result1783 = icmp eq i256 %stack_var_003.122010, 0 + br i1 %comparison_result1783, label %"block_rt_123/7.thread", label %conditional_rt_22_join_block + + conditional_rt_22_join_block: ; preds = %conditional_rt_20_join_block + br label %conditional_rt_45_join_block2670 + + conditional_rt_45_join_block2670: ; preds = %conditional_rt_22_join_block, %"block_rt_46/7" + %stack_var_009.022000 = phi i256 [ %addition_result2803, %"block_rt_46/7" ], [ 0, %conditional_rt_22_join_block ] + %2 = zext i8 %trunc8 to i256 + switch i256 %2, label %conditional_rt_52_join_block3336.preheader [ + i256 1, label %"block_rt_46/7" + i256 0, label %"block_rt_123/7.loopexit3" + ] + + conditional_rt_52_join_block3336.preheader: ; preds = %conditional_rt_45_join_block2670 + br label %conditional_rt_52_join_block3336 + + conditional_rt_52_join_block3336: ; preds = %"block_rt_64/7.us21835", %remainder_join12885, %"block_rt_59/7.lr.ph.split", %conditional_rt_52_join_block3336.preheader + br i1 %comparison_result7009.not, label %"block_rt_46/7", label %"block_rt_59/7.lr.ph.split" + + conditional_rt_74_join_block6475: ; preds = %conditional_rt_74_join_block6475.preheader, %"block_rt_80/7.split.split.split" + %stack_var_013.221721 = phi i256 [ 0, %"block_rt_80/7.split.split.split" ], [ 1, %conditional_rt_74_join_block6475.preheader ] + br label %remainder_join12901 + + conditional_rt_89_join_block7784: ; preds = %remainder_join12901, %conditional_rt_89_join_block7784 + %3 = zext i8 %trunc to i256 + switch i256 %3, label %"block_rt_97/7" [ + i256 0, label %conditional_rt_89_join_block7784 + i256 32, label %"block_rt_81/7" + i256 8, label %"block_rt_123/7.loopexit" + ] + + remainder_join12885: ; preds = %remainder_join12885.preheader, %"block_rt_58/7" + %addition_result218222482 = phi i256 [ %and_result2179, %"block_rt_58/7" ], [ 0, %remainder_join12885.preheader ] + %4 = zext i8 0 to i256 + switch i256 %4, label %"block_rt_73/7.preheader.preheader" [ + i256 0, label %"block_rt_58/7" + i256 1, label %conditional_rt_52_join_block3336 + ] + + "block_rt_73/7.preheader.preheader": ; preds = %remainder_join12885 + br label %"block_rt_73/7.preheader" + + remainder_join12901: ; preds = %conditional_rt_74_join_block6475, %"block_rt_81/7" + br label %conditional_rt_89_join_block7784 + } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } + - { id: 46, class: gpr, preferred-register: '' } + - { id: 47, class: gpr, preferred-register: '' } + - { id: 48, class: gpr, preferred-register: '' } + - { id: 49, class: gpr, preferred-register: '' } + - { id: 50, class: gpr, preferred-register: '' } + - { id: 51, class: gpr, preferred-register: '' } + - { id: 52, class: gpr, preferred-register: '' } + - { id: 53, class: gpr, preferred-register: '' } + - { id: 54, class: gpr, preferred-register: '' } + - { id: 55, class: gpr, preferred-register: '' } + - { id: 56, class: gpr, preferred-register: '' } + - { id: 57, class: gpr, preferred-register: '' } + - { id: 58, class: gpr, preferred-register: '' } + - { id: 59, class: gpr, preferred-register: '' } + - { id: 60, class: gpr, preferred-register: '' } + - { id: 61, class: gpr, preferred-register: '' } + - { id: 62, class: gpr, preferred-register: '' } + - { id: 63, class: gpr, preferred-register: '' } + - { id: 64, class: gpr, preferred-register: '' } + - { id: 65, class: gpr, preferred-register: '' } + - { id: 66, class: gpr, preferred-register: '' } + - { id: 67, class: gpr, preferred-register: '' } + - { id: 68, class: gpr, preferred-register: '' } + - { id: 69, class: gpr, preferred-register: '' } + - { id: 70, class: gpr, preferred-register: '' } + - { id: 71, class: gpr, preferred-register: '' } + - { id: 72, class: gpr, preferred-register: '' } + - { id: 73, class: gpr, preferred-register: '' } + - { id: 74, class: gpr, preferred-register: '' } + - { id: 75, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 4 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 255 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH32_S i256 57896044618658097711785492504343953926634992332820282019728792003956564819967 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH1_S i256 4 + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH1_S i256 6 + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 255 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 8 + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 32 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_20_join_block: + ; CHECK-NEXT: successors: %bb.43(0x00000000), %bb.2(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.43 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.conditional_rt_22_join_block: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.conditional_rt_45_join_block2670: + ; CHECK-NEXT: successors: %bb.4(0x3efbefbf), %bb.5(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.44(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.44 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.conditional_rt_45_join_block2670: + ; CHECK-NEXT: successors: %bb.41(0x04000000), %bb.6(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.41 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7.conditional_rt_52_join_block3336: + ; CHECK-NEXT: successors: %bb.8(0x04000000), %bb.9(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.9 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.44(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.44 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.10(0x2a3677d4), %bb.11(0x55c9882c) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x3efbefbf), %bb.13(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.13 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.14(0x04000001), %bb.15(0x7bffffff) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: PUSH1_S i256 50 + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PseudoJUMPI %bb.15 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.14: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.42 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.15.remainder_join12885.preheader: + ; CHECK-NEXT: successors: %bb.16(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.16.remainder_join12885: + ; CHECK-NEXT: successors: %bb.17(0x3efbefbf), %bb.18(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.17: + ; CHECK-NEXT: successors: %bb.39(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.39 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.18.remainder_join12885: + ; CHECK-NEXT: successors: %bb.19(0x04000000), %bb.20(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.20 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.19: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.20: + ; CHECK-NEXT: successors: %bb.21(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.21: + ; CHECK-NEXT: successors: %bb.22(0x3efbefbf), %bb.23(0x41041041) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.23 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.22: + ; CHECK-NEXT: successors: %bb.36(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.36 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.23: + ; CHECK-NEXT: successors: %bb.24(0x04000000), %bb.25(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.25 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.24: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP12_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.42 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.25.conditional_rt_74_join_block6475.preheader: + ; CHECK-NEXT: successors: %bb.26(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP12_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.26.conditional_rt_74_join_block6475: + ; CHECK-NEXT: successors: %bb.27(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.27.conditional_rt_89_join_block7784: + ; CHECK-NEXT: successors: %bb.28(0x74b4b4b5), %bb.29(0x0b4b4b4b) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.29 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.28: + ; CHECK-NEXT: successors: %bb.27(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PseudoJUMP %bb.27 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.29.conditional_rt_89_join_block7784: + ; CHECK-NEXT: successors: %bb.40(0x2aaaaaae), %bb.30(0x55555552) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMPI %bb.40 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.30.conditional_rt_89_join_block7784: + ; CHECK-NEXT: successors: %bb.31(0x40000008), %bb.45(0x3ffffff8) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP13_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: SWAP14_S + ; CHECK-NEXT: SWAP13_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.45 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.31: + ; CHECK-NEXT: successors: %bb.33(0x04000000), %bb.32(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP15_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: SWAP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: PseudoJUMPI %bb.33 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.32: + ; CHECK-NEXT: successors: %bb.27(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PseudoJUMP %bb.27 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.33: + ; CHECK-NEXT: successors: %bb.34(0x04000000), %bb.35(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: PseudoJUMPI %bb.35 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.34: + ; CHECK-NEXT: successors: %bb.36(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.36 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.35: + ; CHECK-NEXT: successors: %bb.26(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PseudoJUMP %bb.26 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.36: + ; CHECK-NEXT: successors: %bb.37(0x04000000), %bb.38(0x7c000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: EQ_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PseudoJUMPI %bb.38 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.37: + ; CHECK-NEXT: successors: %bb.39(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PseudoJUMP %bb.39 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.38: + ; CHECK-NEXT: successors: %bb.21(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PseudoJUMP %bb.21 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.39: + ; CHECK-NEXT: successors: %bb.16(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP15_S + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: PseudoJUMP %bb.16 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.40: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: PseudoJUMP %bb.42 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.41: + ; CHECK-NEXT: successors: %bb.42(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.42: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.43: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.44: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.45: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoRET + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %11:gpr = ARGUMENT 0, implicit $arguments + %12:gpr = ARGUMENT 1, implicit $arguments + %13:gpr = ARGUMENT 2, implicit $arguments + %14:gpr = ARGUMENT 3, implicit $arguments + %18:gpr = CONST_I256 i256 255, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %17:gpr = AND %18, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %65:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = EQ %17, %65, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = CONST_I256 i256 57896044618658097711785492504343953926634992332820282019728792003956564819967, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %25:gpr = AND %26, %11, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %27:gpr = CONST_I256 i256 4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %28:gpr = EQ %25, %27, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = CONST_I256 i256 6, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %30:gpr = EQ %25, %29, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %64:gpr = CONST_I256 i256 255, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %41:gpr = AND %64, %13, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %44:gpr = EQ %41, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %45:gpr = CONST_I256 i256 32, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %46:gpr = EQ %41, %45, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %60:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_20_join_block: + successors: %bb.27(0x00000000), %bb.2(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.27, %60, implicit-def dead $arguments + + bb.2.conditional_rt_22_join_block: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.3.conditional_rt_45_join_block2670: + successors: %bb.44(0x3efbefbf), %bb.4(0x41041041) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %20, implicit-def $arguments + + bb.44: + successors: %bb.28(0x80000000) + liveins: $value_stack + + JUMP %bb.28, implicit-def $arguments + + bb.4.conditional_rt_45_join_block2670: + successors: %bb.25(0x04000000), %bb.33(0x7c000000) + liveins: $value_stack + + JUMP_UNLESS %bb.25, %17, implicit-def dead $arguments + + bb.33: + successors: %bb.5(0x80000000) + liveins: $value_stack + + bb.5.conditional_rt_52_join_block3336: + successors: %bb.45(0x04000000), %bb.6(0x7c000000) + liveins: $value_stack + + %66:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %23:gpr = AND %12, %66, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %23, implicit-def $arguments + + bb.45: + successors: %bb.28(0x80000000) + liveins: $value_stack + + JUMP %bb.28, implicit-def $arguments + + bb.6: + successors: %bb.30(0x2a3677d4), %bb.7(0x55c9882c) + liveins: $value_stack + + JUMP_UNLESS %bb.7, %28, implicit-def $arguments + + bb.30: + successors: %bb.5(0x80000000) + liveins: $value_stack + + JUMP %bb.5, implicit-def $arguments + + bb.7: + successors: %bb.32(0x3efbefbf), %bb.8(0x41041041) + liveins: $value_stack + + JUMP_UNLESS %bb.8, %30, implicit-def $arguments + + bb.32: + successors: %bb.5(0x80000000) + liveins: $value_stack + + JUMP %bb.5, implicit-def $arguments + + bb.8: + successors: %bb.42(0x04000001), %bb.9(0x7bffffff) + liveins: $value_stack + + %24:gpr = CONST_I256 i256 50, implicit-def dead $arguments + JUMPI %bb.9, %25, implicit-def $arguments + + bb.42: + successors: %bb.26(0x80000000) + liveins: $value_stack + + JUMP %bb.26, implicit-def $arguments + + bb.9.remainder_join12885.preheader: + successors: %bb.10(0x80000000) + liveins: $value_stack + + %10:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.10.remainder_join12885: + successors: %bb.40(0x3efbefbf), %bb.11(0x41041041) + liveins: $value_stack + + %67:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.11, %67, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.40: + successors: %bb.23(0x80000000) + liveins: $value_stack + + JUMP %bb.23, implicit-def $arguments + + bb.11.remainder_join12885: + successors: %bb.31(0x04000000), %bb.12(0x7c000000) + liveins: $value_stack + + %68:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.12, %68, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.31: + successors: %bb.5(0x80000000) + liveins: $value_stack + + JUMP %bb.5, implicit-def $arguments + + bb.12: + successors: %bb.13(0x80000000) + liveins: $value_stack + + %2:gpr = CONST_I256 i256 1, implicit-def dead $arguments + + bb.13: + successors: %bb.38(0x3efbefbf), %bb.14(0x41041041) + liveins: $value_stack + + %69:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.14, %69, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.38: + successors: %bb.22(0x80000000) + liveins: $value_stack + + JUMP %bb.22, implicit-def $arguments + + bb.14: + successors: %bb.43(0x04000000), %bb.15(0x7c000000) + liveins: $value_stack + + %70:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.15, %70, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.43: + successors: %bb.26(0x80000000) + liveins: $value_stack + + JUMP %bb.26, implicit-def $arguments + + bb.15.conditional_rt_74_join_block6475.preheader: + successors: %bb.16(0x80000000) + liveins: $value_stack + + %62:gpr = CONST_I256 i256 1, implicit-def dead $arguments + + bb.16.conditional_rt_74_join_block6475: + successors: %bb.17(0x80000000) + liveins: $value_stack + + %9:gpr = COPY_I256 %62, implicit-def $arguments + + bb.17.conditional_rt_89_join_block7784: + successors: %bb.36(0x74b4b4b5), %bb.18(0x0b4b4b4b) + liveins: $value_stack + + JUMPI %bb.18, %41, implicit-def $arguments + + bb.36: + successors: %bb.17(0x80000000) + liveins: $value_stack + + JUMP %bb.17, implicit-def $arguments + + bb.18.conditional_rt_89_join_block7784: + successors: %bb.24(0x2aaaaaae), %bb.19(0x55555552) + liveins: $value_stack + + JUMPI %bb.24, %44, implicit-def dead $arguments + + bb.19.conditional_rt_89_join_block7784: + successors: %bb.20(0x40000008), %bb.29(0x3ffffff8) + liveins: $value_stack + + JUMP_UNLESS %bb.29, %46, implicit-def $arguments + + bb.20: + successors: %bb.21(0x04000000), %bb.37(0x7c000000) + liveins: $value_stack + + JUMPI %bb.21, %23, implicit-def $arguments + + bb.37: + successors: %bb.17(0x80000000) + liveins: $value_stack + + JUMP %bb.17, implicit-def $arguments + + bb.21: + successors: %bb.39(0x04000000), %bb.35(0x7c000000) + liveins: $value_stack + + %62:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %71:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %52:gpr = EQ %9, %71, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMPI %bb.35, %52, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.39: + successors: %bb.22(0x80000000) + liveins: $value_stack + + JUMP %bb.22, implicit-def $arguments + + bb.35: + successors: %bb.16(0x80000000) + liveins: $value_stack + + JUMP %bb.16, implicit-def $arguments + + bb.22: + successors: %bb.41(0x04000000), %bb.34(0x7c000000) + liveins: $value_stack + + %53:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %54:gpr = EQ %2, %53, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = CONST_I256 i256 0, implicit-def dead $arguments + JUMPI %bb.34, %54, implicit-def $arguments + + bb.41: + successors: %bb.23(0x80000000) + liveins: $value_stack + + JUMP %bb.23, implicit-def $arguments + + bb.34: + successors: %bb.13(0x80000000) + liveins: $value_stack + + JUMP %bb.13, implicit-def $arguments + + bb.23: + successors: %bb.10(0x80000000) + liveins: $value_stack + + %72:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %10:gpr = AND %10, %72, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP %bb.10, implicit-def $arguments + + bb.24: + successors: %bb.26(0x80000000) + liveins: $value_stack + + %24:gpr = CONST_I256 i256 1, implicit-def dead $arguments + JUMP %bb.26, implicit-def $arguments + + bb.25: + successors: %bb.26(0x80000000) + liveins: $value_stack + + %24:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.26: + successors: %bb.1(0x80000000) + liveins: $value_stack + + %74:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = OR %6, %74, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %73:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %60:gpr = AND %24, %73, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP %bb.1, implicit-def $arguments + + bb.27: + successors: + liveins: $value_stack + + bb.28: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %75:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %75, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP %bb.3, implicit-def $arguments + + bb.29: + liveins: $value_stack + + RET implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spills-2.mir b/llvm/test/CodeGen/EVM/bps-spills-2.mir new file mode 100644 index 000000000000..2b80f72a8f8a --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spills-2.mir @@ -0,0 +1,486 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=128 < %s | FileCheck %s + +--- | + source_filename = "reduced.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + define fastcc void @main(i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) { + entry: + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 9 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: + ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x00000000), %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.6 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x7c000000), %bb.8(0x04000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: CALLDATACOPY_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.12 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.9(0x80000000), %bb.10(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: ULT_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.10 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: SWAP11_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12.conditional_rt_181_join_block: + ; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.13(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PseudoJUMPI %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %9:gpr = ARGUMENT 0, implicit $arguments + %10:gpr = ARGUMENT 1, implicit $arguments + %11:gpr = ARGUMENT 2, implicit $arguments + %12:gpr = ARGUMENT 3, implicit $arguments + %13:gpr = ARGUMENT 4, implicit $arguments + %14:gpr = ARGUMENT 5, implicit $arguments + %15:gpr = ARGUMENT 6, implicit $arguments + %16:gpr = ARGUMENT 7, implicit $arguments + %17:gpr = ARGUMENT 8, implicit $arguments + %39:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = AND %39, %17, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %38:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = AND %38, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %20, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %26, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %6, %0, %11, implicit-def dead $arguments + %40:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = AND %5, %40, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %6:gpr = COPY_I256 %10, implicit-def $arguments + JUMP_UNLESS %bb.5, %29, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %31:gpr = ULT %7, %16, implicit-def dead $arguments + %7:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %41:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %41, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %31, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %32, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %5:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %44:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %3:gpr = OR %13, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %24:gpr = GT %3, %44, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = OR %15, %42, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = OR %12, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %4:gpr = COPY_I256 %6, implicit-def $arguments + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %24, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/evm-recursive.ll b/llvm/test/CodeGen/EVM/evm-recursive.ll new file mode 100644 index 000000000000..5d14b31d5ec8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-recursive.ll @@ -0,0 +1,39 @@ +; RUN: opt -passes=evm-mark-recursive-functions -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK: define i256 @indirect_recursive2(i256 %x) #[[RECURSIVE:[0-9]+]] { +define i256 @indirect_recursive2(i256 %x) { +entry: + %call = call i256 @indirect_recursive1(i256 %x) + ret i256 %call +} + +; CHECK: define i256 @indirect_recursive1(i256 %y) #[[RECURSIVE:[0-9]+]] { +define i256 @indirect_recursive1(i256 %y) { +entry: + %call = call i256 @indirect_recursive2(i256 %y) + ret i256 %call +} + +; CHECK: define i256 @recursive(i256 %z) #[[RECURSIVE:[0-9]+]] { +define i256 @recursive(i256 %z) { +entry: + %call = call i256 @recursive(i256 %z) + ret i256 %call +} + +; CHECK: define i256 @calls_recursive(i256 %a) { +define i256 @calls_recursive(i256 %a) { +entry: + %call = call i256 @recursive(i256 %a) + ret i256 %call +} + +; CHECK: define i256 @non_recursive() { +define i256 @non_recursive() { + ret i256 1 +} + +; CHECK: attributes #[[RECURSIVE]] = { "evm-recursive" } diff --git a/llvm/test/CodeGen/EVM/finalize-stack-frames.mir b/llvm/test/CodeGen/EVM/finalize-stack-frames.mir new file mode 100644 index 000000000000..5ab7c46a6bde --- /dev/null +++ b/llvm/test/CodeGen/EVM/finalize-stack-frames.mir @@ -0,0 +1,78 @@ +# RUN: llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=96 < %s | FileCheck %s +# RUN: llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=128 -evm-stack-region-size=128 < %s 2>&1 | FileCheck --check-prefix=CHECK-MOREALLOC %s +# RUN: not --crash llc -x mir -run-pass=evm-finalize-stack-frames < %s 2>&1 | FileCheck --check-prefix=CHECK-NOALLOC %s +# RUN: not --crash llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-size=96 < %s 2>&1 | FileCheck --check-prefix=CHECK-NOSTART %s +# RUN: not --crash llc -x mir -run-pass=evm-finalize-stack-frames -evm-stack-region-offset=129 -evm-stack-region-size=96 < %s 2>&1 | FileCheck --check-prefix=CHECK-NOMOD %s + +# CHECK-MOREALLOC: warning: allocated stack region size: 128 is larger than the total stack size: 96 +# CHECK-NOALLOC: LLVM ERROR: Total stack size: 96 is larger than the allocated stack region size: 0 +# CHECK-NOSTART: LLVM ERROR: Stack region offset must be set when stack region size is set. Use --evm-stack-region-offset to set the offset. +# CHECK-NOMOD: LLVM ERROR: Stack region offset must be a multiple of 32 bytes. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define void @foo() { + ret void + } + + define void @bar() { + ret void + } + +... +--- +name: foo +alignment: 1 +stack: + - { id: 0, name: '', type: spill-slot, offset: 0, size: 32, alignment: 32, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: 0, size: 32, alignment: 32, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments + + ; CHECK-LABEL: name: foo + ; CHECK: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 128 + ; CHECK-NEXT: PUSH1_S i256 160 + ; CHECK-NEXT: PseudoRET + PUSH_FRAME %stack.0 + PUSH_FRAME %stack.1 + PseudoRET + +... +--- +name: bar +alignment: 1 +stack: + - { id: 0, name: '', type: spill-slot, offset: 0, size: 32, alignment: 32, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments + + ; CHECK-LABEL: name: bar + ; CHECK: liveins: $arguments + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 192 + ; CHECK-NEXT: PseudoRET + PUSH_FRAME %stack.0 + PseudoRET + +... diff --git a/llvm/test/CodeGen/EVM/machine-function-info.ll b/llvm/test/CodeGen/EVM/machine-function-info.ll new file mode 100644 index 000000000000..e9878084d454 --- /dev/null +++ b/llvm/test/CodeGen/EVM/machine-function-info.ll @@ -0,0 +1,38 @@ +; RUN: llc -stop-before=finalize-isel < %s | FileCheck --check-prefix=CHECK-PARAMS %s +; RUN: llc -stop-before=finalize-isel < %s | FileCheck --check-prefix=CHECK-PUSHDEPLOY %s +; RUN: llc -stop-after=evm-backward-propagation-stackification < %s | FileCheck --check-prefix=CHECK-STACKIFIED %s + +; Check that the machine function info is correctly set up for different functions. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare i256 @llvm.evm.pushdeployaddress() + +define i256 @params(i256 %arg1, i256 %arg2) { +; CHECK-PARAMS-LABEL: name: params +; CHECK-PARAMS: machineFunctionInfo: +; CHECK-PARAMS: isStackified: false +; CHECK-PARAMS: numberOfParameters: 2 +; CHECK-PARAMS: hasPushDeployAddress: false + ret i256 %arg1 +} + +define void @pushdeploy() noreturn { +; CHECK-PUSHDEPLOY-LABEL: name: pushdeploy +; CHECK-PUSHDEPLOY: machineFunctionInfo: +; CHECK-PUSHDEPLOY: isStackified: false +; CHECK-PUSHDEPLOY: numberOfParameters: 0 +; CHECK-PUSHDEPLOY: hasPushDeployAddress: true + %push = call i256 @llvm.evm.pushdeployaddress() + unreachable +} + +define void @stackified() { +; CHECK-STACKIFIED-LABEL: name: stackified +; CHECK-STACKIFIED: machineFunctionInfo: +; CHECK-STACKIFIED: isStackified: true +; CHECK-STACKIFIED: numberOfParameters: 0 +; CHECK-STACKIFIED: hasPushDeployAddress: false + ret void +} diff --git a/llvm/test/CodeGen/EVM/machine-function-info.mir b/llvm/test/CodeGen/EVM/machine-function-info.mir new file mode 100644 index 000000000000..ef5ba70b2f5b --- /dev/null +++ b/llvm/test/CodeGen/EVM/machine-function-info.mir @@ -0,0 +1,37 @@ +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +# Test that we can properly read the machine function info from MIR and +# that isStackified is changed to true after stackification. + +--- | + ; ModuleID = 'test.ll' + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg1, i256 %arg2) { + ret i256 %arg1 + } + +... +# CHECK-LABEL: name: test +# CHECK: machineFunctionInfo: +# CHECK: isStackified: true +# CHECK: numberOfParameters: 2 +# CHECK: hasPushDeployAddress: false +--- +name: test +alignment: 1 +tracksRegLiveness: true +machineFunctionInfo: + isStackified: false + numberOfParameters: 2 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments, $value_stack + + %0:gpr = ARGUMENT 0, implicit $arguments, implicit-def $value_stack, implicit $value_stack + RET %0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + +... diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-1.ll b/llvm/test/CodeGen/EVM/stack-too-deep-1.ll index 8accc23febdb..044647aa6521 100644 --- a/llvm/test/CodeGen/EVM/stack-too-deep-1.ll +++ b/llvm/test/CodeGen/EVM/stack-too-deep-1.ll @@ -1,28 +1,17 @@ -; RUN: not --crash llc < %s -o /dev/null 2>&1 | FileCheck %s - -; Test that EVMStackSolver can catch errors when transformation from -; the BBs entry stack to the entry stack of the first MI is not possible. - -; The stack built for the 'bad' BB is -; -; 20.block_rt_88/7.outer: -; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %29 %33 ] -; -; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 7 21 ] -; EQ -; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 %72 ] -; -; [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 %72 ] - -; CHECK: EVMStackSolver cannot transform -; CHECK-SAME: [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %29 %33 ] -; CHECK-SAME: to -; CHECK-SAME: [ %82 %85 %7 %78 %70 %11 %64 %17 %19 %54 %46 %5 %2 %40 %9 %82 %85 %29 %33 7 21 ] -; CHECK-SAME: : stack too deep. +; REQUIRES: asserts +; RUN: llc -evm-stack-region-offset=128 -evm-stack-region-size=32 --debug-only=evm-stack-solver < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" +; Check that the stack solver detects unreachable slots, generates spills for them, and +; succesfully compiles the function. Also, check that we allocated the exact amount of +; stack space needed for the function, without any warnings about allocated stack region size. + +; CHECK: Unreachable slots found: 2, iteration: 1 +; CHECK: Spilling 1 registers +; CHECK-NOT: warning: allocated stack region size: + define dso_local fastcc void @main() unnamed_addr { entry: br label %"block_rt_19/0" diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-2.ll b/llvm/test/CodeGen/EVM/stack-too-deep-2.ll index 7afc440e0851..03e3ea1c5826 100644 --- a/llvm/test/CodeGen/EVM/stack-too-deep-2.ll +++ b/llvm/test/CodeGen/EVM/stack-too-deep-2.ll @@ -1,27 +1,28 @@ -; RUN: not --crash llc < %s -o /dev/null 2>&1 | FileCheck %s - -; Test that EVMStackSolver can catch errors when transformation from -; the BBs entry stack to the BBs exit stack (empty BB) is not possible. - -; The stack built for the 'bad' BB is -; -; 69.: -; [ RET %137 %8 %0 %105 %131 %154 %126 %18 %26 %28 %20 %81 %7 %19 %85 %3 %137 %16 %58 %63 ] -; -; [ RET %137 %20 %16 %0 %105 %131 %154 %126 %18 %26 %58 %137 %7 %19 %85 %81 %63 %3 %8 1 255 %28 ] - -; CHECK: EVMStackSolver cannot transform -; CHECK-SAME: [ RET %137 %8 %0 %105 %131 %154 %126 %18 %26 %28 %20 %81 %7 %19 %85 %3 %137 %16 %58 %63 ] -; CHECK-SAME: to -; CHECK-SAME: [ RET %137 %20 %16 %0 %105 %131 %154 %126 %18 %26 %58 %137 %7 %19 %85 %81 %63 %3 %8 1 255 %28 ] -; CHECK-SAME: : stack too deep. +; REQUIRES: asserts +; RUN: llc -evm-stack-region-offset=128 -evm-stack-region-size=192 --debug-only=evm-stack-solver < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" declare i256 @checked_mul_uint8(i256) -define private fastcc i256 @fun_test_462(i256 %0) unnamed_addr { +; Check that the stack solver detects unreachable slots, generates spills for them, and +; succesfully compiles the function. Also, check that we allocated the exact amount of +; stack space needed for the function, without any warnings about allocated stack region size. + +; CHECK: Unreachable slots found: 30, iteration: 1 +; CHECK: Spilling 2 registers +; CHECK: Unreachable slots found: 20, iteration: 2 +; CHECK: Spilling 1 registers +; CHECK: Unreachable slots found: 8, iteration: 3 +; CHECK: Spilling 1 registers +; CHECK: Unreachable slots found: 6, iteration: 4 +; CHECK: Spilling 1 registers +; CHECK: Unreachable slots found: 2, iteration: 5 +; CHECK: Spilling 1 registers +; CHECK-NOT: warning: allocated stack region size: + +define fastcc i256 @fun_test_462(i256 %0) unnamed_addr { entry: %and_result3 = and i256 %0, 255 %trunc = trunc i256 %0 to i8 diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-3.ll b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll index 245741b8ded6..38d8f94df332 100644 --- a/llvm/test/CodeGen/EVM/stack-too-deep-3.ll +++ b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll @@ -1,46 +1,19 @@ -; RUN: not --crash llc < %s -o /dev/null 2>&1 | FileCheck %s - -; Test that EVMStackSolver can catch errors when transformation from -; an MIs exit stack to an entry stack of the next MI is not possible. - -; The stack built for the 'bad' BB is -; -; 2.block_rt_158/3: -; [ %19 %64 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 ] -; -; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %64 %19 ] -; CALLDATALOAD -; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %64 %4 ] -; -; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %4 5 ] -; SHL -; [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %5 ] -; -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %19 %5 0 ] -; SUB -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %19 %46 ] -; -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %46 32 %19 ] -; ADD -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %46 %71 ] -; -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %46 %71 ] -; GT -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %47 ] -; -; [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %47 ] - -; CHECK: EVMStackSolver cannot transform -; CHECK-SAME: [ %19 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %5 ] -; CHECK-SAME: to -; CHECK-SAME: [ %5 %19 %0 %20 %3 %21 %35 %22 %63 %41 %62 %23 %61 %65 %4 %64 %19 %5 0 ] -; CHECK-SAME: : stack too deep. +; REQUIRES: asserts +; RUN: llc -evm-stack-region-offset=128 -evm-stack-region-size=32 --debug-only=evm-stack-solver < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #0 +; Check that the stack solver detects unreachable slots, generates spills for them, and +; succesfully compiles the function. Also, check that we allocated the exact amount of +; stack space needed for the function, without any warnings about allocated stack region size. + +; CHECK: Unreachable slots found: 1, iteration: 1 +; CHECK: Spilling 1 registers +; CHECK-NOT: warning: allocated stack region size: + define dso_local fastcc void @main() unnamed_addr { entry: br i1 poison, label %"block_rt_7/0", label %"block_rt_2/0" From 6efbf1c12b0a1efbbfaad0484c05eb861be56eea Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 20 Jun 2025 15:55:10 +0200 Subject: [PATCH 136/203] [EVM] Add pre-commit test for Don't spill regs whose defs are unreachable Signed-off-by: Vladimir Radosavljevic --- .../CodeGen/EVM/bps-spill-unreachable-arg.mir | 283 +++++++++++++++++ .../CodeGen/EVM/bps-spill-unreachable-ret.mir | 300 ++++++++++++++++++ 2 files changed, 583 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir create mode 100644 llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir new file mode 100644 index 000000000000..41ce72ecc754 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir @@ -0,0 +1,283 @@ +# RUN: not --crash llc -x mir -run-pass=evm-backward-propagation-stackification < %s 2>&1 | FileCheck %s + +# Test that stackification fails when unreachable register is spilled. + +# CHECK: Unexpected stack depth + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + define fastcc void @main(i256 %unused, i256 %unused1, i256 %unused2, i256 %unused3, i256 %unused4, i256 %unused5, i256 %unused6, i256 %unused7, i256 %unused8, i256 %unused9, i256 %unused10, i256 %unused11, i256 %unused12, i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) { + entry: + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 22 + hasPushDeployAddress: false +body: | + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %9:gpr = ARGUMENT 13, implicit $arguments + %10:gpr = ARGUMENT 14, implicit $arguments + %11:gpr = ARGUMENT 15, implicit $arguments + %12:gpr = ARGUMENT 16, implicit $arguments + %13:gpr = ARGUMENT 17, implicit $arguments + %14:gpr = ARGUMENT 18, implicit $arguments + %15:gpr = ARGUMENT 19, implicit $arguments + %16:gpr = ARGUMENT 20, implicit $arguments + %17:gpr = ARGUMENT 21, implicit $arguments + %39:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %20:gpr = AND %39, %17, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %38:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %26:gpr = AND %38, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %8:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %20, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %26, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %6, %0, %11, implicit-def dead $arguments + %40:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %29:gpr = AND %5, %40, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %6:gpr = COPY_I256 %10, implicit-def $arguments + JUMP_UNLESS %bb.5, %29, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %31:gpr = ULT %7, %16, implicit-def dead $arguments + %7:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %41:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = OR %8, %41, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %31, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %32:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %32, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %5:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %44:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %43:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %3:gpr = OR %13, %43, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %24:gpr = GT %3, %44, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = OR %15, %42, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = OR %12, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %4:gpr = COPY_I256 %6, implicit-def $arguments + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %9, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %24, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir new file mode 100644 index 000000000000..4513620b3ae6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir @@ -0,0 +1,300 @@ +# RUN: not --crash llc -x mir -run-pass=evm-backward-propagation-stackification < %s 2>&1 | FileCheck %s + +# Test that stackification fails when unreachable register is spilled. + +# CHECK: Unexpected stack depth + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) + declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + declare { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } @foo() + + define fastcc void @main() { + entry: + %ret = call { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } @foo() + %comparison_result4030.not = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 0 + %calldata_load_result2605 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 1 + %stack_var_012.36954 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 2 + %comparison_result3399.not = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 3 + %calldata_load_result3245 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 4 + %calldata_load_result1899 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 5 + %shift_left_non_overflow_result3390 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 6 + %addition_result2798 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 7 + %calldata_load_result2781 = extractvalue { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } %ret, 8 + br label %conditional_rt_187_join_block + + "block_rt_2/0": ; preds = %conditional_rt_187_join_block, %conditional_rt_181_join_block, %"block_rt_165/1" + unreachable + + "block_rt_160/3": ; preds = %"block_rt_165/1" + %calldatacopy_destination_pointer3688 = inttoptr i256 %stack_var_021.0.in6947 to ptr addrspace(1) + %calldatacopy_source_pointer3689 = inttoptr i256 %addition_result3756 to ptr addrspace(2) + tail call void @llvm.evm.memcpyas1as2(ptr addrspace(1) %calldatacopy_destination_pointer3688, ptr addrspace(2) %calldatacopy_source_pointer3689, i256 %shift_left_non_overflow_result3390, i1 false) + br i1 %comparison_result3909.not, label %conditional_rt_181_join_block, label %"block_rt_181/0" + + "block_rt_165/1": ; preds = %conditional_rt_181_join_block + %addition_result3756 = or i256 %calldata_load_result1899, %addition_result4054 + br i1 %comparison_result3399.not, label %"block_rt_2/0", label %"block_rt_160/3" + + "block_rt_181/0": ; preds = %"block_rt_160/3" + %addition_result4064 = or i256 %stack_var_011.36953, 1 + %comparison_result4003.not = icmp ult i256 %stack_var_013.36955, %calldata_load_result2605 + br i1 %comparison_result4003.not, label %conditional_rt_187_join_block, label %"block_rt_187/0.loopexit" + + "block_rt_187/0.loopexit": ; preds = %"block_rt_181/0" + store i256 %stack_var_021.06950, ptr addrspace(1) null, align 64 + unreachable + + "block_rt_188/0": ; preds = %conditional_rt_187_join_block + %addition_result4054 = or i256 %stack_var_012.36954, 1 + %addition_result1909 = or i256 %calldata_load_result3245, 1 + br label %conditional_rt_181_join_block + + conditional_rt_181_join_block: ; preds = %"block_rt_188/0", %"block_rt_160/3" + %stack_var_021.06950 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + %comparison_result3909.not = phi i1 [ true, %"block_rt_188/0" ], [ false, %"block_rt_160/3" ] + %stack_var_021.0.in6947 = phi i256 [ 0, %"block_rt_188/0" ], [ %addition_result2798, %"block_rt_160/3" ] + store i256 %calldata_load_result2781, ptr addrspace(1) null, align 1 + %comparison_result1913.not = icmp slt i256 0, %addition_result1909 + br i1 %comparison_result1913.not, label %"block_rt_165/1", label %"block_rt_2/0" + + conditional_rt_187_join_block: ; preds = %"block_rt_181/0", %entry + %stack_var_013.36955 = phi i256 [ 0, %entry ], [ 1, %"block_rt_181/0" ] + %stack_var_011.36953 = phi i256 [ 0, %entry ], [ %addition_result4064, %"block_rt_181/0" ] + br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" + } + + ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) + declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 + + attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } + +... +--- +name: main +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } + - { id: 43, class: gpr, preferred-register: '' } + - { id: 44, class: gpr, preferred-register: '' } + - { id: 45, class: gpr, preferred-register: '' } + - { id: 46, class: gpr, preferred-register: '' } + - { id: 47, class: gpr, preferred-register: '' } + - { id: 48, class: gpr, preferred-register: '' } + - { id: 49, class: gpr, preferred-register: '' } + - { id: 50, class: gpr, preferred-register: '' } + - { id: 51, class: gpr, preferred-register: '' } + - { id: 52, class: gpr, preferred-register: '' } + - { id: 53, class: gpr, preferred-register: '' } + - { id: 54, class: gpr, preferred-register: '' } + - { id: 55, class: gpr, preferred-register: '' } + - { id: 56, class: gpr, preferred-register: '' } + - { id: 57, class: gpr, preferred-register: '' } + - { id: 58, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: true + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0.entry: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + %52:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr, %1:gpr, %2:gpr, %3:gpr, %4:gpr, %5:gpr, %6:gpr, %7:gpr, %8:gpr, dead %19:gpr, dead %20:gpr, dead %21:gpr, dead %22:gpr, dead %23:gpr, dead %24:gpr, dead %25:gpr, dead %26:gpr, dead %27:gpr, dead %28:gpr, dead %29:gpr, dead %30:gpr, dead %31:gpr = FCALL @foo, implicit-def dead $arguments, implicit $sp, implicit-def $value_stack, implicit $value_stack + %33:gpr = AND %0, %52, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %51:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %39:gpr = AND %51, %3, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %16:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %17:gpr = CONST_I256 i256 0, implicit-def dead $arguments + + bb.1.conditional_rt_187_join_block: + successors: %bb.7(0x80000000), %bb.10(0x00000000) + liveins: $value_stack + + JUMPI %bb.7, %33, implicit-def $arguments + + bb.10: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2: + successors: + liveins: $value_stack + + bb.3: + successors: %bb.12(0x00000000), %bb.4(0x80000000) + liveins: $value_stack + + JUMP_UNLESS %bb.4, %39, implicit-def $arguments + + bb.12: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.4: + successors: %bb.13(0x7c000000), %bb.5(0x04000000) + liveins: $value_stack + + CALLDATACOPY %15, %9, %6, implicit-def dead $arguments + %53:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %42:gpr = AND %14, %53, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %14:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %15:gpr = COPY_I256 %7, implicit-def $arguments + JUMP_UNLESS %bb.5, %42, implicit-def $arguments + + bb.13: + successors: %bb.8(0x80000000) + liveins: $value_stack + + JUMP %bb.8, implicit-def $arguments + + bb.5: + successors: %bb.9(0x80000000), %bb.6(0x00000000) + liveins: $value_stack + + %44:gpr = ULT %16, %1, implicit-def dead $arguments + %16:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %54:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %17:gpr = OR %17, %54, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %44, implicit-def $arguments + + bb.9: + successors: %bb.1(0x80000000) + liveins: $value_stack + + JUMP %bb.1, implicit-def $arguments + + bb.6: + successors: + liveins: $value_stack + + %45:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %45, %13, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 64, addrspace 1) + + bb.7: + successors: %bb.8(0x80000000) + liveins: $value_stack + + %14:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %15:gpr = CONST_I256 i256 0, implicit-def dead $arguments + %57:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %56:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = OR %4, %56, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %37:gpr = GT %12, %57, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %55:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %11:gpr = OR %2, %55, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = OR %5, %11, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.8.conditional_rt_181_join_block: + successors: %bb.3(0x80000000), %bb.11(0x00000000) + liveins: $value_stack + + %13:gpr = COPY_I256 %15, implicit-def $arguments + %58:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + MSTORE %58, %8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack :: (store (s256) into `ptr addrspace(1) null`, align 1, addrspace 1) + JUMPI %bb.3, %37, implicit-def $arguments + + bb.11: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + +... From e53bc850df9e5630028e80c8551fecdff39b0dc9 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 20 Jun 2025 15:48:40 +0200 Subject: [PATCH 137/203] [EVM] Don't spill regs whose defs are unreachable Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 26 +- llvm/lib/Target/EVM/EVMStackSolver.h | 4 + .../CodeGen/EVM/bps-spill-unreachable-arg.mir | 231 ++++++++++++++++- .../CodeGen/EVM/bps-spill-unreachable-ret.mir | 234 +++++++++++++++++- 4 files changed, 482 insertions(+), 13 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 6334d3a38404..21b5c30fb0ff 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -276,6 +276,30 @@ void EVMStackSolver::calculateSpillWeights() { IsSpillWeightsCalculated = true; } +bool EVMStackSolver::hasUnreachableDef(const Register &Reg) const { + for (const auto &MI : MF.getRegInfo().def_instructions(Reg)) { + int Depth = -1; + if (MI.getOpcode() == EVM::ARGUMENT) { + // For function arguments, first operand represents the index of the + // argument on the stack, so we can use it to determine the depth. + Depth = MI.getOperand(1).getImm(); + } else { + for (auto [DefIdx, Def] : enumerate(reverse(MI.defs()))) { + if (Def.getReg() == Reg) { + Depth = DefIdx; + break; + } + } + } + assert(Depth >= 0 && "Register not found in the instruction defs."); + + // If the def is deeper than the stack depth limit, it is unreachable. + if (static_cast(Depth + 1) > StackModel.stackDepthLimit()) + return true; + } + return false; +} + EVMStackSolver::UnreachableSlotVec EVMStackSolver::getUnreachableSlots() const { UnreachableSlotVec UnreachableSlots; const unsigned StackDepthLimit = StackModel.stackDepthLimit(); @@ -413,7 +437,7 @@ void EVMStackSolver::run() { SmallSetVector SpillableRegs; for (unsigned I = Idx, E = StackSlots.size(); I < E; ++I) if (const auto *RegSlot = dyn_cast(StackSlots[I])) - if (!RegSlot->isSpill()) + if (!RegSlot->isSpill() && !hasUnreachableDef(RegSlot->getReg())) SpillableRegs.insert(RegSlot->getReg()); if (!SpillableRegs.empty()) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h index 5bd35c232192..68d1a7a5dc30 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.h +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -107,6 +107,10 @@ class EVMStackSolver { /// spill when we have multiple spillable registers. void calculateSpillWeights(); + /// Return true if any definition of \p Reg is unreachable. This can happen + /// for function arguments and return values. + bool hasUnreachableDef(const Register &Reg) const; + MachineFunction &MF; EVMStackModel &StackModel; const MachineLoopInfo *MLI; diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir index 41ce72ecc754..9eb57fec1909 100644 --- a/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir @@ -1,15 +1,16 @@ -# RUN: not --crash llc -x mir -run-pass=evm-backward-propagation-stackification < %s 2>&1 | FileCheck %s +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s -# Test that stackification fails when unreachable register is spilled. - -# CHECK: Unexpected stack depth +# Test that regs (%12:gpr, %13:gpr, %15:gpr, %16:gpr) that have unreachable defs +# are not taken into account when spilling, and we chose other reachable register +# to spill, so we compile successfully. If we chose one of them, we would hit an +# assert, since we would exceed the depth of DUP instruction. --- | source_filename = "test.ll" target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" - ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 define fastcc void @main(i256 %unused, i256 %unused1, i256 %unused2, i256 %unused3, i256 %unused4, i256 %unused5, i256 %unused6, i256 %unused7, i256 %unused8, i256 %unused9, i256 %unused10, i256 %unused11, i256 %unused12, i256 %calldata_load_result2781, i256 %addition_result2798, i256 %shift_left_non_overflow_result3390, i256 %calldata_load_result1899, i256 %calldata_load_result3245, i1 %comparison_result3399.not, i256 %stack_var_012.36954, i256 %calldata_load_result2605, i1 %comparison_result4030.not) { @@ -57,7 +58,6 @@ br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" } - ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } @@ -165,6 +165,225 @@ machineFunctionInfo: numberOfParameters: 22 hasPushDeployAddress: false body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: + ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x00000000), %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.6 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x7c000000), %bb.8(0x04000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: CALLDATACOPY_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.12 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.9(0x80000000), %bb.10(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: ULT_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.10 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12.conditional_rt_181_join_block: + ; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.13(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PseudoJUMPI %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 bb.0.entry: successors: %bb.1(0x80000000) liveins: $arguments, $value_stack diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir index 4513620b3ae6..2f5f8f871c04 100644 --- a/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir @@ -1,15 +1,16 @@ -# RUN: not --crash llc -x mir -run-pass=evm-backward-propagation-stackification < %s 2>&1 | FileCheck %s +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s -# Test that stackification fails when unreachable register is spilled. - -# CHECK: Unexpected stack depth +# Test that regs (%1:gpr, %2:gpr, %4:gpr, %5:gpr) that have unreachable defs +# are not taken into account when spilling, and we chose other reachable register +# to spill, so we compile successfully. If we chose one of them, we would hit an +# assert, since we would exceed the depth of DUP instruction. --- | source_filename = "test.ll" target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" - ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.memcpy.p1.p2.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 declare { i1, i256, i256, i1, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256, i256 } @foo() @@ -69,7 +70,6 @@ br i1 %comparison_result4030.not, label %"block_rt_188/0", label %"block_rt_2/0" } - ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) declare void @llvm.evm.memcpyas1as2(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(2) noalias nocapture readonly, i256, i1 immarg) #0 attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } @@ -190,6 +190,228 @@ machineFunctionInfo: numberOfParameters: 0 hasPushDeployAddress: false body: | + ; CHECK-LABEL: name: main + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH_LABEL + ; CHECK-NEXT: PseudoCALL @foo, + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: + ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.11 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x00000000), %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP13_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.6 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.7(0x7c000000), %bb.8(0x04000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: DUP5_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: CALLDATACOPY_S + ; CHECK-NEXT: AND_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP14_S + ; CHECK-NEXT: SWAP15_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.12 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8: + ; CHECK-NEXT: successors: %bb.9(0x80000000), %bb.10(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: DUP7_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: ULT_S + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.10 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9: + ; CHECK-NEXT: successors: %bb.1(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: SWAP10_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMP %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.10: + ; CHECK-NEXT: successors: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.11: + ; CHECK-NEXT: successors: %bb.12(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP10_S + ; CHECK-NEXT: SWAP9_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP9_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP6_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: DUP8_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.12.conditional_rt_181_join_block: + ; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.13(0x00000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SWAP16_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PseudoJUMPI %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.13: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 bb.0.entry: successors: %bb.1(0x80000000) liveins: $arguments, $value_stack From b2c29dad83f63cd50ed2f4aa0d792fdf0dcd62a1 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 20 Jun 2025 17:24:42 +0200 Subject: [PATCH 138/203] [EVM] Add tests on how spills are propagated This commit also adds -evm-force-reg-spills option that will make easier to test certain parts of the spills mechanism. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 15 ++ .../CodeGen/EVM/bps-spill-propagation-1.mir | 177 ++++++++++++++++++ .../CodeGen/EVM/bps-spill-propagation-2.mir | 164 ++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir create mode 100644 llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 21b5c30fb0ff..541f0761985e 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -32,6 +32,13 @@ static cl::opt MaxSpillIterations( cl::desc("Maximum number of iterations to spill stack slots " "to avoid stack too deep issues.")); +#ifndef NDEBUG +static cl::list + ForceRegSpills("evm-force-reg-spills", + llvm::cl::desc("Force spilling of the specified registers."), + cl::CommaSeparated, cl::Hidden); +#endif // NDEBUG + namespace { /// \return index of \p Item in \p RangeOrContainer or std::nullopt. @@ -372,6 +379,14 @@ EVMStackSolver::UnreachableSlotVec EVMStackSolver::getUnreachableSlots() const { } void EVMStackSolver::run() { +#ifndef NDEBUG + SmallSet ForceSpillRegs; + for (auto Reg : ForceRegSpills) + ForceSpillRegs.insert(Register::index2VirtReg(Reg)); + if (!ForceSpillRegs.empty()) + StackModel.addSpillRegs(ForceSpillRegs); +#endif // NDEBUG + unsigned IterCount = 0; while (true) { runPropagation(); diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir new file mode 100644 index 000000000000..066bccfb1815 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-1.mir @@ -0,0 +1,177 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=2 < %s | FileCheck %s + +# In bb.0 we have def and use of %2:gpr and two uses in bb.2. +# When spilling %2:gpr, test that in bb.0 we are not doing reload +# immediately after spill, and we are doing only one reload in bb.2, +# even there are two uses of %2:gpr in bb.2. + +--- | + source_filename = "test_rename.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg) { + bb: + %icmp = icmp sgt i256 %arg, 0 + br i1 %icmp, label %bb1, label %bb4 + + bb1: ; preds = %bb + %xor = xor i256 %arg, 10 + %or = or i256 %arg, 1234 + %add = add nuw nsw i256 %or, 5 + %add2 = add nuw nsw i256 %add, %xor + br label %bb4 + + bb4: ; preds = %bb, %bb1 + %phi = phi i256 [ %add2, %bb1 ], [ 10, %bb ] + ret i256 %phi + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 1 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.bb: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.bb1: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 5 + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PUSH2_S i256 1234 + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: XOR_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: OR_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.bb4: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoRET + bb.0.bb: + successors: %bb.2(0x50000000), %bb.1(0x30000000) + liveins: $arguments, $value_stack + + %2:gpr = ARGUMENT 0, implicit $arguments + %4:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = LT %2, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.2, %5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.1: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %12:gpr = CONST_I256 i256 10, implicit-def dead $arguments + JUMP %bb.3, implicit-def $arguments + + bb.2.bb1: + successors: %bb.3(0x80000000) + liveins: $value_stack + + %11:gpr = CONST_I256 i256 5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = CONST_I256 i256 10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = XOR %2, %6, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = CONST_I256 i256 1234, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = OR %2, %8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %10:gpr = nuw ADD %9, %7, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = nuw ADD %10, %11, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.3.bb4: + liveins: $value_stack + + RET %12, implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir new file mode 100644 index 000000000000..700bd9166223 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-2.mir @@ -0,0 +1,164 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=9 < %s | FileCheck %s + +# Test that when spilling %9:gpr, we are not doing DUP1 before and +# POP after spill, since %9:gpr is not used anymore in these MBBs. + +--- | + source_filename = "test_rename.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define dso_local i256 @test(i256 noundef %arg) local_unnamed_addr { + bb: + %icmp = icmp sgt i256 %arg, 0 + %add = add nuw nsw i256 %arg, 10 + br i1 %icmp, label %bb2, label %bb1 + + bb1: ; preds = %bb + br label %bb2 + + bb2: ; preds = %bb1, %bb + %phi = phi i256 [ 10, %bb1 ], [ %add, %bb ] + %ret = add nuw nsw i256 %phi, 5 + ret i256 %ret + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 1 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.bb: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: DUP2_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: PseudoJUMPI %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: successors: %bb.3(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.bb2: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 5 + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoRET + bb.0.bb: + successors: %bb.1(0x50000000), %bb.3(0x30000000) + liveins: $arguments, $value_stack + + %3:gpr = ARGUMENT 0, implicit $arguments + %9:gpr = CONST_I256 i256 10, implicit-def dead $arguments + %4:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = GT %3, %4, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMPI %bb.1, %5, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.3: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.1: + successors: %bb.2(0x80000000) + liveins: $value_stack + + %10:gpr = CONST_I256 i256 10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = nuw nsw ADD %3, %10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.2.bb2: + liveins: $value_stack + + %7:gpr = CONST_I256 i256 5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %8:gpr = nuw nsw ADD %9, %7, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + RET %8, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + +... From c2f79c3dfe268cc84807df4f54ae7db58d444d72 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 23 Jun 2025 16:07:47 +0200 Subject: [PATCH 139/203] [EVM] Add pre-commit test for Don't propagate cond reg reloads Add test that shows that conditional register reload is propagated through the MBB's predecessors. Signed-off-by: Vladimir Radosavljevic --- .../CodeGen/EVM/bps-spill-propagation-3.mir | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir new file mode 100644 index 000000000000..b7f6cb2768ad --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir @@ -0,0 +1,265 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=0 < %s | FileCheck %s + +# Test how we are propagating conditional register reloads. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg1, i256 %arg2, i256 %arg3) { + entry: + %cmp1 = icmp sgt i256 %arg1, 0 + %cmp2 = icmp sgt i256 %arg3, 0 + %zext = zext i1 %cmp2 to i256 + %sub = sub nuw nsw i256 %arg2, %zext + br i1 %cmp1, label %bb1, label %bb2 + + bb1: ; preds = %entry + %cmp3 = icmp sgt i256 %arg2, 0 + br i1 %cmp3, label %bb2, label %bb3 + + bb2: ; preds = %bb1, %entry + br i1 %cmp2, label %bb3, label %bb4 + + bb3: ; preds = %bb2, %bb1 + %add1 = add nuw nsw i256 %arg1, 10 + br label %bb4 + + bb4: ; preds = %bb3, %bb2 + %phi1 = phi i256 [ %add1, %bb3 ], [ %sub, %bb2 ] + ret i256 %phi1 + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 3 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: GT_S + ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: SUB_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: PseudoJUMP %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.bb1: + ; CHECK-NEXT: successors: %bb.4(0x50000000), %bb.3(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.bb2: + ; CHECK-NEXT: successors: %bb.7(0x50000000), %bb.6(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoJUMPI %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.9 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8.bb3: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9.bb4: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoRET + bb.0.entry: + successors: %bb.1(0x50000000), %bb.5(0x30000000) + liveins: $arguments, $value_stack + + %4:gpr = ARGUMENT 0, implicit $arguments + %5:gpr = ARGUMENT 1, implicit $arguments + %7:gpr = ARGUMENT 2, implicit $arguments + %6:gpr = CONST_I256 i256 0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %0:gpr = GT %7, %6, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %13:gpr = nuw nsw SUB %5, %0, implicit-def dead $arguments + %14:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = LT %4, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.1, %9, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.5: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.1.bb1: + successors: %bb.6(0x50000000), %bb.7(0x30000000) + liveins: $value_stack + + %15:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %11:gpr = LT %5, %15, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %11, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.7: + successors: %bb.3(0x80000000) + liveins: $value_stack + + JUMP %bb.3, implicit-def $arguments + + bb.6: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2.bb2: + successors: %bb.8(0x50000000), %bb.9(0x30000000) + liveins: $value_stack + + JUMPI %bb.8, %0, implicit-def $arguments + + bb.9: + successors: %bb.4(0x80000000) + liveins: $value_stack + + JUMP %bb.4, implicit-def $arguments + + bb.8: + successors: %bb.3(0x80000000) + liveins: $value_stack + + bb.3.bb3: + successors: %bb.4(0x80000000) + liveins: $value_stack + + %12:gpr = CONST_I256 i256 10, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %13:gpr = nuw nsw ADD %4, %12, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + + bb.4.bb4: + liveins: $value_stack + + RET %13, implicit-def dead $arguments + +... From b88fa9b0bde8d750fa0001b1ab863bc4f2895de6 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 20 Jun 2025 18:09:03 +0200 Subject: [PATCH 140/203] [EVM] Don't propagate cond reg reloads When condition register is reloaded, there is no need to propagate it through a MBB if there is no use or def in the MBB. This will reduce stack pressure, and will reduce code size, since in some cases, reload is propagated through the predecessors, thus doing more reloads than it is needed. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 41 +++++++++++++++---- .../CodeGen/EVM/bps-spill-propagation-3.mir | 7 +--- .../CodeGen/EVM/bps-spill-unreachable-arg.mir | 15 +++---- .../CodeGen/EVM/bps-spill-unreachable-ret.mir | 15 +++---- 4 files changed, 46 insertions(+), 32 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 541f0761985e..003de075b7ce 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -468,12 +468,18 @@ void EVMStackSolver::run() { } } -// Return true if the register is defined or used before MI. -static bool isRegDefOrUsedBefore(const MachineInstr &MI, const Register &Reg) { - return std::any_of(std::next(MachineBasicBlock::const_reverse_iterator(MI)), - MI.getParent()->rend(), [&Reg](const MachineInstr &Instr) { - return Instr.readsRegister(Reg, /*TRI*/ nullptr) || - Instr.definesRegister(Reg, /*TRI=*/nullptr); +/// Return true if \p Slot is a spillable register and it has no use or +/// definition before \p MI. +static bool spillRegHasNoUseOrDefBefore(const StackSlot *Slot, + const MachineInstr &MI) { + if (!isSpillReg(Slot)) + return false; + + auto Reg = cast(Slot)->getReg(); + return std::all_of(std::next(MachineBasicBlock::const_reverse_iterator(MI)), + MI.getParent()->rend(), [Reg](const MachineInstr &Instr) { + return !Instr.readsRegister(Reg, /*TRI*/ nullptr) && + !Instr.definesRegister(Reg, /*TRI=*/nullptr); }); } @@ -510,8 +516,7 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, // MI. They are not cheap and ideally we shouldn't do more than one reload // in the BB. if (BackSlot->isRematerializable() || - (isSpillReg(BackSlot) && - !isRegDefOrUsedBefore(MI, cast(BackSlot)->getReg()))) { + spillRegHasNoUseOrDefBefore(BackSlot, MI)) { BeforeMI.pop_back(); } else if (auto Offset = offset(drop_begin(reverse(BeforeMI), 1), BackSlot)) { @@ -611,6 +616,9 @@ void EVMStackSolver::runPropagation() { // Get the MBB exit stack. std::optional ExitStack = std::nullopt; + // True if we need to pop the spilled condition from propagating it + // through the MBB. + bool PopSpilledCond = false; auto [BranchTy, TBB, FBB, BrInsts, Condition] = getBranchInfo(MBB); switch (BranchTy) { @@ -663,8 +671,16 @@ void EVMStackSolver::runPropagation() { StackModel.getMBBEntryStack(FBB)); // Retain the jump condition in ExitStack because StackSolver doesn't // propagate stacks through branch instructions. - CombinedStack.emplace_back(StackModel.getStackSlot(*Condition)); + const auto *CondSlot = StackModel.getStackSlot(*Condition); + CombinedStack.emplace_back(CondSlot); ExitStack = std::move(CombinedStack); + + // If the condition is spilled, we should not propagate it through the + // MBB, if there is no use or def of the condition. This should reduce + // the stack pressure if the condition is not used in the MBB. + if (spillRegHasNoUseOrDefBefore(CondSlot, + *BrInsts[BrInsts.size() - 1])) + PopSpilledCond = true; } } } @@ -672,6 +688,13 @@ void EVMStackSolver::runPropagation() { if (ExitStack) { Visited.insert(MBB); insertMBBExitStack(MBB, *ExitStack); + if (PopSpilledCond) { + assert(ExitStack->back() == + StackModel.getRegisterSlot(Condition->getReg()) && + "Condition slot should be on top of the exit stack."); + ExitStack->pop_back(); + } + insertMBBEntryStack( MBB, propagateThroughMBB(*ExitStack, MBB, ForceCompressStack)); append_range(Worklist, MBB->predecessors()); diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir index b7f6cb2768ad..d2fa395aa532 100644 --- a/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-3.mir @@ -133,8 +133,6 @@ body: | ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: POP_S - ; CHECK-NEXT: PUSH_FRAME %stack.0 - ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: PseudoJUMP %bb.5 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.2.bb1: @@ -160,13 +158,12 @@ body: | ; CHECK-NEXT: successors: %bb.5(0x80000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: PUSH_FRAME %stack.0 - ; CHECK-NEXT: MLOAD_S - ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.5.bb2: ; CHECK-NEXT: successors: %bb.7(0x50000000), %bb.6(0x30000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: PseudoJUMPI %bb.7 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.6: diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir index 9eb57fec1909..2523d2bea559 100644 --- a/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-arg.mir @@ -187,29 +187,28 @@ body: | ; CHECK-NEXT: POP_S ; CHECK-NEXT: SWAP3_S ; CHECK-NEXT: SWAP2_S - ; CHECK-NEXT: SWAP1_S ; CHECK-NEXT: SWAP6_S ; CHECK-NEXT: SWAP4_S - ; CHECK-NEXT: SWAP5_S + ; CHECK-NEXT: SWAP1_S ; CHECK-NEXT: SWAP7_S ; CHECK-NEXT: PUSH1_S i256 1 ; CHECK-NEXT: AND_S - ; CHECK-NEXT: DUP1_S ; CHECK-NEXT: PUSH_FRAME %stack.0 ; CHECK-NEXT: MSTORE_S - ; CHECK-NEXT: SWAP5_S ; CHECK-NEXT: PUSH1_S i256 1 ; CHECK-NEXT: AND_S - ; CHECK-NEXT: SWAP6_S - ; CHECK-NEXT: PUSH0_S - ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP5_S ; CHECK-NEXT: PUSH0_S ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP6_S ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: PseudoJUMPI %bb.11 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.2: @@ -311,8 +310,6 @@ body: | ; CHECK-NEXT: SWAP9_S ; CHECK-NEXT: SWAP10_S ; CHECK-NEXT: POP_S - ; CHECK-NEXT: PUSH_FRAME %stack.0 - ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: PseudoJUMP %bb.1 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.10: diff --git a/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir index 2f5f8f871c04..c504d024f6ed 100644 --- a/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir +++ b/llvm/test/CodeGen/EVM/bps-spill-unreachable-ret.mir @@ -216,28 +216,27 @@ body: | ; CHECK-NEXT: SWAP1_S ; CHECK-NEXT: SWAP4_S ; CHECK-NEXT: SWAP3_S - ; CHECK-NEXT: SWAP2_S ; CHECK-NEXT: SWAP7_S ; CHECK-NEXT: SWAP5_S - ; CHECK-NEXT: SWAP6_S + ; CHECK-NEXT: SWAP2_S ; CHECK-NEXT: SWAP8_S ; CHECK-NEXT: AND_S - ; CHECK-NEXT: DUP1_S ; CHECK-NEXT: PUSH_FRAME %stack.0 ; CHECK-NEXT: MSTORE_S - ; CHECK-NEXT: SWAP5_S ; CHECK-NEXT: PUSH1_S i256 1 ; CHECK-NEXT: AND_S - ; CHECK-NEXT: SWAP6_S - ; CHECK-NEXT: PUSH0_S - ; CHECK-NEXT: SWAP8_S + ; CHECK-NEXT: SWAP5_S ; CHECK-NEXT: PUSH0_S ; CHECK-NEXT: SWAP7_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: SWAP6_S ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.1.conditional_rt_187_join_block: ; CHECK-NEXT: successors: %bb.11(0x80000000), %bb.2(0x00000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: PseudoJUMPI %bb.11 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.2: @@ -339,8 +338,6 @@ body: | ; CHECK-NEXT: SWAP9_S ; CHECK-NEXT: SWAP10_S ; CHECK-NEXT: POP_S - ; CHECK-NEXT: PUSH_FRAME %stack.0 - ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: PseudoJUMP %bb.1 ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.10: From b758a3dcb7d93d45cb0fa83b8f5c68ca5c02fada Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 24 Jun 2025 13:48:56 +0200 Subject: [PATCH 141/203] [EVM] Add pre-commit test for Change how we propagate spilled second operand of commutable inst Signed-off-by: Vladimir Radosavljevic --- .../CodeGen/EVM/bps-spill-propagation-4.mir | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir new file mode 100644 index 000000000000..071393bf351e --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir @@ -0,0 +1,260 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# REQUIRES: asserts +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification -evm-force-reg-spills=5 < %s | FileCheck %s + +# Test how we are propagating second operand of commutable instruction that is spilled. + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @test(i256 %arg1, i256 %arg2, i256 %arg3, i256 %arg4) { + entry: + %cmp1 = icmp sgt i256 %arg1, 0 + br i1 %cmp1, label %bb1, label %bb2 + + bb1: ; preds = %entry + %cmp3 = icmp sgt i256 %arg2, 0 + br i1 %cmp3, label %bb2, label %bb3 + + bb2: ; preds = %bb1, %entry + %cmp2 = icmp sgt i256 %arg3, 0 + br i1 %cmp2, label %bb3, label %bb4 + + bb3: ; preds = %bb2, %bb1 + %add1 = add nuw nsw i256 %arg1, %arg4 + br label %bb4 + + bb4: ; preds = %bb3, %bb2 + %phi1 = phi i256 [ %add1, %bb3 ], [ 10, %bb2 ] + ret i256 %phi1 + } + +... +--- +name: test +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 4 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: test + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.2(0x50000000), %bb.1(0x30000000) + ; CHECK-NEXT: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: DUP4_S + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MSTORE_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.bb1: + ; CHECK-NEXT: successors: %bb.4(0x50000000), %bb.3(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.8 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.bb2: + ; CHECK-NEXT: successors: %bb.7(0x50000000), %bb.6(0x30000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH1_S i256 1 + ; CHECK-NEXT: PUSH1_S i256 10 + ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: LT_S + ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: PseudoJUMP %bb.9 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.7: + ; CHECK-NEXT: successors: %bb.8(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP3_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.8.bb3: + ; CHECK-NEXT: successors: %bb.9(0x80000000) + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.9.bb4: + ; CHECK-NEXT: liveins: $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PseudoRET + bb.0.entry: + successors: %bb.1(0x50000000), %bb.5(0x30000000) + liveins: $arguments, $value_stack + + %2:gpr = ARGUMENT 0, implicit $arguments + %3:gpr = ARGUMENT 1, implicit $arguments + %4:gpr = ARGUMENT 2, implicit $arguments + %5:gpr = ARGUMENT 3, implicit $arguments + %14:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %7:gpr = LT %2, %14, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.1, %7, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.5: + successors: %bb.2(0x80000000) + liveins: $value_stack + + JUMP %bb.2, implicit-def $arguments + + bb.1.bb1: + successors: %bb.6(0x50000000), %bb.7(0x30000000) + liveins: $value_stack + + %15:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %9:gpr = LT %3, %15, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.6, %9, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.7: + successors: %bb.3(0x80000000) + liveins: $value_stack + + JUMP %bb.3, implicit-def $arguments + + bb.6: + successors: %bb.2(0x80000000) + liveins: $value_stack + + bb.2.bb2: + successors: %bb.8(0x50000000), %bb.9(0x30000000) + liveins: $value_stack + + %13:gpr = CONST_I256 i256 10, implicit-def dead $arguments + %16:gpr = CONST_I256 i256 1, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %12:gpr = LT %4, %16, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + JUMP_UNLESS %bb.8, %12, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + + bb.9: + successors: %bb.4(0x80000000) + liveins: $value_stack + + JUMP %bb.4, implicit-def $arguments + + bb.8: + successors: %bb.3(0x80000000) + liveins: $value_stack + + bb.3.bb3: + successors: %bb.4(0x80000000) + liveins: $value_stack + + %13:gpr = nuw nsw ADD %2, %5, implicit-def dead $arguments + + bb.4.bb4: + liveins: $value_stack + + RET %13, implicit-def dead $arguments + +... From 039d04cf701eee58457604a7c4105f44f12a0431 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 24 Jun 2025 09:13:31 +0200 Subject: [PATCH 142/203] [EVM] Change how we propagate spilled second operand of commutable inst In case second operand of commutable inst is spilled, don't propagate it if there are no use or def before. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 9 +++++++++ .../CodeGen/EVM/bps-spill-propagation-4.mir | 18 ++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 003de075b7ce..426325044a69 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -508,6 +508,15 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, LLVM_DEBUG({ dbgs() << "\tstack before: " << BeforeMI.toString() << ".\n"; }); + // If this is a commutable instruction, and second operand is spilled and + // has no use or def before MI, we can remove it from the stack. This should + // reduce the stack pressure and in some cases, it should reduce code size. + if (MI.isCommutable() && + spillRegHasNoUseOrDefBefore(BeforeMI[BeforeMI.size() - 2], MI)) { + std::swap(BeforeMI[BeforeMI.size() - 1], BeforeMI[BeforeMI.size() - 2]); + BeforeMI.pop_back(); + } + // If the top stack slots can be rematerialized just before MI, remove it // from propagation to reduce pressure. while (!BeforeMI.empty()) { diff --git a/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir b/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir index 071393bf351e..f6aa12037fbd 100644 --- a/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir +++ b/llvm/test/CodeGen/EVM/bps-spill-propagation-4.mir @@ -113,12 +113,10 @@ body: | ; CHECK-NEXT: DUP4_S ; CHECK-NEXT: PUSH_FRAME %stack.0 ; CHECK-NEXT: MSTORE_S - ; CHECK-NEXT: SWAP2_S - ; CHECK-NEXT: SWAP4_S ; CHECK-NEXT: SWAP3_S ; CHECK-NEXT: POP_S ; CHECK-NEXT: PUSH1_S i256 1 - ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: DUP4_S ; CHECK-NEXT: LT_S ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.2 ; CHECK-NEXT: {{ $}} @@ -142,10 +140,6 @@ body: | ; CHECK-NEXT: successors: %bb.8(0x80000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: PUSH_FRAME %stack.0 - ; CHECK-NEXT: MLOAD_S - ; CHECK-NEXT: SWAP2_S - ; CHECK-NEXT: SWAP3_S ; CHECK-NEXT: POP_S ; CHECK-NEXT: PseudoJUMP %bb.8 ; CHECK-NEXT: {{ $}} @@ -159,7 +153,7 @@ body: | ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: PUSH1_S i256 1 ; CHECK-NEXT: PUSH1_S i256 10 - ; CHECK-NEXT: SWAP4_S + ; CHECK-NEXT: SWAP2_S ; CHECK-NEXT: LT_S ; CHECK-NEXT: PseudoJUMP_UNLESS %bb.7 ; CHECK-NEXT: {{ $}} @@ -167,6 +161,8 @@ body: | ; CHECK-NEXT: successors: %bb.9(0x80000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: SWAP1_S ; CHECK-NEXT: POP_S ; CHECK-NEXT: PseudoJUMP %bb.9 ; CHECK-NEXT: {{ $}} @@ -174,16 +170,14 @@ body: | ; CHECK-NEXT: successors: %bb.8(0x80000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: PUSH_FRAME %stack.0 - ; CHECK-NEXT: MLOAD_S - ; CHECK-NEXT: SWAP2_S - ; CHECK-NEXT: SWAP3_S ; CHECK-NEXT: POP_S ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: bb.8.bb3: ; CHECK-NEXT: successors: %bb.9(0x80000000) ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH_FRAME %stack.0 + ; CHECK-NEXT: MLOAD_S ; CHECK-NEXT: ADD_S ; CHECK-NEXT: SWAP1_S ; CHECK-NEXT: {{ $}} From 3109a73f958803d62435caf5dcabfe129b91918c Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 24 Jun 2025 14:39:11 +0200 Subject: [PATCH 143/203] [EVM] Improve description in EVMMarkRecursiveFunctions Add comment that we don't need to check functions for unknown calls, since all functions are known at compile time. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp b/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp index aa0cfebce24e..a8185222a4a0 100644 --- a/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp +++ b/llvm/lib/Target/EVM/EVMMarkRecursiveFunctions.cpp @@ -7,9 +7,12 @@ //===----------------------------------------------------------------------===// // // This pass marks all recursive functions in the module with the -// "evm-recursive" attribute by analyzing the call graph. This is needed during -// stackification, since we can't use spills for recursive functions, as we are -// using memory for spills, and not the real stack. +// "evm-recursive" attribute by analyzing the call graph. We dont' need to check +// functions to detect unknown calls (as it is implemented in FunctionAttrs.cpp, +// function createSCCNodeSet), because all functions are known at compile time, +// and we don't have any indirect calls. +// This is needed during stackification, since we can't use spills for recursive +// functions, as we are using memory for spills, and not the real stack. // //===----------------------------------------------------------------------===// From d9b1acd4e9b2b745c6c2c5e834f92763bee65ca9 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Mon, 23 Jun 2025 18:59:52 +0200 Subject: [PATCH 144/203] [EVM] Add mechanism for informing the front-end (FE) of the required spill region size This update adds support for notifying the front-end (FE) about the spill region size required by the stackifier. It introduces the following components: 1. A new C API allows the FE to register a handler that the stackifier calls when a stack-too-deep (STD) error occurs typedef void (*LLVMStackErrorHandlerEVM)(uint64_t SpillRegionSize); void LLVMInstallEVMStackErrorHandler(LLVMStackErrorHandlerEVM Handler); 2. Function for triggering the handler. The stackifier can invoke the registered handler using: void report_evm_stack_error(uint64_t spill_region_size); Spill/Reload Workflow. - FE sets up a stack error handler: void on_std(uint64_t SpillRegionSize) { // Bookkeeping for SpillRegionSize exit(1); } - The FE compiles the LLVM IR normally, without specifying spill-related options - If no STD error is encountered, compilation completes successfully and object code is generated - If a stack-too-deep error occurs: - The 'EVMFinalizeStackFrames' pass calls 'report_evm_stack_error(size)' with the required spill region size - The FE captures and stores this size, gracefully terminating the compilation thread/process - The FE updates the memory layout and restarts the compilation process, this time supplying: - 'StackRegionSize' - the allocated spill region size - 'StackRegionOffset' - the memory start address of the allocated spill region --- llvm/include/llvm-c/ErrorHandling.h | 20 +++++++ llvm/include/llvm/Support/ErrorHandling.h | 13 ++++ llvm/lib/Support/ErrorHandling.cpp | 59 +++++++++++++++++++ .../lib/Target/EVM/EVMFinalizeStackFrames.cpp | 11 ++-- llvm/unittests/Target/EVM/CMakeLists.txt | 1 + llvm/unittests/Target/EVM/SpillRegion.cpp | 34 +++++++++++ 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 llvm/unittests/Target/EVM/SpillRegion.cpp diff --git a/llvm/include/llvm-c/ErrorHandling.h b/llvm/include/llvm-c/ErrorHandling.h index d9b9f22752b8..529cab6b0c9f 100644 --- a/llvm/include/llvm-c/ErrorHandling.h +++ b/llvm/include/llvm-c/ErrorHandling.h @@ -15,6 +15,9 @@ #define LLVM_C_ERRORHANDLING_H #include "llvm-c/ExternC.h" +// EVM local beging +#include +// EVM local end LLVM_C_EXTERN_C_BEGIN @@ -26,6 +29,10 @@ LLVM_C_EXTERN_C_BEGIN typedef void (*LLVMFatalErrorHandler)(const char *Reason); +// EVM local begin +typedef void (*LLVMStackErrorHandlerEVM)(uint64_t SpillRegionSize); +// EVM local end + /** * Install a fatal error handler. By default, if LLVM detects a fatal error, it * will call exit(1). This may not be appropriate in many contexts. For example, @@ -48,6 +55,19 @@ void LLVMResetFatalErrorHandler(void); */ void LLVMEnablePrettyStackTrace(void); +// EVM local begin +/** + * Register a handler that the stackification algorithm invokes when it + * requires a pre-allocated spill region. + */ +void LLVMInstallEVMStackErrorHandler(LLVMStackErrorHandlerEVM Handler); + +/** + * Reset the EVM stack error handler. + */ +void LLVMResetEVMStackErrorHandler(void); +// EVM local end + /** * @} */ diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h index 9c8e3448f3a0..764e3e43789c 100644 --- a/llvm/include/llvm/Support/ErrorHandling.h +++ b/llvm/include/llvm/Support/ErrorHandling.h @@ -15,6 +15,9 @@ #define LLVM_SUPPORT_ERRORHANDLING_H #include "llvm/Support/Compiler.h" +// EVM local begin +#include +// EVM local end namespace llvm { class StringRef; @@ -25,6 +28,11 @@ namespace llvm { const char *reason, bool gen_crash_diag); + // EVM local begin + typedef void (*evm_stack_error_handler_t)(void *user_data, + uint64_t spill_region_size); + // EVM local end + /// install_fatal_error_handler - Installs a new error handler to be used /// whenever a serious (non-recoverable) error is encountered by LLVM. /// @@ -75,6 +83,11 @@ namespace llvm { [[noreturn]] void report_fatal_error(const Twine &reason, bool gen_crash_diag = true); +// EVM local begin +/// Report a stackification error and request a spill region. +void report_evm_stack_error(const Twine &reason, uint64_t spill_region_size); +// EVM local end + /// Installs a new bad alloc error handler that should be used whenever a /// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM. /// diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp index cb42e28c04a8..59a60bbc9cef 100644 --- a/llvm/lib/Support/ErrorHandling.cpp +++ b/llvm/lib/Support/ErrorHandling.cpp @@ -45,6 +45,11 @@ static void *ErrorHandlerUserData = nullptr; static fatal_error_handler_t BadAllocErrorHandler = nullptr; static void *BadAllocErrorHandlerUserData = nullptr; +// EVM local begin +static evm_stack_error_handler_t EVMStackErrorHandler = nullptr; +static void *EVMStackErrorHandlerUserData = nullptr; +// EVM local end + #if LLVM_ENABLE_THREADS == 1 // Mutexes to synchronize installing error handlers and calling error handlers. // Do not use ManagedStatic, or that may allocate memory while attempting to @@ -59,6 +64,9 @@ static void *BadAllocErrorHandlerUserData = nullptr; // builds. We can remove these ifdefs if that script goes away. static std::mutex ErrorHandlerMutex; static std::mutex BadAllocErrorHandlerMutex; +// EVM local begin +static std::mutex EVMStackErrorHandlerMutex; +// EVM local end #endif void llvm::install_fatal_error_handler(fatal_error_handler_t handler, @@ -234,6 +242,57 @@ void LLVMResetFatalErrorHandler() { remove_fatal_error_handler(); } +// EVM local begin +static void bindingsEVMStackErrorHandler(void *user_data, + uint64_t spill_region_size) { + LLVMStackErrorHandlerEVM handler = + LLVM_EXTENSION reinterpret_cast(user_data); + handler(spill_region_size); +} + +void LLVMInstallEVMStackErrorHandler(LLVMStackErrorHandlerEVM Handler) { +#if LLVM_ENABLE_THREADS == 1 + std::lock_guard Lock(EVMStackErrorHandlerMutex); +#endif + assert(!EVMStackErrorHandler && "Error handler already registered!\n"); + EVMStackErrorHandler = bindingsEVMStackErrorHandler; + EVMStackErrorHandlerUserData = + LLVM_EXTENSION reinterpret_cast(Handler); +} + +void LLVMResetEVMStackErrorHandler(void) { +#if LLVM_ENABLE_THREADS == 1 + std::lock_guard Lock(EVMStackErrorHandlerMutex); +#endif + EVMStackErrorHandler = nullptr; + EVMStackErrorHandlerUserData = nullptr; +} + +void llvm::report_evm_stack_error(const Twine &Reason, + uint64_t spillRegionSize) { + llvm::evm_stack_error_handler_t handler = nullptr; + void *handlerData = nullptr; + { + // Only acquire the mutex while reading the handler, so as not to invoke a + // user-supplied callback under a lock. +#if LLVM_ENABLE_THREADS == 1 + std::lock_guard Lock(EVMStackErrorHandlerMutex); +#endif + handler = EVMStackErrorHandler; + handlerData = EVMStackErrorHandlerUserData; + } + + if (handler) + handler(handlerData, spillRegionSize); + + // If execution reaches this point, either the front-end handler wasn't + // set up, or it returned without calling abort()/exit(). + // In either case, fall back to the default error handler. + report_fatal_error(Reason); +} + +// EVM local end + #ifdef _WIN32 #define WIN32_NO_STATUS diff --git a/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp index 05eab5f45280..7935b2ea87dd 100644 --- a/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp +++ b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp @@ -171,10 +171,13 @@ bool EVMFinalizeStackFrames::runOnModule(Module &M) { // Check if it is valid to replace frame indices. if (TotalStackSize > 0) { - if (TotalStackSize > StackRegionSize) - report_fatal_error("Total stack size: " + Twine(TotalStackSize) + - " is larger than the allocated stack region size: " + - Twine(StackRegionSize)); + if (TotalStackSize > StackRegionSize) { + report_evm_stack_error( + "Total stack size: " + Twine(TotalStackSize) + + " is larger than the allocated stack region size: " + + Twine(StackRegionSize), + TotalStackSize); + } if (StackRegionSize > TotalStackSize) errs() << "warning: allocated stack region size: " + diff --git a/llvm/unittests/Target/EVM/CMakeLists.txt b/llvm/unittests/Target/EVM/CMakeLists.txt index 315aef1b0650..ab5689adb0a8 100644 --- a/llvm/unittests/Target/EVM/CMakeLists.txt +++ b/llvm/unittests/Target/EVM/CMakeLists.txt @@ -18,6 +18,7 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_target_unittest(EVMTests + SpillRegion.cpp StackShuffler.cpp StackModel.cpp ) diff --git a/llvm/unittests/Target/EVM/SpillRegion.cpp b/llvm/unittests/Target/EVM/SpillRegion.cpp new file mode 100644 index 000000000000..101196e492d9 --- /dev/null +++ b/llvm/unittests/Target/EVM/SpillRegion.cpp @@ -0,0 +1,34 @@ +//===---------- llvm/unittests/MC/AssemblerTest.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/ErrorHandling.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +static void on_std(uint64_t SpillRegionSize) { + EXPECT_TRUE(SpillRegionSize == 32); +} + +static void on_std_exit(uint64_t SpillRegionSize) { + EXPECT_TRUE(SpillRegionSize == 64); + exit(1); +} + +class SpillRegionTest : public testing::Test {}; + +TEST_F(SpillRegionTest, Basic) { + LLVMInstallEVMStackErrorHandler(on_std); + EXPECT_DEATH(report_evm_stack_error("STD error", 32), "STD error"); + LLVMResetEVMStackErrorHandler(); + LLVMInstallEVMStackErrorHandler(on_std_exit); + EXPECT_DEATH(report_evm_stack_error("STD error", 64), ""); +} From ebeeb2029fd327b4f932ce45d9d80c85adbc3ff8 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Tue, 27 May 2025 10:28:46 +0200 Subject: [PATCH 145/203] [EVM] Pre-commit test for Improve code generation for conditional jumps --- llvm/test/CodeGen/EVM/brcond.ll | 434 ++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/brcond.ll diff --git a/llvm/test/CodeGen/EVM/brcond.ll b/llvm/test/CodeGen/EVM/brcond.ll new file mode 100644 index 000000000000..c9fafdea70da --- /dev/null +++ b/llvm/test/CodeGen/EVM/brcond.ll @@ -0,0 +1,434 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Two-value comparisons + +define void @br_eq(i256 %a, i256 %b) { +; CHECK-LABEL: br_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB0_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp eq i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ne(i256 %a, i256 %b) { +; CHECK-LABEL: br_ne: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB1_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ne i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ugt(i256 %a, i256 %b) { +; CHECK-LABEL: br_ugt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: GT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB2_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ugt i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_uge(i256 %a, i256 %b) { +; CHECK-LABEL: br_uge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: LT +; CHECK-NEXT: PUSH4 @.BB3_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB3_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp uge i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ult(i256 %a, i256 %b) { +; CHECK-LABEL: br_ult: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB4_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB4_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ult i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ule(i256 %a, i256 %b) { +; CHECK-LABEL: br_ule: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: GT +; CHECK-NEXT: PUSH4 @.BB5_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB5_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ule i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sgt(i256 %a, i256 %b) { +; CHECK-LABEL: br_sgt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB6_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB6_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sgt i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sge(i256 %a, i256 %b) { +; CHECK-LABEL: br_sge: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB7_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB7_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sge i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_slt(i256 %a, i256 %b) { +; CHECK-LABEL: br_slt: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB8_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB8_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp slt i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sle(i256 %a, i256 %b) { +; CHECK-LABEL: br_sle: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB9_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB9_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sle i256 %a, %b + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +; One-value comparisons with zero + +define void @br_eq_0(i256 %a) { +; CHECK-LABEL: br_eq_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB10_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB10_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp eq i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ne_0(i256 %a) { +; CHECK-LABEL: br_ne_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB11_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB11_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ne i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ugt_0(i256 %a) { +; CHECK-LABEL: br_ugt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB12_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB12_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ugt i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_uge_0(i256 %a) { +; CHECK-LABEL: br_uge_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: PUSH4 @.BB13_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB13_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp uge i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ult_0(i256 %a) { +; CHECK-LABEL: br_ult_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH4 @.BB14_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB14_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ult i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_ule_0(i256 %a) { +; CHECK-LABEL: br_ule_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB15_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB15_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp ule i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sgt_0(i256 %a) { +; CHECK-LABEL: br_sgt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB16_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB16_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sgt i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sge_0(i256 %a) { +; CHECK-LABEL: br_sge_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB17_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB17_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sge i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_slt_0(i256 %a) { +; CHECK-LABEL: br_slt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB18_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB18_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp slt i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + +define void @br_sle_0(i256 %a) { +; CHECK-LABEL: br_sle_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB19_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %true +; CHECK-NEXT: .BB19_2: ; %false +; CHECK-NEXT: JUMPDEST + %cond = icmp sle i256 %a, 0 + br i1 %cond, label %true, label %false +true: + unreachable +false: + unreachable +} + From 60fc6458d428758dc5641187b365347e49c519f6 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 21 May 2025 01:13:18 +0200 Subject: [PATCH 146/203] [EVM] Improve code generation for conditional jumps The patch introduces a few SelectionDAG patterns to improve codegen for the `br (brcond (setcc))` case. Additionally, due to the late expansion of JUMP_UNLESS, the patch introduces a peephole pass that optimizes JUMPI predicates: * `ISZERO ISZERO` is folded to nothing * `EQ ISZERO` is folded to `SUB` * `SUB ISZERO` is folded to `EQ` --- llvm/lib/Target/EVM/CMakeLists.txt | 7 +- llvm/lib/Target/EVM/EVMInstrInfo.td | 11 +- llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp | 79 +++++++++++--- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 6 +- llvm/test/CodeGen/EVM/br.ll | 7 +- llvm/test/CodeGen/EVM/brcond.ll | 19 +--- llvm/test/CodeGen/EVM/lower-jump-unless.mir | 108 ++++++++++++++++++++ llvm/test/CodeGen/EVM/select.ll | 2 - 8 files changed, 198 insertions(+), 41 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/lower-jump-unless.mir diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index f4c4bba98d3d..a5096bad5d90 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -53,17 +53,18 @@ add_llvm_target(EVMCodeGen EVMMachineFunctionInfo.cpp EVMMarkRecursiveFunctions.cpp EVMMCInstLower.cpp + EVMMachineFunctionInfo.cpp EVMOptimizeLiveIntervals.cpp EVMRegColoring.cpp EVMRegisterInfo.cpp EVMSHA3ConstFolding.cpp EVMSingleUseExpression.cpp EVMSplitCriticalEdges.cpp - EVMStackSolver.cpp EVMStackModel.cpp + EVMStackShuffler.cpp + EVMStackSolver.cpp EVMStackify.cpp EVMStackifyCodeEmitter.cpp - EVMStackShuffler.cpp EVMSubtarget.cpp EVMTargetMachine.cpp EVMTargetTransformInfo.cpp @@ -85,8 +86,6 @@ add_llvm_target(EVMCodeGen Target TargetParser TransformUtils - EVMDesc - EVMInfo ADD_TO_COMPONENT EVM diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index b56f6a245551..9b0a070588cb 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -415,10 +415,17 @@ let isBarrier = 1 in { } // isBarrier = 1 } // isBranch = 1, isTerminator = 1 +def : Pat<(brcond (setcc GPR:$src, 0, SETNE), bb:$dst), + (JUMPI bb:$dst, GPR:$src)>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETEQ), bb:$dst), + (JUMPI bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), + (JUMPI bb:$dst, (SUB GPR:$rs1, GPR:$rs0))>; + def : Pat<(brcond (setcc GPR:$src, 0, SETEQ), bb:$dst), (JUMP_UNLESS bb:$dst, GPR:$src)>; -def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), - (JUMP_UNLESS bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$src, -1, SETGT), bb:$dst), + (JUMP_UNLESS bb:$dst, (LT GPR:$src, (CONST_I256 0)))>; def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETGE), bb:$dst), (JUMP_UNLESS bb:$dst, (LT GPR:$rs0, GPR:$rs1))>; def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETLE), bb:$dst), diff --git a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp index 2e85a5157e70..938c095db557 100644 --- a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp +++ b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp @@ -14,14 +14,20 @@ #include "EVMMachineFunctionInfo.h" #include "EVMSubtarget.h" #include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" using namespace llvm; #define DEBUG_TYPE "evm-lower-jump-unless" #define EVM_LOWER_JUMP_UNLESS_NAME "EVM Lower jump_unless" +STATISTIC(NumPseudoJumpUnlessFolded, "Number of PseudoJUMP_UNLESS folded"); + namespace { class EVMLowerJumpUnless final : public MachineFunctionPass { public: @@ -66,14 +72,51 @@ static void lowerJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, .addReg(NewReg); } -// Lower pseudo jump_unless into iszero and jumpi instructions. This pseudo -// instruction can only be present in stackified functions. +/// Fold ` ; PseudoJUMP_UNLESS` into `PseudoJUMPI`. +/// +/// Supported `PrevMI` patterns and changes: +/// • `ISZERO_S` -> delete `ISZERO_S` +/// • `EQ_S` -> change to `SUB_S` +/// • `SUB_S` -> change to `EQ_S` +/// +/// Returns `true` if any fold was performed. +static bool tryFoldJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII) { + auto I = MachineBasicBlock::iterator(&MI); + auto *PrevMI = I == MI.getParent()->begin() ? nullptr : &*std::prev(I); + bool CanFold = PrevMI && (PrevMI->getOpcode() == EVM::ISZERO_S || + PrevMI->getOpcode() == EVM::EQ_S || + PrevMI->getOpcode() == EVM::SUB_S); + + if (!CanFold) + return false; + + ++NumPseudoJumpUnlessFolded; + + if (PrevMI->getOpcode() == EVM::ISZERO_S) + PrevMI->eraseFromParent(); + else if (PrevMI->getOpcode() == EVM::EQ_S) + PrevMI->setDesc(TII->get(EVM::SUB_S)); + else if (PrevMI->getOpcode() == EVM::SUB_S) + PrevMI->setDesc(TII->get(EVM::EQ_S)); + return true; +} + +/// Lower a `PseudoJUMP_UNLESS` to condition-setting + `PseudoJUMPI`. +/// +/// If `FoldJumps` is enabled and the local pattern allows it, an +/// optimisation in `tryFoldJumpUnless` removes the explicit `ISZERO_S`. +/// Otherwise the pseudo-op expands to: +/// ISZERO_S +/// PseudoJUMPI static void lowerPseudoJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, - const bool IsStackified) { + const bool IsStackified, + const bool FoldJumps) { assert(IsStackified && "Found pseudo jump_unless in non-stackified function"); assert(MI.getNumExplicitOperands() == 1 && "Unexpected number of operands in pseudo jump_unless"); - BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO_S)); + + if (!FoldJumps || !tryFoldJumpUnless(MI, TII)) + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO_S)); BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::PseudoJUMPI)) .add(MI.getOperand(0)); } @@ -84,6 +127,7 @@ bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { << "********** Function: " << MF.getName() << '\n'; }); + CodeGenOptLevel OptLevel = MF.getTarget().getOptLevel(); MachineRegisterInfo &MRI = MF.getRegInfo(); const auto *TII = MF.getSubtarget().getInstrInfo(); const bool IsStackified = @@ -91,17 +135,24 @@ bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { bool Changed = false; for (MachineBasicBlock &MBB : MF) { - for (auto &MI : make_early_inc_range(MBB)) { - if (MI.getOpcode() == EVM::PseudoJUMP_UNLESS) - lowerPseudoJumpUnless(MI, TII, IsStackified); - else if (MI.getOpcode() == EVM::JUMP_UNLESS) - lowerJumpUnless(MI, TII, IsStackified, MRI); - else - continue; - - MI.eraseFromParent(); - Changed = true; + auto TermIt = MBB.getFirstInstrTerminator(); + if (TermIt == MBB.end()) + continue; + + switch (TermIt->getOpcode()) { + case EVM::PseudoJUMP_UNLESS: + lowerPseudoJumpUnless(*TermIt, TII, IsStackified, + OptLevel != CodeGenOptLevel::None); + break; + case EVM::JUMP_UNLESS: + lowerJumpUnless(*TermIt, TII, IsStackified, MRI); + break; + default: + continue; } + + TermIt->eraseFromParent(); + Changed = true; } return Changed; } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 5fd67d0a1c78..8edf8320225f 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -48,10 +48,14 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { // Register the target. const RegisterTargetMachine X(getTheEVMTarget()); auto &PR = *PassRegistry::getPassRegistry(); - initializeEVMCodegenPreparePass(PR); + initializeEVMAAWrapperPassPass(PR); initializeEVMAllocaHoistingPass(PR); + initializeEVMBPStackificationPass(PR); + initializeEVMCodegenPreparePass(PR); + initializeEVMExternalAAWrapperPass(PR); initializeEVMLinkRuntimePass(PR); initializeEVMLowerIntrinsicsPass(PR); + initializeEVMLowerJumpUnlessPass(PR); initializeEVMOptimizeLiveIntervalsPass(PR); initializeEVMRegColoringPass(PR); initializeEVMSingleUseExpressionPass(PR); diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index e51d49e3da4d..49170eb40263 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -8,10 +8,9 @@ define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: diamond: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DUP2 -; CHECK-NEXT: DUP2 -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true_bb diff --git a/llvm/test/CodeGen/EVM/brcond.ll b/llvm/test/CodeGen/EVM/brcond.ll index c9fafdea70da..193d2be5b7e2 100644 --- a/llvm/test/CodeGen/EVM/brcond.ll +++ b/llvm/test/CodeGen/EVM/brcond.ll @@ -12,8 +12,7 @@ define void @br_eq(i256 %a, i256 %b) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO +; CHECK-NEXT: SUB ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -227,9 +226,6 @@ define void @br_eq_0(i256 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB10_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -329,9 +325,6 @@ define void @br_ule_0(i256 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB15_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -391,13 +384,11 @@ define void @br_slt_0(i256 %a) { ; CHECK-LABEL: br_slt_0: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SUB -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: SGT +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB18_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true diff --git a/llvm/test/CodeGen/EVM/lower-jump-unless.mir b/llvm/test/CodeGen/EVM/lower-jump-unless.mir new file mode 100644 index 000000000000..015fdebf103c --- /dev/null +++ b/llvm/test/CodeGen/EVM/lower-jump-unless.mir @@ -0,0 +1,108 @@ +# RUN: llc -x mir --run-pass=evm-lower-jump-unless < %s | FileCheck %s +# RUN: llc -x mir --run-pass=evm-lower-jump-unless -O0 < %s | FileCheck %s --check-prefix=CHECK-O0 + +--- | + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define void @br_eq(i256 %a, i256 %b) { + %cond = icmp eq i256 %a, %b + br i1 %cond, label %true, label %false + + true: ; preds = %0 + unreachable + + false: ; preds = %0 + unreachable + } + + define void @br_ugt(i256 %a, i256 %b) { + %cond = icmp ugt i256 %a, %b + br i1 %cond, label %true, label %false + + true: ; preds = %0 + unreachable + + false: ; preds = %0 + unreachable + } + +... +--- +name: br_eq +alignment: 1 +machineFunctionInfo: + isStackified: true + numberOfParameters: 2 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: br_eq + ; CHECK: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: SUB_S + ; CHECK-NEXT: PseudoJUMPI %bb.2 + ; + ; CHECK-O0-LABEL: name: br_eq + ; CHECK-O0: SWAP2_S + ; CHECK-O0-NEXT: POP_S + ; CHECK-O0-NEXT: EQ_S + ; CHECK-O0-NEXT: ISZERO_S + ; CHECK-O0-NEXT: PseudoJUMPI %bb.2 + bb.0 (%ir-block.0): + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $arguments, $value_stack + + SWAP2_S + POP_S + EQ_S + PseudoJUMP_UNLESS %bb.2 + + bb.1.true: + successors: + liveins: $value_stack + + bb.2.false: + liveins: $value_stack + +... +--- +name: br_ugt +alignment: 1 +machineFunctionInfo: + isStackified: true + numberOfParameters: 2 + hasPushDeployAddress: false +body: | + ; CHECK-LABEL: name: br_ugt + ; CHECK: SWAP1_S + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: POP_S + ; CHECK-NEXT: UGT_S + ; CHECK-NEXT: ISZERO_S + ; CHECK-NEXT: PseudoJUMPI %bb.2 + ; + ; CHECK-O0-LABEL: name: br_ugt + ; CHECK-O0: SWAP1_S + ; CHECK-O0-NEXT: SWAP2_S + ; CHECK-O0-NEXT: POP_S + ; CHECK-O0-NEXT: UGT_S + ; CHECK-O0-NEXT: ISZERO_S + ; CHECK-O0-NEXT: PseudoJUMPI %bb.2 + bb.0 (%ir-block.0): + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $arguments, $value_stack + + SWAP1_S + SWAP2_S + POP_S + UGT_S + PseudoJUMP_UNLESS %bb.2 + + bb.1.true: + successors: + liveins: $value_stack + + bb.2.false: + liveins: $value_stack + +... diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll index caec55c8ec4c..95fd2bf114f5 100644 --- a/llvm/test/CodeGen/EVM/select.ll +++ b/llvm/test/CodeGen/EVM/select.ll @@ -12,8 +12,6 @@ define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: From 2a328de96d29893fe688e38ed16e1436300b5425 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Mon, 30 Jun 2025 14:41:57 +0200 Subject: [PATCH 147/203] [EVM] Use the MCContext::allocFragment() function to allocate an MCDataFragment --- llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp index 5f717cd2a7b6..760bb15bf99e 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMTargetStreamer.cpp @@ -64,7 +64,7 @@ void EVMTargetObjStreamer::emitWideRelocatableSymbol(const MCInst &PushInst, const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); auto &S = static_cast(Streamer); - auto *DF = new MCDataFragment(); + auto *DF = Ctx.allocFragment(); S.insert(DF); SmallString<128> Code; S.getAssembler().getEmitter().encodeInstruction(PushInst, Code, From 854a964f554f9678b51a15d934335125a1bcc979 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 5 Jun 2025 18:22:26 +0300 Subject: [PATCH 148/203] [EVM] Implement large constants unfolding Large constants are broken into sequences of operations to reduce bytecode size. For example, PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 (33 bytes) can be replaced with PUSH4 0xFFFFFFFF PUSH1 0xE0 SHL (8 bytes) --- llvm/lib/Target/EVM/CMakeLists.txt | 1 + llvm/lib/Target/EVM/EVM.h | 7 + llvm/lib/Target/EVM/EVMConstantUnfolding.cpp | 579 ++++++++++++++++++ .../lib/Target/EVM/EVMFinalizeStackFrames.cpp | 25 +- llvm/lib/Target/EVM/EVMInstrFormats.td | 8 + llvm/lib/Target/EVM/EVMInstrInfo.h | 7 + llvm/lib/Target/EVM/EVMInstrInfo.td | 92 ++- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 6 +- .../CodeGen/EVM/bitmanipulation-intrinsics.ll | 120 +++- .../test/CodeGen/EVM/constant-unfolding-Oz.ll | 316 ++++++++++ llvm/test/CodeGen/EVM/constant-unfolding.ll | 99 +++ .../CodeGen/EVM/force-constant-unfolding.ll | 43 ++ llvm/test/CodeGen/EVM/icmp.ll | 4 +- llvm/test/CodeGen/EVM/mul.ll | 5 +- .../EVM/no-small-constant-unfolding.ll | 51 ++ llvm/test/CodeGen/EVM/stack-too-deep-3.ll | 8 +- llvm/test/MC/EVM/imm-hex-output.ll | 4 +- 17 files changed, 1265 insertions(+), 110 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMConstantUnfolding.cpp create mode 100644 llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll create mode 100644 llvm/test/CodeGen/EVM/constant-unfolding.ll create mode 100644 llvm/test/CodeGen/EVM/force-constant-unfolding.ll create mode 100644 llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index a5096bad5d90..50734f6f7f30 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -41,6 +41,7 @@ add_llvm_target(EVMCodeGen EVMArgumentMove.cpp EVMAsmPrinter.cpp EVMBackwardPropagationStackification.cpp + EVMConstantUnfolding.cpp EVMCodegenPrepare.cpp EVMFinalizeStackFrames.cpp EVMFrameLowering.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index f0bacc052896..1d08997b29e4 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -42,8 +42,13 @@ namespace EVMCOST { unsigned constexpr SWAP = 3; unsigned constexpr DUP = 3; unsigned constexpr POP = 2; +unsigned constexpr PUSH0 = 2; unsigned constexpr PUSH = 3; unsigned constexpr MLOAD = 3; +unsigned constexpr SHIFT = 3; +unsigned constexpr ADD = 3; +unsigned constexpr SUB = 3; +unsigned constexpr NOT = 3; } // namespace EVMCOST // LLVM IR passes. @@ -69,6 +74,7 @@ FunctionPass *createEVMBPStackification(); FunctionPass *createEVMLowerJumpUnless(); ModulePass *createEVMFinalizeStackFrames(); ModulePass *createEVMMarkRecursiveFunctionsPass(); +FunctionPass *createEVMConstantUnfolding(); // PassRegistry initialization declarations. void initializeEVMCodegenPreparePass(PassRegistry &); @@ -87,6 +93,7 @@ void initializeEVMExternalAAWrapperPass(PassRegistry &); void initializeEVMLowerJumpUnlessPass(PassRegistry &); void initializeEVMFinalizeStackFramesPass(PassRegistry &); void initializeEVMMarkRecursiveFunctionsPass(PassRegistry &); +void initializeEVMConstantUnfoldingPass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { EVMLinkRuntimePass() = default; diff --git a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp new file mode 100644 index 000000000000..61aa92c4934f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp @@ -0,0 +1,579 @@ +//===----- EVMConstantUnfolding.cpp - Constant unfolding -------*- 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 implement large constants unfolding. +// +// Large constants are broken into sequences of operations +// to reduce bytecode size. For example, +// +// PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +// (33 bytes) +// +// can be replaced with +// +// PUSH4 0xFFFFFFFF +// PUSH1 0xE0 +// SHL +// (8 bytes) +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "EVMInstrInfo.h" +#include "EVMMachineFunctionInfo.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "TargetInfo/EVMTargetInfo.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "evm-constant-unfolding" + +STATISTIC(NumOfUnfoldings, "Number of unfolded constants"); + +// The initial values of the following options were determined experimentally +// to allow constant unfolding in non-OptForSize mode without noticeably +// impacting performance. +static cl::opt + SizeGainThreshold("evm-const-unfolding-size-gain-threshold", cl::Hidden, + cl::init(2.5), + cl::desc("Minimum original-to-unfolded size ratio" + "required for constant unfolding when" + "optimizing for speed")); + +static cl::opt + InstrNumLimitUnfolInto("evm-const-unfolding-inst-num-limit", cl::Hidden, + cl::init(3), + cl::desc("Maximum number of instructions an original" + "instruction can be unfolded into")); + +static cl::opt GasWeight("evm-const-unfolding-gas-weight", cl::Hidden, + cl::init(2), + cl::desc("Gas weight used as a factor in the" + "transformation cost function")); + +static cl::opt + LoopDepthLimit("evm-const-loop-depth-limit", cl::Hidden, cl::init(2), + cl::desc("The maximum loop depth at which constant" + "unfolding is still considered beneficial")); + +namespace { +class EVMConstantUnfolding final : public MachineFunctionPass { +public: + static char ID; + + EVMConstantUnfolding() : MachineFunctionPass(ID) {} + +private: + // Estimates the execution cost of EVM-style stack operations. + // Tracks instruction count, gas cost, and unfolded bytecode size. + // It abstracts gas accounting for pushes and simple arithmetic/logical + // operations. + class StackCostModel { + public: + StackCostModel() = default; + + void push(const APInt &Val) { + Gas += (Val.isZero() ? EVMCOST::PUSH0 : EVMCOST::PUSH); + ByteSize += getPushSize(Val); + ++InstrCount; + } + + void shift() { accountInstr(EVMCOST::SHIFT); } + + void add() { accountInstr(EVMCOST::ADD); } + + void sub() { accountInstr(EVMCOST::SUB); } + + void bit_not() { accountInstr(EVMCOST::NOT); } + + unsigned getInstrCount() const { return InstrCount; } + unsigned getByteSize() const { return ByteSize; } + unsigned getGas() const { return Gas; } + + // Get the size of the PUSH instruction required for + // the immediate value. + static unsigned getPushSize(const APInt &Val) { + return 1 + (alignTo(Val.getActiveBits(), 8) / 8); + } + + private: + void accountInstr(unsigned GasCost) { + ++InstrCount; + ++ByteSize; + Gas += GasCost; + } + + unsigned InstrCount = 0; + unsigned ByteSize = 0; + unsigned Gas = 0; + }; + + // Builds and applies a sequence of machine instructions required to + // unfold a constant. Instruction generation is deferred using lambdas, + // allowing the TransformationBuilder object to be reused for repeated + // constants. As new instructions are added, the StackCostModel is used + // to track the accumulated cost. + class TransformationBuilder { + public: + TransformationBuilder(LLVMContext &Context, const TargetInstrInfo *TII) + : Context(Context), TII(TII) {} + + void addSub() { + CostModel.sub(); + BuildItems.push_back( + [this](MachineInstr &MI) { insertInstr(MI, EVM::SUB_S); }); + } + + void addShl() { + CostModel.shift(); + BuildItems.push_back( + [this](MachineInstr &MI) { insertInstr(MI, EVM::SHL_S); }); + } + + void addShr() { + CostModel.shift(); + BuildItems.push_back( + [this](MachineInstr &MI) { insertInstr(MI, EVM::SHR_S); }); + } + + void addNot() { + CostModel.bit_not(); + BuildItems.push_back( + [this](MachineInstr &MI) { insertInstr(MI, EVM::NOT_S); }); + } + + void addImm(const APInt &Val) { + CostModel.push(Val); + BuildItems.push_back( + [this, Val = Val](MachineInstr &MI) { buildImm(MI, Val); }); + } + + // Applies queued build instruction steps to replace a given instruction. + void apply(MachineInstr &MI) const { + for (const auto &func : BuildItems) + func(MI); + } + + const StackCostModel &getCost() const { return CostModel; } + + private: + using BuildFunction = std::function; + + LLVMContext &Context; + const TargetInstrInfo *TII; + + StackCostModel CostModel; + SmallVector BuildItems; + + void insertInstr(MachineInstr &MI, unsigned Opcode) { + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(Opcode)); + } + + void buildImm(MachineInstr &MI, const APInt &Val) { + unsigned PushOpc = EVM::getPUSHOpcode(Val); + auto NewMI = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), + TII->get(EVM::getStackOpcode(PushOpc))); + if (PushOpc != EVM::PUSH0) + NewMI.addCImm(ConstantInt::get(Context, Val)); + } + }; + + MachineFunction *MF = nullptr; + const TargetInstrInfo *TII = nullptr; + DenseMap> TransformationCache; + + StringRef getPassName() const override { return "EVM constant unfolding"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + const TransformationBuilder *findOptimalTransfomtation(const APInt &Imm); + + bool tryUnfoldConstant(MachineInstr &MI); +}; +} // end anonymous namespace + +char EVMConstantUnfolding::ID = 0; + +INITIALIZE_PASS_BEGIN(EVMConstantUnfolding, DEBUG_TYPE, "Constant unfolding", + false, false) +INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass) +INITIALIZE_PASS_END(EVMConstantUnfolding, DEBUG_TYPE, "Constant unfolding", + false, false) + +FunctionPass *llvm::createEVMConstantUnfolding() { + return new EVMConstantUnfolding(); +} + +const EVMConstantUnfolding::TransformationBuilder * +EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { + if (auto It = TransformationCache.find(Imm); + It != TransformationCache.end()) { + LLVM_DEBUG({ dbgs() << " Retrieving transformation from the cache\n"; }); + return It->second.get(); + } + + // Decompose a given immediate operand of the form: + // + // 0x0000001...100000000 + // + // into: + // - trailing and leading zero bits + // - 'Val' part, that starts and ends with '1' + // - abs('Val') + // + unsigned Ones = Imm.popcount(); + unsigned TrailZ = Imm.countTrailingZeros(); + unsigned LeadZ = Imm.countLeadingZeros(); + APInt Val = Imm.extractBits(Imm.getBitWidth() - TrailZ - LeadZ, TrailZ); + unsigned ValLen = Val.getActiveBits(); + bool IsMask = ((Ones + LeadZ + TrailZ) == Imm.getBitWidth()); + assert(ValLen == (Imm.getBitWidth() - TrailZ - LeadZ)); + assert(Val.isNegative()); + + bool OptForSize = MF->getFunction().hasOptSize(); + + SmallVector, 8> Transformations; + + // 1. A transformation that represents an immediate value as: + // + // (~(AbsVal - 1) << shift_l) >> shift_r + // + // For example, + // + // 0x00000000FFFFFFFFFFFFFF000000000000000000000000000000000000000000 + // + // is represented as: + // + // (~0 << 200) >> 32 + // + // Cost: + // PUSH AbsVal // 1 + AbsValLen + // NOT // 1 + // PUSH1 shiftl // 2 + // SHR // 1 + // -------------------- // Optional + // PUSH1 shiftr // 2 + // SHL // 1 + // --------- + // [2 ... 8] + AbsValLen + // + { + // Not and left/right shift + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + assert(!Val.abs().isZero()); + Tr->addImm(Val.abs() - 1); + Tr->addNot(); + Tr->addImm(APInt(256, 256 - ValLen)); + Tr->addShl(); + if (LeadZ) { + Tr->addImm(APInt(256, LeadZ)); + Tr->addShr(); + } + Transformations.emplace_back(std::move(Tr)); + } + + // 2. A transformation that represents an immediate value in the + // form of a right-shifted mask. + // + // For example, + // + // 0x0000000000000000000000000000000000000000000000000000FFFFFFFFFFFF + // + // is represented as: + // + // (~0) >> 192 + // + // Cost: + // PUSH0 // 1 + // NOT // 1 + // PUSH1 shift // 2 + // SHR // 1 + // --------- + // 5 + // + if (IsMask && !TrailZ) { + assert(ValLen != 256); + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + Tr->addImm(APInt::getZero(256)); + Tr->addNot(); + Tr->addImm(APInt(256, 256 - ValLen)); + Tr->addShr(); + Transformations.emplace_back(std::move(Tr)); + } + + // 3. A transformation that expresses an immediate value as a smaller + // bit-width value shifted to the left + // + // For example, + // + // 0xFFFFFF0000000000000000000000000000000000000000000000000000000000 + // + // is represented as: + // + // 0xFFFFFF << 192 + // + // Cost: + // PUSH Val // 1 + ValLen + // PUSH1 shift // 2 + // SHL // 1 + // --------- + // 4 + ValLen + // + if (TrailZ) { + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + Tr->addImm(Val); + Tr->addImm(APInt(256, TrailZ)); + Tr->addShl(); + Transformations.emplace_back(std::move(Tr)); + } + + if (!LeadZ) { + // 4. A transformation that represents an immediate value in the + // form of bit-reversing. + // + // For example, + // + // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE + // + // is represented as: + // + // ~0x01 + // + // Cost: + // PUSH // 1 InvImm + // NOT // 1 + // --------- + // 2 + InvImm + // + { + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + Tr->addImm(~Imm); + Tr->addNot(); + Transformations.emplace_back(std::move(Tr)); + } + + // 5. A transformation that represents an immediate value by reversing its + // bits and representing the reversed portion using a left shift + // operation. + // + // For example, + // + // 0xFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + // + // is represented as: + // + // ~(0x01 << 155) + // + // Cost: + // PUSH // 1 InvImmVal + // PUSH1 shift // 2 + // SHL // 1 + // NOT // 1 + // --------- + // 5 + InvImmVal + // + { + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + APInt ImmNot = ~Imm; + unsigned Tz = ImmNot.countTrailingZeros(); + unsigned Lz = ImmNot.countLeadingZeros(); + APInt ImmNotVal = ImmNot.extractBits(ImmNot.getBitWidth() - Tz - Lz, Tz); + Tr->addImm(ImmNotVal); + Tr->addImm(APInt(256, Tz)); + Tr->addShl(); + Tr->addNot(); + Transformations.emplace_back(std::move(Tr)); + } + } + + // 6. No transformation, leave immediate as is. + { + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + Tr->addImm(Imm); + Transformations.emplace_back(std::move(Tr)); + } + + auto *OptIt = std::min_element( + Transformations.begin(), Transformations.end(), + [OptForSize](const auto &TrA, const auto &TrB) { + const StackCostModel &CostA = TrA->getCost(); + const StackCostModel &CostB = TrB->getCost(); + // When optimizing for size, our primary goal is + // to minimize the total size of the instructions. + if (OptForSize) { + // First check, if sizes differ. + if (CostA.getByteSize() != CostB.getByteSize()) + return CostA.getByteSize() < CostB.getByteSize(); + + // Then the number of operations. + return CostA.getInstrCount() < CostB.getInstrCount(); + } + // Optimizing for speed. + // Expressing total cost in terms of both size and gas + // enables more performance-friendly transformation choices. + unsigned SizeGasA = CostA.getByteSize() + (GasWeight * CostA.getGas()); + unsigned SizeGasB = CostB.getByteSize() + (GasWeight * CostB.getGas()); + if (SizeGasA != SizeGasB) + return SizeGasA < SizeGasB; + + return CostA.getInstrCount() < CostB.getInstrCount(); + }); + +#ifndef NDEBUG + LLVM_DEBUG({ dbgs() << " Candidate transformations:\n"; }); + for (const auto &Tr : Transformations) { + const StackCostModel &Cost = Tr->getCost(); + LLVM_DEBUG({ + dbgs() << " [size: " << Cost.getByteSize() + << ", OpNum: " << Cost.getInstrCount() + << ", Gas: " << Cost.getGas() << "]\n"; + }); + } +#endif // NDEBUG + + const TransformationBuilder *Tr = OptIt->get(); + [[maybe_unused]] auto Res = + TransformationCache.try_emplace(Imm, std::move(*OptIt)); + assert(Res.second); + + return Tr; +} + +bool EVMConstantUnfolding::tryUnfoldConstant(MachineInstr &MI) { + const APInt Imm = MI.getOperand(0).getCImm()->getValue().zext(256); + assert(Imm.getActiveBits() > 4 * 8); + + // Check for the -1 value early + if (Imm.isAllOnes()) { + auto Tr = std::make_unique( + MF->getFunction().getContext(), TII); + Tr->addImm(APInt::getZero(256)); + Tr->addNot(); + Tr->apply(MI); + MI.eraseFromParent(); + ++NumOfUnfoldings; + + LLVM_DEBUG({ dbgs() << " Transforming -1 to ~0\n"; }); + return true; + } + + bool OptForSize = MF->getFunction().hasOptSize(); + const TransformationBuilder *OptTransformation = + findOptimalTransfomtation(Imm); + + const StackCostModel &OptCost = OptTransformation->getCost(); + LLVM_DEBUG({ + dbgs() << " Transformation candidate:\n [size: " << OptCost.getByteSize() + << ", OpNum: " << OptCost.getInstrCount() + << ", Gas: " << OptCost.getGas() << "]\n"; + }); + + if (OptCost.getInstrCount() == 1) { + LLVM_DEBUG({ dbgs() << " Skipping identity transformation\n"; }); + return false; + } + + bool Changed = false; + if (OptForSize) { + OptTransformation->apply(MI); + Changed = true; + } else { + unsigned OrigSize = (alignTo(Imm.getActiveBits(), 8) / 8) + 1; + // When optimizing for speed, only unfold constants if it reduces the size + // by a factor of at least the 'SizeGainThreshold' and keeps the instruction + // count to 'InstrNumLimitUnfolInto' or fewer. + if ((static_cast(OrigSize) / + static_cast(OptCost.getByteSize()) > + SizeGainThreshold) && + OptCost.getInstrCount() <= InstrNumLimitUnfolInto) { + OptTransformation->apply(MI); + Changed = true; + } else { + LLVM_DEBUG({ + dbgs() << " Unfolding is omitted as its effect does not meet the" + << "required gain threshold\n"; + }); + } + } + + if (Changed) { + MI.eraseFromParent(); + ++NumOfUnfoldings; + } + + return Changed; +} + +bool EVMConstantUnfolding::runOnMachineFunction(MachineFunction &Mf) { + MF = &Mf; + bool Changed = false; + LLVM_DEBUG({ + dbgs() << "\n********** Constant unfolding **********\n" + << "********** Function: " << Mf.getName() << '\n'; + }); + + TII = MF->getSubtarget().getInstrInfo(); + MachineLoopInfo *MLI = &getAnalysis().getLI(); + for (MachineBasicBlock &MBB : *MF) { + unsigned LoopDepth = MLI->getLoopDepth(&MBB); + if (LoopDepth > LoopDepthLimit) { + LLVM_DEBUG({ + dbgs() << "Skipping block " << MBB.getName() + << " due to exceeding the loop depth limit: " << LoopDepthLimit + << '\n'; + }); + continue; + } + + for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) { + if (!EVMInstrInfo::isPush(&MI)) + continue; + + LLVM_DEBUG({ dbgs() << "Checking " << MI; }); + + // It’s not practical to check small constants, since the default + // instructions are cheapest in those cases. The limit is based on the + // fact that the most compact transformation for representing a + // constant is SmallValueAndLeftShift which requires at least 5 bytes. + switch (MI.getOpcode()) { + case EVM::PUSH0_S: + case EVM::PUSH1_S: + case EVM::PUSH2_S: + case EVM::PUSH3_S: + case EVM::PUSH4_S: { + LLVM_DEBUG({ dbgs() << " Skipped due to bit-wise small constant\n"; }); + continue; + } + default: + break; + } + + Changed |= tryUnfoldConstant(MI); + } + } + return Changed; +} diff --git a/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp index 7935b2ea87dd..0ece97c31870 100644 --- a/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp +++ b/llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp @@ -170,21 +170,18 @@ bool EVMFinalizeStackFrames::runOnModule(Module &M) { LLVM_DEBUG({ dbgs() << "Total stack size: " << TotalStackSize << "\n"; }); // Check if it is valid to replace frame indices. - if (TotalStackSize > 0) { - if (TotalStackSize > StackRegionSize) { - report_evm_stack_error( - "Total stack size: " + Twine(TotalStackSize) + - " is larger than the allocated stack region size: " + - Twine(StackRegionSize), - TotalStackSize); - } - - if (StackRegionSize > TotalStackSize) - errs() << "warning: allocated stack region size: " + - Twine(StackRegionSize) + - " is larger than the total stack size: " + - Twine(TotalStackSize) + "\n"; + if (TotalStackSize > 0 && TotalStackSize > StackRegionSize) { + report_evm_stack_error( + "Total stack size: " + Twine(TotalStackSize) + + " is larger than the allocated stack region size: " + + Twine(StackRegionSize), + TotalStackSize); } + if (StackRegionSize > TotalStackSize) + errs() << "warning: allocated stack region size: " + + Twine(StackRegionSize) + + " is larger than the total stack size: " + + Twine(TotalStackSize) + "\n"; // Replace frame indices with their offsets. for (auto &[MF, StackRegionStart] : ToReplaceFI) diff --git a/llvm/lib/Target/EVM/EVMInstrFormats.td b/llvm/lib/Target/EVM/EVMInstrFormats.td index dfb2dd75797a..2442cafa6815 100644 --- a/llvm/lib/Target/EVM/EVMInstrFormats.td +++ b/llvm/lib/Target/EVM/EVMInstrFormats.td @@ -33,6 +33,7 @@ class EVMInst // Imm. bits<264> Inst; bits<8> Opc; + bit IsPush = 0; bit StackBased = stack; string BaseName = NAME; int GasCost = cost; @@ -40,6 +41,7 @@ class EVMInst let Pattern = []; let AsmString = asmstr; let TSFlags{0} = stack; + let TSFlags{1} = IsPush; } // Normal instructions. Default instantiation of a EVMInst. @@ -79,3 +81,9 @@ class EVMPseudo pattern, bit stack = 0> let isPseudo = 1; let isCodeGenOnly = 1; } + +class PushBase pattern, bit stack, + string asmstr = "", bits<8> inst = 0, int cost = 0> + : NI { + let IsPush = 1; +} diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h index f56d954c3245..1ad5f1d830de 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.h +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -28,6 +28,9 @@ enum { // TSF flag to check if this is a stack instruction. IsStackPos = 0, IsStackMask = 0x1, + // TSF flag to check if this is a PUSH instruction. + IsPushPos = 1, + IsPushMask = 0x1, }; } // namespace EVMII @@ -89,6 +92,10 @@ class EVMInstrInfo final : public EVMGenInstrInfo { static bool isStack(const MachineInstr *MI) { return getTSFlag(MI, EVMII::IsStackPos, EVMII::IsStackMask); } + + static bool isPush(const MachineInstr *MI) { + return getTSFlag(MI, EVMII::IsPushPos, EVMII::IsPushMask); + } }; } // end namespace llvm diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 9b0a070588cb..37b3712a3d91 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -82,17 +82,12 @@ def EVMMemcpy_heap let OperandType = "OPERAND_IMMEDIATE" in def i256imm : Operand; +def jmptarget : Operand; -def neg_imm128 : Operand, IntImmLeaf; - -def imm128 : Operand, IntImmLeaf, IntImmLeaf; -def jmptarget : Operand; - //===----------------------------------------------------------------------===// // EVM Register to Stack instruction mapping //===----------------------------------------------------------------------===// @@ -133,9 +128,7 @@ include "EVMInstrFormats.td" def negate_imm : SDNodeXFormgetAPIntValue(); - APInt pos = neg; - pos.negate(); - return CurDAG->getTargetConstant(pos, SDLoc(N), MVT::i256); + return CurDAG->getTargetConstant(-neg, SDLoc(N), MVT::i256); }]>; @@ -301,15 +294,9 @@ def : Pat<(sext_inreg GPR:$src, i32), (SIGNEXTEND (CONST_I256 3), GPR:$src)>; def : Pat<(sext_inreg GPR:$src, i64), (SIGNEXTEND (CONST_I256 7), GPR:$src)>; def : Pat<(sext_inreg GPR:$src, i128), (SIGNEXTEND (CONST_I256 15), GPR:$src)>; -// Reverse some operations with negative immediate operands to reduce bit-size -// of the immediate encoding. -// TODO: It needs to be discussed starting which bit-size this transformation -// is deems profitable. -def : Pat<(neg_imm128:$imm), - (SUB (CONST_I256 0), (CONST_I256 (negate_imm imm:$imm)))>; -def : Pat<(add GPR:$res, neg_imm128:$imm), +def : Pat<(add GPR:$res, neg_imm:$imm), (SUB GPR:$res, (CONST_I256 (negate_imm imm:$imm)))>; -def : Pat<(sub GPR:$res, neg_imm128:$imm), +def : Pat<(sub GPR:$res, neg_imm:$imm), (ADD GPR:$res, (CONST_I256 (negate_imm imm:$imm)))>; def : Pat<(int_evm_div GPR:$op1, GPR:$op2), (DIV GPR:$op1, GPR:$op2)>; @@ -317,7 +304,6 @@ def : Pat<(int_evm_sdiv GPR:$op1, GPR:$op2), (SDIV GPR:$op1, GPR:$op2)>; def : Pat<(int_evm_mod GPR:$op1, GPR:$op2), (MOD GPR:$op1, GPR:$op2)>; def : Pat<(int_evm_smod GPR:$op1, GPR:$op2), (SMOD GPR:$op1, GPR:$op2)>; - //===----------------------------------------------------------------------===// // EVM comparison instructions. //===----------------------------------------------------------------------===// @@ -828,7 +814,7 @@ def PUSH_FRAME : NI<(outs), (ins i256imm:$imm), [], true, "", 0, 0> { // Define register PUSH* instructions foreach I = {1 - 32} in { - def PUSH#I : NI<(outs), (ins i256imm:$imm), [], false, "PUSH"#I#" $imm", + def PUSH#I : PushBase<(outs), (ins i256imm:$imm), [], false, "PUSH"#I#" $imm", !add(I, 0x5F), 3> { let BaseName = "PUSH"#I; let Size = !add(I, 1); @@ -837,7 +823,7 @@ foreach I = {1 - 32} in { } // Define stack PUSH* instructions -def PUSH1_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH1$imm", +def PUSH1_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH1$imm", 0x60, 3> { bits<8> imm; let Inst{15-8} = Opc; @@ -846,7 +832,7 @@ def PUSH1_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH1$imm", let BaseName = "PUSH1"; } -def PUSH2_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH2$imm", +def PUSH2_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH2$imm", 0x61, 3> { bits<16> imm; let Inst{23-16} = Opc; @@ -855,7 +841,7 @@ def PUSH2_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH2$imm", let BaseName = "PUSH2"; } -def PUSH3_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH3$imm", +def PUSH3_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH3$imm", 0x62, 3> { bits<24> imm; let Inst{31-24} = Opc; @@ -864,7 +850,7 @@ def PUSH3_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH3$imm", let BaseName = "PUSH3"; } -def PUSH4_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH4$imm", +def PUSH4_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH4$imm", 0x63, 3> { bits<32> imm; let Inst{39-32} = Opc; @@ -873,7 +859,7 @@ def PUSH4_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH4$imm", let BaseName = "PUSH4"; } -def PUSH5_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH5$imm", +def PUSH5_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH5$imm", 0x64, 3> { bits<40> imm; let Inst{47-40} = Opc; @@ -882,7 +868,7 @@ def PUSH5_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH5$imm", let BaseName = "PUSH5"; } -def PUSH6_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH6$imm", +def PUSH6_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH6$imm", 0x65, 3> { bits<48> imm; let Inst{55-48} = Opc; @@ -891,7 +877,7 @@ def PUSH6_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH6$imm", let BaseName = "PUSH6"; } -def PUSH7_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH7$imm", +def PUSH7_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH7$imm", 0x66, 3> { bits<56> imm; let Inst{63-56} = Opc; @@ -900,7 +886,7 @@ def PUSH7_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH7$imm", let BaseName = "PUSH7"; } -def PUSH8_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH8$imm", +def PUSH8_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH8$imm", 0x67, 3> { bits<64> imm; let Inst{71-64} = Opc; @@ -909,7 +895,7 @@ def PUSH8_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH8$imm", let BaseName = "PUSH8"; } -def PUSH9_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH9$imm", +def PUSH9_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH9$imm", 0x68, 3> { bits<72> imm; let Inst{79-72} = Opc; @@ -919,7 +905,7 @@ def PUSH9_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH9$imm", let DecoderMethod = "decodePUSH<9>"; } -def PUSH10_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH10$imm", +def PUSH10_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH10$imm", 0x69, 3> { bits<80> imm; let Inst{87-80} = Opc; @@ -929,7 +915,7 @@ def PUSH10_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH10$imm", let DecoderMethod = "decodePUSH<10>"; } -def PUSH11_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH11$imm", +def PUSH11_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH11$imm", 0x6a, 3> { bits<88> imm; let Inst{95-88} = Opc; @@ -939,7 +925,7 @@ def PUSH11_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH11$imm", let DecoderMethod = "decodePUSH<11>"; } -def PUSH12_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH12$imm", +def PUSH12_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH12$imm", 0x6b, 3> { bits<96> imm; let Inst{103-96} = Opc; @@ -949,7 +935,7 @@ def PUSH12_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH12$imm", let DecoderMethod = "decodePUSH<12>"; } -def PUSH13_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH13$imm", +def PUSH13_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH13$imm", 0x6c, 3> { bits<104> imm; let Inst{111-104} = Opc; @@ -959,7 +945,7 @@ def PUSH13_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH13$imm", let DecoderMethod = "decodePUSH<13>"; } -def PUSH14_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH14$imm", +def PUSH14_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH14$imm", 0x6d, 3> { bits<112> imm; let Inst{119-112} = Opc; @@ -969,7 +955,7 @@ def PUSH14_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH14$imm", let DecoderMethod = "decodePUSH<14>"; } -def PUSH15_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH15$imm", +def PUSH15_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH15$imm", 0x6e, 3> { bits<120> imm; let Inst{127-120} = Opc; @@ -979,7 +965,7 @@ def PUSH15_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH15$imm", let DecoderMethod = "decodePUSH<15>"; } -def PUSH16_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH16$imm", +def PUSH16_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH16$imm", 0x6f, 3> { bits<128> imm; let Inst{135-128} = Opc; @@ -989,7 +975,7 @@ def PUSH16_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH16$imm", let DecoderMethod = "decodePUSH<16>"; } -def PUSH17_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH17$imm", +def PUSH17_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH17$imm", 0x70, 3> { bits<136> imm; let Inst{143-136} = Opc; @@ -999,7 +985,7 @@ def PUSH17_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH17$imm", let DecoderMethod = "decodePUSH<17>"; } -def PUSH18_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH18$imm", +def PUSH18_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH18$imm", 0x71, 3> { bits<144> imm; let Inst{151-144} = Opc; @@ -1009,7 +995,7 @@ def PUSH18_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH18$imm", let DecoderMethod = "decodePUSH<18>"; } -def PUSH19_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH19$imm", +def PUSH19_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH19$imm", 0x72, 3> { bits<152> imm; let Inst{159-152} = Opc; @@ -1019,7 +1005,7 @@ def PUSH19_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH19$imm", let DecoderMethod = "decodePUSH<19>"; } -def PUSH20_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH20$imm", +def PUSH20_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH20$imm", 0x73, 3> { bits<160> imm; let Inst{167-160} = Opc; @@ -1029,7 +1015,7 @@ def PUSH20_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH20$imm", let DecoderMethod = "decodePUSH<20>"; } -def PUSH21_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH21$imm", +def PUSH21_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH21$imm", 0x74, 3> { bits<168> imm; let Inst{175-168} = Opc; @@ -1039,7 +1025,7 @@ def PUSH21_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH21$imm", let DecoderMethod = "decodePUSH<21>"; } -def PUSH22_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH22$imm", +def PUSH22_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH22$imm", 0x75, 3> { bits<176> imm; let Inst{183-176} = Opc; @@ -1049,7 +1035,7 @@ def PUSH22_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH22$imm", let DecoderMethod = "decodePUSH<22>"; } -def PUSH23_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH23$imm", +def PUSH23_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH23$imm", 0x76, 3> { bits<184> imm; let Inst{191-184} = Opc; @@ -1059,7 +1045,7 @@ def PUSH23_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH23$imm", let DecoderMethod = "decodePUSH<23>"; } -def PUSH24_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH24$imm", +def PUSH24_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH24$imm", 0x77, 3> { bits<192> imm; let Inst{199-192} = Opc; @@ -1069,7 +1055,7 @@ def PUSH24_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH24$imm", let DecoderMethod = "decodePUSH<24>"; } -def PUSH25_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH25$imm", +def PUSH25_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH25$imm", 0x78, 3> { bits<200> imm; let Inst{207-200} = Opc; @@ -1079,7 +1065,7 @@ def PUSH25_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH25$imm", let DecoderMethod = "decodePUSH<25>"; } -def PUSH26_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH26$imm", +def PUSH26_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH26$imm", 0x79, 3> { bits<208> imm; let Inst{215-208} = Opc; @@ -1089,7 +1075,7 @@ def PUSH26_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH26$imm", let DecoderMethod = "decodePUSH<26>"; } -def PUSH27_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH27$imm", +def PUSH27_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH27$imm", 0x7a, 3> { bits<216> imm; let Inst{223-216} = Opc; @@ -1099,7 +1085,7 @@ def PUSH27_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH27$imm", let DecoderMethod = "decodePUSH<27>"; } -def PUSH28_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH28$imm", +def PUSH28_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH28$imm", 0x7b, 3> { bits<224> imm; let Inst{231-224} = Opc; @@ -1109,7 +1095,7 @@ def PUSH28_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH28$imm", let DecoderMethod = "decodePUSH<28>"; } -def PUSH29_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH29$imm", +def PUSH29_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH29$imm", 0x7c, 3> { bits<232> imm; let Inst{239-232} = Opc; @@ -1119,7 +1105,7 @@ def PUSH29_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH29$imm", let DecoderMethod = "decodePUSH<29>"; } -def PUSH30_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH30$imm", +def PUSH30_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH30$imm", 0x7d, 3> { bits<240> imm; let Inst{247-240} = Opc; @@ -1129,7 +1115,7 @@ def PUSH30_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH30$imm", let DecoderMethod = "decodePUSH<30>"; } -def PUSH31_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH31$imm", +def PUSH31_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH31$imm", 0x7e, 3> { bits<248> imm; let Inst{255-248} = Opc; @@ -1139,7 +1125,7 @@ def PUSH31_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH31$imm", let DecoderMethod = "decodePUSH<31>"; } -def PUSH32_S : NI<(outs), (ins i256imm:$imm), [], true, "PUSH32$imm", +def PUSH32_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH32$imm", 0x7f, 3> { bits<256> imm; let Inst{263-256} = Opc; diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 8edf8320225f..557dede0069a 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -67,6 +67,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMLowerJumpUnlessPass(PR); initializeEVMFinalizeStackFramesPass(PR); initializeEVMMarkRecursiveFunctionsPass(PR); + initializeEVMConstantUnfoldingPass(PR); } static std::string computeDataLayout() { @@ -292,7 +293,10 @@ void EVMPassConfig::addPreEmitPass() { } } -void EVMPassConfig::addPreEmitPass2() { addPass(createEVMLowerJumpUnless()); } +void EVMPassConfig::addPreEmitPass2() { + addPass(createEVMLowerJumpUnless()); + addPass(createEVMConstantUnfolding()); +} TargetPassConfig *EVMTargetMachine::createPassConfig(PassManagerBase &PM) { return new EVMPassConfig(*this, PM); diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll index 37384d3a2e70..27df73f32730 100644 --- a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -108,46 +108,62 @@ define i256 @bitreversetest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH17 0xFF00000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH18 0xFF0000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH19 0xFF000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x90 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x28 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH20 0xFF00000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0x38 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH21 0xFF0000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x48 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH22 0xFF000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0x58 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH23 0xFF00000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0x68 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH24 0xFF0000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP5 ; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHL @@ -156,23 +172,31 @@ define i256 @bitreversetest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH25 0xFF000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x88 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH26 0xFF00000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x98 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH27 0xFF0000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH28 0xFF000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0xB8 ; CHECK-NEXT: SHL @@ -180,19 +204,25 @@ define i256 @bitreversetest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH29 0xFF00000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0xC8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH30 0xFF0000000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH31 0xFF000000000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHL @@ -343,46 +373,62 @@ define i256 @bswaptest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH17 0xFF00000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH18 0xFF0000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x88 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH19 0xFF000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x90 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x28 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH20 0xFF00000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0x38 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH21 0xFF0000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x48 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH22 0xFF000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xA8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0x58 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH23 0xFF00000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0x68 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH24 0xFF0000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP5 ; CHECK-NEXT: PUSH1 0x78 ; CHECK-NEXT: SHL @@ -391,23 +437,31 @@ define i256 @bswaptest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH25 0xFF000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x88 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH26 0xFF00000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x98 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR -; CHECK-NEXT: PUSH27 0xFF0000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH28 0xFF000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xD8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP4 ; CHECK-NEXT: PUSH1 0xB8 ; CHECK-NEXT: SHL @@ -415,19 +469,25 @@ define i256 @bswaptest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH29 0xFF00000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0xC8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH30 0xFF0000000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0xD8 ; CHECK-NEXT: SHL ; CHECK-NEXT: AND ; CHECK-NEXT: OR ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH31 0xFF000000000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0xF0 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHL diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll b/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll new file mode 100644 index 000000000000..17de50bb1b8b --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll @@ -0,0 +1,316 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; This file tests how constants are unfolded in OptForSize mode. + +; 0x4e487b7100000000000000000000000000000000000000000000000000000000 +define void @test1() #1 { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0x4E487B71 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 35408467139433450592217433187231851964531694900788300625387963629091585785856) + unreachable +} + +; 0x00000000FFFFFF00000000000000000000000000000000000000000000000000 +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959945060212595535676739545057538332474541900337578698310774947840) + unreachable +} + +; 0x00000000FFFFFFFFFFFF00000000000000000000000000000000000000000000 +define void @test3() #1 { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xD0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959946667150544013695710968965983276947947528216596309908473774080) + unreachable +} + +; 0x00000000FFFFFFFFFFFFFF000000000000000000000000000000000000000000 +define void @test4() #1 { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959946667150639420522595930308483613493827247172119449184879247360) + unreachable +} + +; 0xFFFFFF0000000000000000000000000000000000000000000000000000000000 +define void @test5() #1 { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792082335569848633007197573932045576244532214531591869071028845388905840640) + unreachable +} + +; 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +define void @test6() #1 { +; CHECK-LABEL: test6: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089210356248756420345214020892766250353992003419616917011526809519390720) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test7() #1 { +; CHECK-LABEL: test7: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 -1) + unreachable +} + + +; 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 +define void @test8() #1 { +; CHECK-LABEL: test8: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x81 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 57896044618658097711785492504343953926294709965899343556265417396524796608512) + unreachable +} + +; 0x000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF +define void @test11() #1 { +; CHECK-LABEL: test11: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 18446744073709551615) + unreachable +} + +; 0x000000000000000000000000000000000000000000000000FFFFFFFAFFFFFFFF +define void @test12() #1 { +; CHECK-LABEL: test12: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH8 0xFFFFFFFAFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 18446744052234715135) + unreachable +} + +; 0x000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFE0 +define void @test13() #1 { +; CHECK-LABEL: test13: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xC5 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0xC0 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 18446744073709551584) + unreachable +} + +; 0x00000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFE0 +define void @test14() #1 { +; CHECK-LABEL: test14: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0xB5 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0xB0 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 1208925819614629174706144) + unreachable +} + +; 0x00000000000000000000000000000000000000FFFFFFFFFFFFFFFFAFFFFFFFFF +define void @test15() #1 { +; CHECK-LABEL: test15: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH5 0x5000000000 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x98 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 20282409603651670423603653902335) + unreachable +} + +; 0x00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test16() #1 { +; CHECK-LABEL: test16: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x54 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 5986310706507378352962293074805895248510699696029695) + unreachable +} + +; 0x00000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE0000000000000000000000 +define void @test17() #1 { +; CHECK-LABEL: test17: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x28 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x82 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 52656145834278593348959013841835216134069776894924259991723442176) + unreachable +} + +; 0x0000000000000000000000000000000100000000000000000000000000000000 +define void @test18() #1 { +; CHECK-LABEL: test18: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 340282366920938463463374607431768211456) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE +define void @test19() #1 { +; CHECK-LABEL: test19: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089237316195423570985008687907853269984665640564039457584007913129639934) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test20() #1 { +; CHECK-LABEL: test20: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x9B +; CHECK-NEXT: SHL +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089237316195423570985008642235927103393949446698888435200163548881747967) + unreachable +} + +; 0x00000000000001000000000000000000000000000000000000000000000000FF +define void @test21() #1 { +; CHECK-LABEL: test21: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH26 0x1000000000000000000000000000000000000000000000000FF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 1606938044258990275541962092341162602522202993782792835301631) + unreachable +} + +attributes #1 = { minsize nofree nounwind noreturn optsize } diff --git a/llvm/test/CodeGen/EVM/constant-unfolding.ll b/llvm/test/CodeGen/EVM/constant-unfolding.ll new file mode 100644 index 000000000000..6e91e612ed66 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding.ll @@ -0,0 +1,99 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; This file tests how constants are unfolded in non-OptForSize mode. + +; 0x4e487b7100000000000000000000000000000000000000000000000000000000 +define void @test1() #1 { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0x4E487B71 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 35408467139433450592217433187231851964531694900788300625387963629091585785856) + unreachable +} + +; 0x00000000FFFFFF00000000000000000000000000000000000000000000000000 +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 26959945060212595535676739545057538332474541900337578698310774947840) + unreachable +} + +; 0xFFFFFF0000000000000000000000000000000000000000000000000000000000 +define void @test3() #1 { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792082335569848633007197573932045576244532214531591869071028845388905840640) + unreachable +} + +; 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +define void @test4() #1 { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 115792089210356248756420345214020892766250353992003419616917011526809519390720) + unreachable +} + +; 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test5() #1 { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 -1) + unreachable +} + +; 0x0000000000000000000000000000000100000000000000000000000000000000 +define void @test6() #1 { +; CHECK-LABEL: test6: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 340282366920938463463374607431768211456) + unreachable +} + +attributes #1 = { nofree nounwind noreturn } diff --git a/llvm/test/CodeGen/EVM/force-constant-unfolding.ll b/llvm/test/CodeGen/EVM/force-constant-unfolding.ll new file mode 100644 index 000000000000..27148b4efe0d --- /dev/null +++ b/llvm/test/CodeGen/EVM/force-constant-unfolding.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 --evm-const-unfolding-size-gain-threshold=0 --evm-const-unfolding-inst-num-limit=10 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; Verifies that constant unfolding occurs in non-OptForSize mode when limits +; are overridden through options. + + +; 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 +define void @test8() #1 { +; CHECK-LABEL: test8: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH16 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 57896044618658097711785492504343953926294709965899343556265417396524796608512) + unreachable +} + +; 0x00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x54 +; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 5986310706507378352962293074805895248510699696029695) + unreachable +} + +attributes #1 = { nofree nounwind noreturn } diff --git a/llvm/test/CodeGen/EVM/icmp.ll b/llvm/test/CodeGen/EVM/icmp.ll index ee533dcea709..d39d70c10546 100644 --- a/llvm/test/CodeGen/EVM/icmp.ll +++ b/llvm/test/CodeGen/EVM/icmp.ll @@ -24,7 +24,9 @@ define i256 @icmp_big_imm_eq(i256 %a) nounwind { ; CHECK-LABEL: icmp_big_imm_eq: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH32 0x6057361D00000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH4 0x6057361D +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL ; CHECK-NEXT: EQ ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/mul.ll b/llvm/test/CodeGen/EVM/mul.ll index 1ca8f3e539cd..54a8f728a40b 100644 --- a/llvm/test/CodeGen/EVM/mul.ll +++ b/llvm/test/CodeGen/EVM/mul.ll @@ -20,9 +20,8 @@ define i256 @mulrri(i256 %rs1) nounwind { ; CHECK-LABEL: mulrri: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH3 0x7A120 -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH3 0x7A11F +; CHECK-NEXT: NOT ; CHECK-NEXT: MUL ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll b/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll new file mode 100644 index 000000000000..61b84017de22 --- /dev/null +++ b/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll @@ -0,0 +1,51 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 -stats < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn + +; This file tests that small constants are not unfolded. + +; CHECK-NOT: evm-constant-unfolding + +; 0x0000000000000000000000000000000000000000000000000000000000000000 +define void @test1() #1 { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} + +; 0x00000000000000000000000000000000000000000000000000000000000000FF +define void @test2() #1 { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 255) + unreachable +} + +; 0x00000000000000000000000000000000000000000000000000000000FFFFFFFF +define void @test3() #1 { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 4294967295) + unreachable +} + +attributes #1 = { minsize nofree nounwind noreturn optsize } diff --git a/llvm/test/CodeGen/EVM/stack-too-deep-3.ll b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll index 38d8f94df332..52489c51fb8b 100644 --- a/llvm/test/CodeGen/EVM/stack-too-deep-3.ll +++ b/llvm/test/CodeGen/EVM/stack-too-deep-3.ll @@ -6,13 +6,7 @@ target triple = "evm-unknown-unknown" declare void @llvm.memmove.p1.p1.i256(ptr addrspace(1) nocapture writeonly, ptr addrspace(1) nocapture readonly, i256, i1 immarg) #0 -; Check that the stack solver detects unreachable slots, generates spills for them, and -; succesfully compiles the function. Also, check that we allocated the exact amount of -; stack space needed for the function, without any warnings about allocated stack region size. - -; CHECK: Unreachable slots found: 1, iteration: 1 -; CHECK: Spilling 1 registers -; CHECK-NOT: warning: allocated stack region size: +; CHECK: warning: allocated stack region size: 32 is larger than the total stack size: 0 define dso_local fastcc void @main() unnamed_addr { entry: diff --git a/llvm/test/MC/EVM/imm-hex-output.ll b/llvm/test/MC/EVM/imm-hex-output.ll index 4b0fa1f908f6..9083715cc9ad 100644 --- a/llvm/test/MC/EVM/imm-hex-output.ll +++ b/llvm/test/MC/EVM/imm-hex-output.ll @@ -13,7 +13,9 @@ define void @foo() { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 +; CHECK-NEXT: PUSH4 0xFFFFFFFF +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL ; CHECK-NEXT: PUSH1 0x40 ; CHECK-NEXT: RETURN tail call void @llvm.evm.return( ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), i256 -26959946667150639794667015087019630673637144422540572481103610249216) From b3a3a6ad9e7ec693bdbfe2d90b81bcb36b4c298e Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Sun, 6 Jul 2025 17:55:34 +0200 Subject: [PATCH 149/203] [EVM] Update attributes of some intrinsics Co-authored-by: Oleksandr Zarudnyi Co-authored-by: Vladimir Radosavljevic Co-authored-by: Pavel Kopyl > --- llvm/include/llvm/IR/IntrinsicsEVM.td | 36 ++- llvm/lib/Target/EVM/EVMInstrInfo.cpp | 10 - llvm/lib/Target/EVM/EVMInstrInfo.td | 37 +-- llvm/test/CodeGen/EVM/context-remat.ll | 60 ++-- llvm/test/CodeGen/EVM/cse-intrinsic.ll | 374 ++++++++++++++++++++++++- 5 files changed, 435 insertions(+), 82 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index 0eccab48feb3..af750b3679a2 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -113,7 +113,8 @@ def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; // Getting values from the context. def int_evm_address : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_origin : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_origin : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; def int_evm_caller : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; @@ -130,7 +131,8 @@ def int_evm_calldataload def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; def int_evm_extcodesize : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; @@ -148,28 +150,38 @@ def int_evm_returndatasize def int_evm_extcodehash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; -def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; -def int_evm_number : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_number : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; -def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; -def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; -def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; -def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, + IntrWillReturn]>; // Logging. // Log intrinsics should have 'read/write of inaccessible' memory along with read memory diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.cpp b/llvm/lib/Target/EVM/EVMInstrInfo.cpp index e22441aa6405..637883f66b29 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.cpp +++ b/llvm/lib/Target/EVM/EVMInstrInfo.cpp @@ -27,20 +27,10 @@ bool EVMInstrInfo::isReallyTriviallyReMaterializable( const MachineInstr &MI) const { switch (MI.getOpcode()) { case EVM::ADDRESS: - case EVM::ORIGIN: case EVM::CALLER: case EVM::CALLVALUE: case EVM::CALLDATASIZE: case EVM::CODESIZE: - case EVM::GASPRICE: - case EVM::COINBASE: - case EVM::TIMESTAMP: - case EVM::NUMBER: - case EVM::DIFFICULTY: - case EVM::GASLIMIT: - case EVM::CHAINID: - case EVM::BASEFEE: - case EVM::BLOBBASEFEE: case EVM::CONST_I256: return true; default: diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 37b3712a3d91..6eec15caa143 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -545,10 +545,6 @@ defm ADDRESS : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_address))], "ADDRESS", "$dst", 0x30, 2>; -defm ORIGIN - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], - "ORIGIN", "$dst", 0x32, 2>; - defm CALLER : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_caller))], "CALLER", "$dst", 0x33, 2>; @@ -564,14 +560,13 @@ defm CALLDATASIZE defm CODESIZE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_codesize))], "CODESIZE", "$dst", 0x38, 2>; +} -defm GASPRICE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], - "GASPRICE", "$dst", 0x3A, 2>; +let isAsCheapAsAMove = 1 in { -defm COINBASE - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], - "COINBASE", "$dst", 0x41, 2>; +defm ORIGIN + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_origin))], + "ORIGIN", "$dst", 0x32, 2>; defm TIMESTAMP : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_timestamp))], @@ -581,22 +576,30 @@ defm NUMBER : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_number))], "NUMBER", "$dst", 0x43, 2>; -defm DIFFICULTY - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], - "DIFFICULTY", "$dst", 0x44, 2>; - defm GASLIMIT : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gaslimit))], "GASLIMIT", "$dst", 0x45, 2>; -defm CHAINID - : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], - "CHAINID", "$dst", 0x46, 2>; +defm COINBASE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_coinbase))], + "COINBASE", "$dst", 0x41, 2>; defm BASEFEE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_basefee))], "BASEFEE", "$dst", 0x48, 2>; +defm CHAINID + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_chainid))], + "CHAINID", "$dst", 0x46, 2>; + +defm GASPRICE + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_gasprice))], + "GASPRICE", "$dst", 0x3A, 2>; + +defm DIFFICULTY + : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_difficulty))], + "DIFFICULTY", "$dst", 0x44, 2>; + defm BLOBBASEFEE : I<(outs GPR:$dst), (ins), [(set GPR:$dst, (int_evm_blobbasefee))], "BLOBBASEFEE", "$dst", 0x4A, 2>; diff --git a/llvm/test/CodeGen/EVM/context-remat.ll b/llvm/test/CodeGen/EVM/context-remat.ll index 4a58830498a1..465f178e3d63 100644 --- a/llvm/test/CodeGen/EVM/context-remat.ll +++ b/llvm/test/CodeGen/EVM/context-remat.ll @@ -28,14 +28,14 @@ define i256 @test_origin() { ; CHECK-LABEL: test_origin: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET1 ; CHECK-NEXT: ORIGIN +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET1 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET1: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: ORIGIN -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.origin() call void @use(i256 %ret) @@ -118,14 +118,14 @@ define i256 @test_gasprice() { ; CHECK-LABEL: test_gasprice: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET6 ; CHECK-NEXT: GASPRICE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET6 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET6: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: GASPRICE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.gasprice() call void @use(i256 %ret) @@ -136,14 +136,14 @@ define i256 @test_coinbase() { ; CHECK-LABEL: test_coinbase: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET7 ; CHECK-NEXT: COINBASE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET7 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET7: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: COINBASE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.coinbase() call void @use(i256 %ret) @@ -154,14 +154,14 @@ define i256 @test_timestamp() { ; CHECK-LABEL: test_timestamp: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET8 ; CHECK-NEXT: TIMESTAMP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET8 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET8: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: TIMESTAMP -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.timestamp() call void @use(i256 %ret) @@ -172,14 +172,14 @@ define i256 @test_number() { ; CHECK-LABEL: test_number: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET9 ; CHECK-NEXT: NUMBER +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET9 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET9: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: NUMBER -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.number() call void @use(i256 %ret) @@ -190,14 +190,14 @@ define i256 @test_difficulty() { ; CHECK-LABEL: test_difficulty: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET10 ; CHECK-NEXT: DIFFICULTY +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET10 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET10: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DIFFICULTY -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.difficulty() call void @use(i256 %ret) @@ -208,14 +208,14 @@ define i256 @test_gaslimit() { ; CHECK-LABEL: test_gaslimit: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET11 ; CHECK-NEXT: GASLIMIT +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET11 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET11: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: GASLIMIT -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.gaslimit() call void @use(i256 %ret) @@ -226,14 +226,14 @@ define i256 @test_chainid() { ; CHECK-LABEL: test_chainid: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET12 ; CHECK-NEXT: CHAINID +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET12 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET12: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: CHAINID -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.chainid() call void @use(i256 %ret) @@ -244,14 +244,14 @@ define i256 @test_basefee() { ; CHECK-LABEL: test_basefee: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET13 ; CHECK-NEXT: BASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET13 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET13: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: BASEFEE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.basefee() call void @use(i256 %ret) @@ -262,14 +262,14 @@ define i256 @test_blobbasefee() { ; CHECK-LABEL: test_blobbasefee: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH4 @.FUNC_RET14 ; CHECK-NEXT: BLOBBASEFEE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.FUNC_RET14 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH4 @use ; CHECK-NEXT: JUMP ; CHECK-NEXT: .FUNC_RET14: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: BLOBBASEFEE -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %ret = call i256 @llvm.evm.blobbasefee() call void @use(i256 %ret) diff --git a/llvm/test/CodeGen/EVM/cse-intrinsic.ll b/llvm/test/CodeGen/EVM/cse-intrinsic.ll index 721235d95d97..7e0a933f9001 100644 --- a/llvm/test/CodeGen/EVM/cse-intrinsic.ll +++ b/llvm/test/CodeGen/EVM/cse-intrinsic.ll @@ -76,6 +76,114 @@ define i256 @returndatasize_dce() nounwind { ret i256 0 } +define i256 @origin_dce() nounwind { +; CHECK-LABEL: define noundef i256 @origin_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.origin() + ret i256 0 +} + +define i256 @gasprice_dce() nounwind { +; CHECK-LABEL: define noundef i256 @gasprice_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.gasprice() + ret i256 0 +} + +define i256 @blockhash_dce(i256 %rs1) nounwind { +; CHECK-LABEL: define noundef i256 @blockhash_dce +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.blockhash(i256 %rs1) + ret i256 0 +} + +define i256 @blobhash_dce(i256 %rs1) nounwind { +; CHECK-LABEL: define noundef i256 @blobhash_dce +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.blobhash(i256 %rs1) + ret i256 0 +} + +define i256 @coinbase_dce() nounwind { +; CHECK-LABEL: define noundef i256 @coinbase_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.coinbase() + ret i256 0 +} + +define i256 @timestamp_dce() nounwind { +; CHECK-LABEL: define noundef i256 @timestamp_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.timestamp() + ret i256 0 +} + +define i256 @number_dce() nounwind { +; CHECK-LABEL: define noundef i256 @number_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.number() + ret i256 0 +} + +define i256 @difficulty_dce() nounwind { +; CHECK-LABEL: define noundef i256 @difficulty_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.difficulty() + ret i256 0 +} + +define i256 @gaslimit_dce() nounwind { +; CHECK-LABEL: define noundef i256 @gaslimit_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.gaslimit() + ret i256 0 +} + +define i256 @chainid_dce() nounwind { +; CHECK-LABEL: define noundef i256 @chainid_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.chainid() + ret i256 0 +} + +define i256 @basefee_dce() nounwind { +; CHECK-LABEL: define noundef i256 @basefee_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.basefee() + ret i256 0 +} + +define i256 @blobbasefee_dce() nounwind { +; CHECK-LABEL: define noundef i256 @blobbasefee_dce +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { +; CHECK-NEXT: ret i256 0 +; + %res = call i256 @llvm.evm.blobbasefee() + ret i256 0 +} + define i256 @addmod(i256 %rs1, i256 %rs2, i256 %rs3) nounwind { ; CHECK-LABEL: define i256 @addmod ; CHECK-SAME: (i256 [[RS1:%.*]], i256 [[RS2:%.*]], i256 [[RS3:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { @@ -211,7 +319,7 @@ define i256 @address() nounwind { define i256 @origin() nounwind { ; CHECK-LABEL: define i256 @origin -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.origin() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -237,7 +345,7 @@ define i256 @caller() nounwind { define i256 @balance(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @balance -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -268,6 +376,246 @@ define i256 @balance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %ro ret i256 %ret } +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @origin_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @origin_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.origin() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.origin() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.origin() + %v2 = call i256 @llvm.evm.origin() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.origin() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @gasprice_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @gasprice_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.gasprice() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.gasprice() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.gasprice() + %v2 = call i256 @llvm.evm.gasprice() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.gasprice() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @blockhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @blockhash_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.blockhash(i256 %addr) + %v2 = call i256 @llvm.evm.blockhash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.blockhash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @blobhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @blobhash_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.blobhash(i256 %addr) + %v2 = call i256 @llvm.evm.blobhash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.blobhash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @coinbase_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @coinbase_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.coinbase() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.coinbase() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.coinbase() + %v2 = call i256 @llvm.evm.coinbase() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.coinbase() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @timestamp_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @timestamp_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.timestamp() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.timestamp() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.timestamp() + %v2 = call i256 @llvm.evm.timestamp() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.timestamp() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @number_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @number_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.number() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.number() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.number() + %v2 = call i256 @llvm.evm.number() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.number() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @difficulty_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @difficulty_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.difficulty() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.difficulty() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.difficulty() + %v2 = call i256 @llvm.evm.difficulty() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.difficulty() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @gaslimit_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @gaslimit_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.gaslimit() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.gaslimit() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.gaslimit() + %v2 = call i256 @llvm.evm.gaslimit() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.gaslimit() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @chainid_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @chainid_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.chainid() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.chainid() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.chainid() + %v2 = call i256 @llvm.evm.chainid() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.chainid() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @basefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @basefee_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.basefee() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.basefee() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.basefee() + %v2 = call i256 @llvm.evm.basefee() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.basefee() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @blobbasefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @blobbasefee_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blobbasefee() +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blobbasefee() +; CHECK-NEXT: [[TMP:%.*]] = shl i256 [[V1]], 1 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[V3]], [[TMP]] +; CHECK-NEXT: ret i256 [[RET]] +; + %v1 = call i256 @llvm.evm.blobbasefee() + %v2 = call i256 @llvm.evm.blobbasefee() + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.blobbasefee() + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + ret i256 %ret +} + define i256 @calldatasize() nounwind { ; CHECK-LABEL: define i256 @calldatasize ; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { @@ -322,7 +670,7 @@ define i256 @codesize() nounwind { define i256 @gasprice() nounwind { ; CHECK-LABEL: define i256 @gasprice -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gasprice() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -414,7 +762,7 @@ define i256 @returndatasize() nounwind { define i256 @blockhash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @blockhash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -427,7 +775,7 @@ define i256 @blockhash(i256 %rs1) nounwind { define i256 @blobhash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @blobhash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -440,7 +788,7 @@ define i256 @blobhash(i256 %rs1) nounwind { define i256 @coinbase() nounwind { ; CHECK-LABEL: define i256 @coinbase -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.coinbase() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -453,7 +801,7 @@ define i256 @coinbase() nounwind { define i256 @timestamp() nounwind { ; CHECK-LABEL: define i256 @timestamp -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.timestamp() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -466,7 +814,7 @@ define i256 @timestamp() nounwind { define i256 @number() nounwind { ; CHECK-LABEL: define i256 @number -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.number() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -479,7 +827,7 @@ define i256 @number() nounwind { define i256 @difficulty() nounwind { ; CHECK-LABEL: define i256 @difficulty -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.difficulty() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -492,7 +840,7 @@ define i256 @difficulty() nounwind { define i256 @gaslimit() nounwind { ; CHECK-LABEL: define i256 @gaslimit -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gaslimit() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -505,7 +853,7 @@ define i256 @gaslimit() nounwind { define i256 @chainid() nounwind { ; CHECK-LABEL: define i256 @chainid -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.chainid() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -551,7 +899,7 @@ define i256 @selfbalance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) define i256 @basefee() nounwind { ; CHECK-LABEL: define i256 @basefee -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.basefee() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -564,7 +912,7 @@ define i256 @basefee() nounwind { define i256 @blobbasefee() nounwind { ; CHECK-LABEL: define i256 @blobbasefee -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobbasefee() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] From 1526ad82957e88d14c97b3d64f7fa3d36f121ca7 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 8 Jul 2025 13:49:41 +0200 Subject: [PATCH 150/203] [EVM] Add pre-commit test for Clear transformation cache in EVMConstantUnfolding Add test that shows that we are wrongly doing the same unfold when optimizing for speed and for size. Signed-off-by: Vladimir Radosavljevic --- .../EVM/constant-unfolding-speed-and-Oz.ll | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll b/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll new file mode 100644 index 000000000000..251f656d2820 --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll @@ -0,0 +1,31 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s + +; Test that we are wrongly using the same unfold for speed and size tests. + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define i256 @test_speed() { +; CHECK-LABEL: test_speed: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + ret i256 115792082335569848633007197573932045576244532214531591869071028845388905840640 +} + +define i256 @test_size() minsize { +; CHECK-LABEL: test_size: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH1 0xE8 +; CHECK-NEXT: SHL +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + ret i256 115792082335569848633007197573932045576244532214531591869071028845388905840640 +} From e6b518ac7d80182f59615d849504ea71aeba3608 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 8 Jul 2025 13:56:26 +0200 Subject: [PATCH 151/203] [EVM] Clear transformation cache in EVMConstantUnfolding In some cases, EVMConstantUnfolding is not destroyed after the run, and can be reused for other functions. In that case, we don't want to reuse the cache from the previous runs. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMConstantUnfolding.cpp | 5 +++++ llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp index 61aa92c4934f..e3f8cfa28c98 100644 --- a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp +++ b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp @@ -575,5 +575,10 @@ bool EVMConstantUnfolding::runOnMachineFunction(MachineFunction &Mf) { Changed |= tryUnfoldConstant(MI); } } + + // Clear the transformation cache, since in some cases, EVMConstantUnfolding + // is not destroyed after the run, and can be reused for other functions. In + // that case, we don't want to reuse the cache from the previous runs. + TransformationCache.clear(); return Changed; } diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll b/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll index 251f656d2820..fb77fb8848bf 100644 --- a/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll +++ b/llvm/test/CodeGen/EVM/constant-unfolding-speed-and-Oz.ll @@ -1,7 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 ; RUN: llc -O3 < %s | FileCheck %s -; Test that we are wrongly using the same unfold for speed and size tests. +; Test that we are using different unfold for speed and size tests. target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" @@ -22,7 +22,8 @@ define i256 @test_size() minsize { ; CHECK-LABEL: test_size: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH3 0xFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT ; CHECK-NEXT: PUSH1 0xE8 ; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP1 From 051d116d6fe31f673f2d16c385d74f837e9dc8d1 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Mon, 7 Jul 2025 11:53:49 +0200 Subject: [PATCH 152/203] [EVM] Always inline functions with one call site This change also places the __entry function at the top of the module's function list. --- llvm/lib/Target/EVM/CMakeLists.txt | 3 + llvm/lib/Target/EVM/EVM.h | 9 +++ llvm/lib/Target/EVM/EVMAlwaysInline.cpp | 78 ++++++++++++++++++++++++ llvm/lib/Target/EVM/EVMModuleLayout.cpp | 73 ++++++++++++++++++++++ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 9 +++ llvm/test/CodeGen/EVM/always-inline.ll | 45 ++++++++++++++ llvm/test/CodeGen/EVM/module-layout.ll | 21 +++++++ 7 files changed, 238 insertions(+) create mode 100644 llvm/lib/Target/EVM/EVMAlwaysInline.cpp create mode 100644 llvm/lib/Target/EVM/EVMModuleLayout.cpp create mode 100644 llvm/test/CodeGen/EVM/always-inline.ll create mode 100644 llvm/test/CodeGen/EVM/module-layout.ll diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 50734f6f7f30..18b8301d07ef 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -38,6 +38,7 @@ add_custom_target(EVMStdLib DEPENDS "${EVM_STDLIB_PATH_OUT}") add_llvm_target(EVMCodeGen EVMAliasAnalysis.cpp EVMAllocaHoisting.cpp + EVMAlwaysInline.cpp EVMArgumentMove.cpp EVMAsmPrinter.cpp EVMBackwardPropagationStackification.cpp @@ -55,6 +56,7 @@ add_llvm_target(EVMCodeGen EVMMarkRecursiveFunctions.cpp EVMMCInstLower.cpp EVMMachineFunctionInfo.cpp + EVMModuleLayout.cpp EVMOptimizeLiveIntervals.cpp EVMRegColoring.cpp EVMRegisterInfo.cpp @@ -81,6 +83,7 @@ add_llvm_target(EVMCodeGen IRReader Linker MC + Passes Scalar SelectionDAG Support diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 1d08997b29e4..7a9783879409 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -56,6 +56,8 @@ ModulePass *createEVMLowerIntrinsicsPass(); FunctionPass *createEVMCodegenPreparePass(); ImmutablePass *createEVMAAWrapperPass(); ImmutablePass *createEVMExternalAAWrapperPass(); +ModulePass *createEVMAlwaysInlinePass(); +ModulePass *createEVMModuleLayoutPass(); // ISel and immediate followup passes. FunctionPass *createEVMISelDag(EVMTargetMachine &TM, @@ -90,6 +92,8 @@ void initializeEVMStackifyPass(PassRegistry &); void initializeEVMBPStackificationPass(PassRegistry &); void initializeEVMAAWrapperPassPass(PassRegistry &); void initializeEVMExternalAAWrapperPass(PassRegistry &); +void initializeEVMAlwaysInlinePass(PassRegistry &); +void initializeEVMModuleLayoutPass(PassRegistry &); void initializeEVMLowerJumpUnlessPass(PassRegistry &); void initializeEVMFinalizeStackFramesPass(PassRegistry &); void initializeEVMMarkRecursiveFunctionsPass(PassRegistry &); @@ -116,5 +120,10 @@ struct EVMMarkRecursiveFunctionsPass PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; +struct EVMAlwaysInlinePass : PassInfoMixin { + EVMAlwaysInlinePass() = default; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + } // namespace llvm #endif // LLVM_LIB_TARGET_EVM_EVM_H diff --git a/llvm/lib/Target/EVM/EVMAlwaysInline.cpp b/llvm/lib/Target/EVM/EVMAlwaysInline.cpp new file mode 100644 index 000000000000..4243c049699e --- /dev/null +++ b/llvm/lib/Target/EVM/EVMAlwaysInline.cpp @@ -0,0 +1,78 @@ +//===---- EVMAlwaysInline.cpp - Add alwaysinline attribute ------*- 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 pass adds alwaysinline attribute to functions with one call site. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" + +#define DEBUG_TYPE "evm-always-inline" + +using namespace llvm; + +namespace { + +class EVMAlwaysInline final : public ModulePass { +public: + static char ID; // Pass ID + EVMAlwaysInline() : ModulePass(ID) {} + + StringRef getPassName() const override { return "EVM always inline"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override; +}; + +} // end anonymous namespace + +static bool runImpl(Module &M) { + bool Changed = false; + for (auto &F : M) { + if (F.isDeclaration() || F.hasOptNone() || + F.hasFnAttribute(Attribute::NoInline) || !F.hasOneUse()) + continue; + + auto *Call = dyn_cast(*F.user_begin()); + + // Skip non call instructions, recursive calls, or calls with noinline + // attribute. + if (!Call || Call->getFunction() == &F || Call->isNoInline()) + continue; + + F.addFnAttr(Attribute::AlwaysInline); + Changed = true; + } + + return Changed; +} + +bool EVMAlwaysInline::runOnModule(Module &M) { + if (skipModule(M)) + return false; + return runImpl(M); +} + +char EVMAlwaysInline::ID = 0; + +INITIALIZE_PASS(EVMAlwaysInline, "evm-always-inline", + "Add alwaysinline attribute to functions with one call site", + false, false) + +ModulePass *llvm::createEVMAlwaysInlinePass() { return new EVMAlwaysInline; } + +PreservedAnalyses EVMAlwaysInlinePass::run(Module &M, + ModuleAnalysisManager &AM) { + runImpl(M); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/EVM/EVMModuleLayout.cpp b/llvm/lib/Target/EVM/EVMModuleLayout.cpp new file mode 100644 index 000000000000..0ed695377986 --- /dev/null +++ b/llvm/lib/Target/EVM/EVMModuleLayout.cpp @@ -0,0 +1,73 @@ +//===--- EVMModuleLayout.cpp - Proper arrangement of functions --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Ensures the '__entry' function is positioned at the beginning of the +// module’s function list. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "evm-module-layout" + +using namespace llvm; + +namespace { + +class EVMModuleLayout final : public ModulePass { +public: + static char ID; // Pass ID + EVMModuleLayout() : ModulePass(ID) {} + + StringRef getPassName() const override { return "EVM module layout"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override; +}; + +} // end anonymous namespace + +static bool runImpl(Module &M) { + LLVM_DEBUG({ dbgs() << "********** Module: " << M.getName() << '\n'; }); + auto &Functions = M.getFunctionList(); + auto It = find_if(Functions, [](const Function &F) { + return F.getName() == StringRef("__entry"); + }); + if (It == M.begin() || It == M.end()) + return false; + + LLVM_DEBUG({ + dbgs() << "Moving " << It->getName() + << " to the beginning of the functions list\n"; + }); + + Functions.splice(Functions.begin(), Functions, It); + return true; +} + +bool EVMModuleLayout::runOnModule(Module &M) { + if (skipModule(M)) + return false; + return runImpl(M); +} + +char EVMModuleLayout::ID = 0; + +INITIALIZE_PASS(EVMModuleLayout, "evm-module-layout", + "Places the '__entry' function at the beginning of the" + "function list", + false, false) + +ModulePass *llvm::createEVMModuleLayoutPass() { return new EVMModuleLayout; } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 557dede0069a..88b38717c017 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -64,6 +64,8 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMBPStackificationPass(PR); initializeEVMAAWrapperPassPass(PR); initializeEVMExternalAAWrapperPass(PR); + initializeEVMAlwaysInlinePass(PR); + initializeEVMModuleLayoutPass(PR); initializeEVMLowerJumpUnlessPass(PR); initializeEVMFinalizeStackFramesPass(PR); initializeEVMMarkRecursiveFunctionsPass(PR); @@ -131,6 +133,8 @@ void EVMTargetMachine::registerDefaultAliasAnalyses(AAManager &AAM) { void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PB.registerPipelineStartEPCallback( [](ModulePassManager &PM, OptimizationLevel Level) { + if (Level != OptimizationLevel::O0) + PM.addPass(EVMAlwaysInlinePass()); PM.addPass(EVMLinkRuntimePass()); PM.addPass(GlobalDCEPass()); PM.addPass(createModuleToFunctionPassAdaptor(EVMAllocaHoistingPass())); @@ -154,6 +158,10 @@ void EVMTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PM.addPass(EVMMarkRecursiveFunctionsPass()); return true; } + if (PassName == "evm-always-inline") { + PM.addPass(EVMAlwaysInlinePass()); + return true; + } return false; }); @@ -230,6 +238,7 @@ bool EVMPassConfig::addPreISel() { } void EVMPassConfig::addCodeGenPrepare() { + addPass(createEVMModuleLayoutPass()); addPass(createEVMCodegenPreparePass()); TargetPassConfig::addCodeGenPrepare(); } diff --git a/llvm/test/CodeGen/EVM/always-inline.ll b/llvm/test/CodeGen/EVM/always-inline.ll new file mode 100644 index 000000000000..126da7aa14b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/always-inline.ll @@ -0,0 +1,45 @@ +; RUN: opt -passes=evm-always-inline -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S32-a:256:256" +target triple = "evm" + +; CHECK: Function Attrs: alwaysinline +; CHECK-LABEL: @inline +define void @inline() { + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @noinline +define void @noinline() { + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @test +define void @test() { + call void @inline() + call void @noinline() + call void @noinline() + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @callattr +define void @callattr() { + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @test_noinline_callattr +define void @test_noinline_callattr() { + call void @callattr() noinline + ret void +} + +; CHECK-NOT: Function Attrs: alwaysinline +; CHECK-LABEL: @test_noinline_recursion +define void @test_noinline_recursion() { + call void @test_noinline_recursion() + ret void +} diff --git a/llvm/test/CodeGen/EVM/module-layout.ll b/llvm/test/CodeGen/EVM/module-layout.ll new file mode 100644 index 000000000000..d9647e15f1c7 --- /dev/null +++ b/llvm/test/CodeGen/EVM/module-layout.ll @@ -0,0 +1,21 @@ +; RUN: llc -O3 < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +; CHECK-LABEL: __entry: +; CHECK-LABEL: fun_fib: + +define private fastcc i256 @fun_fib(i256 %0) noinline { +entry: + %res = add i256 %0, 1 + ret i256 %res +} + +define void @__entry() noreturn { +entry: + %fun_res = tail call fastcc i256 @fun_fib(i256 7) + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 %fun_res) + unreachable +} From 0f03d060ba9ac60ab5f0fe92428348fca70ce8c7 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 26 Jun 2025 16:48:51 +0200 Subject: [PATCH 153/203] [EVM][BPS] Add pre-commit test for Change how we propagate immediates in optsize Signed-off-by: Vladimir Radosavljevic --- .../EVM/bps-imm-propagation-optsize.mir | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir diff --git a/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir b/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir new file mode 100644 index 000000000000..2f45e8d80aa6 --- /dev/null +++ b/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir @@ -0,0 +1,105 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +# RUN: llc -x mir -run-pass=evm-backward-propagation-stackification < %s | FileCheck %s + +--- | + source_filename = "test.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + define i256 @foo(i256 %arg1, i256 %arg2) #0 { + %srem = srem i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, %arg1 + %sdiv = sdiv i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, %arg2 + %add = add nsw i256 %sdiv, %srem + ret i256 %add + } + + attributes #0 = { minsize } + +... +--- +name: foo +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: false + numberOfParameters: 2 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments, $value_stack + + ; CHECK-LABEL: name: foo + ; CHECK: liveins: $arguments, $value_stack + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: PUSH32_S i256 1000000000000000000000000000000000000000000000000000000000000000000000000001 + ; CHECK-NEXT: SMOD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PUSH32_S i256 1000000000000000000000000000000000000000000000000000000000000000000000000001 + ; CHECK-NEXT: SDIV_S + ; CHECK-NEXT: ADD_S + ; CHECK-NEXT: SWAP1_S + ; CHECK-NEXT: PseudoRET + %0:gpr = ARGUMENT 0, implicit $arguments + %3:gpr = ARGUMENT 1, implicit $arguments + %1:gpr = CONST_I256 i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %2:gpr = SMOD %1, %0, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %6:gpr = CONST_I256 i256 1000000000000000000000000000000000000000000000000000000000000000000000000001, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %4:gpr = SDIV %6, %3, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + %5:gpr = nsw ADD %4, %2, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + RET %5, implicit-def dead $arguments, implicit-def $value_stack, implicit $value_stack + +... From e2ada110bec69155b487f6d787b4a9e5ed27f5b2 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 25 Jun 2025 16:41:06 +0200 Subject: [PATCH 154/203] [EVM][BPS] Don't rematerialize large constants in optsize In case we are optimizing for size, it is profitable to propagate large immediates, in case it will reduce code size (e.g. if the same large immediate is used in two different instructions in the same MBB, we can only once do PUSH instead of doing 2 PUSHes). Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 40 ++++++++++++++++++- llvm/lib/Target/EVM/EVMStackSolver.h | 8 ++++ .../EVM/bps-imm-propagation-optsize.mir | 6 ++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 426325044a69..33cfcb1c31a9 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -483,6 +483,44 @@ static bool spillRegHasNoUseOrDefBefore(const StackSlot *Slot, }); } +bool EVMStackSolver::shouldRemoveSlot(const StackSlot *Slot, + const MachineInstr &MI) const { + const auto *Literal = dyn_cast(Slot); + if (!Literal) + return Slot->isRematerializable(); + + // For now, remove all literals if we are not optimizing for size to reduce + // stack pressure. + if (!MI.getMF()->getFunction().hasOptSize()) + return true; + + const auto &Imm = Literal->getValue(); + + // PUSH0 is always profitable to remove, since it is one of the cheapest + // instructions. + if (Imm.isZero()) + return true; + + // Remove smaller literals, as they are not profitable to propagate and + // can increase stack pressure and potentially cause spilling. The minimum + // PUSH size of 4 is got after doing benchmarks with different sizes + // (9, 8, 6, 5, 4, 3). + const unsigned PushByteWidth = alignTo(Imm.getActiveBits(), 8) / 8; + constexpr unsigned MinPushByteWidth = 4; + if (PushByteWidth < MinPushByteWidth) + return true; + + // If there is no other use of the literal in the MBB, we can remove it + // from the stack to reduce stack pressure, otherwise we keep it to reduce + // code size. + return std::all_of( + std::next(MachineBasicBlock::const_reverse_iterator(MI)), + MI.getParent()->rend(), [Literal, this](const MachineInstr &Instr) { + return StackModel.skipMI(Instr) || + !is_contained(StackModel.getMIInput(Instr), Literal); + }); +} + Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, const MachineInstr &MI, bool CompressStack) { @@ -524,7 +562,7 @@ Stack EVMStackSolver::propagateThroughMI(const Stack &ExitStack, // Don't remove register spills if the register is used or defined before // MI. They are not cheap and ideally we shouldn't do more than one reload // in the BB. - if (BackSlot->isRematerializable() || + if (shouldRemoveSlot(BackSlot, MI) || spillRegHasNoUseOrDefBefore(BackSlot, MI)) { BeforeMI.pop_back(); } else if (auto Offset = diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h index 68d1a7a5dc30..840f291e489a 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.h +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -111,6 +111,14 @@ class EVMStackSolver { /// for function arguments and return values. bool hasUnreachableDef(const Register &Reg) const; + /// Return true if \p Slot is a rematerializable slot and it can be removed. + /// In cases of LiteralSlot, it checks if the literal is used before \p MI, + /// and if it is not, it can be removed to reduce stack pressure. In case it + /// is used, we don't remove it to reduce size of the stack, as PUSH + /// instructions are large. Currently, this check whether to propagate large + /// literals is only enabled when optimizing for size. + bool shouldRemoveSlot(const StackSlot *Slot, const MachineInstr &MI) const; + MachineFunction &MF; EVMStackModel &StackModel; const MachineLoopInfo *MLI; diff --git a/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir b/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir index 2f45e8d80aa6..daf8a6d4c10f 100644 --- a/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir +++ b/llvm/test/CodeGen/EVM/bps-imm-propagation-optsize.mir @@ -85,10 +85,12 @@ body: | ; CHECK-LABEL: name: foo ; CHECK: liveins: $arguments, $value_stack ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: PUSH32_S i256 1000000000000000000000000000000000000000000000000000000000000000000000000001 - ; CHECK-NEXT: SMOD_S ; CHECK-NEXT: SWAP1_S ; CHECK-NEXT: PUSH32_S i256 1000000000000000000000000000000000000000000000000000000000000000000000000001 + ; CHECK-NEXT: SWAP2_S + ; CHECK-NEXT: DUP3_S + ; CHECK-NEXT: SMOD_S + ; CHECK-NEXT: SWAP2_S ; CHECK-NEXT: SDIV_S ; CHECK-NEXT: ADD_S ; CHECK-NEXT: SWAP1_S From e632c5b1e7135f7da5fc86943faa01b31f1d3007 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 9 Jul 2025 10:12:45 +0200 Subject: [PATCH 155/203] [EVM] Don't rematerialize large constants in opt for speed Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMStackSolver.cpp | 7 +- llvm/lib/Target/EVM/EVMStackSolver.h | 3 +- .../CodeGen/EVM/bitmanipulation-intrinsics.ll | 72 ++++++++++--------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackSolver.cpp b/llvm/lib/Target/EVM/EVMStackSolver.cpp index 33cfcb1c31a9..d70e443b007e 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.cpp +++ b/llvm/lib/Target/EVM/EVMStackSolver.cpp @@ -489,11 +489,6 @@ bool EVMStackSolver::shouldRemoveSlot(const StackSlot *Slot, if (!Literal) return Slot->isRematerializable(); - // For now, remove all literals if we are not optimizing for size to reduce - // stack pressure. - if (!MI.getMF()->getFunction().hasOptSize()) - return true; - const auto &Imm = Literal->getValue(); // PUSH0 is always profitable to remove, since it is one of the cheapest @@ -506,7 +501,7 @@ bool EVMStackSolver::shouldRemoveSlot(const StackSlot *Slot, // PUSH size of 4 is got after doing benchmarks with different sizes // (9, 8, 6, 5, 4, 3). const unsigned PushByteWidth = alignTo(Imm.getActiveBits(), 8) / 8; - constexpr unsigned MinPushByteWidth = 4; + unsigned MinPushByteWidth = MI.getMF()->getFunction().hasOptSize() ? 4 : 8; if (PushByteWidth < MinPushByteWidth) return true; diff --git a/llvm/lib/Target/EVM/EVMStackSolver.h b/llvm/lib/Target/EVM/EVMStackSolver.h index 840f291e489a..fc19e4ee2bcc 100644 --- a/llvm/lib/Target/EVM/EVMStackSolver.h +++ b/llvm/lib/Target/EVM/EVMStackSolver.h @@ -115,8 +115,7 @@ class EVMStackSolver { /// In cases of LiteralSlot, it checks if the literal is used before \p MI, /// and if it is not, it can be removed to reduce stack pressure. In case it /// is used, we don't remove it to reduce size of the stack, as PUSH - /// instructions are large. Currently, this check whether to propagate large - /// literals is only enabled when optimizing for size. + /// instructions are large. bool shouldRemoveSlot(const StackSlot *Slot, const MachineInstr &MI) const; MachineFunction &MF; diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll index 27df73f32730..6377e70a84f9 100644 --- a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -517,16 +517,19 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP10 ; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x2 @@ -535,8 +538,7 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP7 -; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP9 ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR @@ -545,7 +547,7 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ; CHECK-NEXT: AND ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x2 @@ -586,7 +588,16 @@ define i256 @ctlztest(i256 %v) { ; CHECK-LABEL: ctlztest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 +; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP9 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR @@ -619,22 +630,17 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: SHR ; CHECK-NEXT: OR ; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 0xFF -; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 -; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: DUP10 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 -; CHECK-NEXT: DUP8 ; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP4 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x2 @@ -643,9 +649,7 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP7 -; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP10 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR @@ -653,7 +657,7 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x2 @@ -694,28 +698,32 @@ define i256 @cttztest(i256 %v) { ; CHECK-LABEL: cttztest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: DUP2 -; CHECK-NEXT: SUB -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: NOT -; CHECK-NEXT: AND ; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 ; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: DUP3 -; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 ; CHECK-NEXT: DUP8 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP10 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: NOT +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: DUP10 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x80 ; CHECK-NEXT: SHR ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: DUP4 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x2 @@ -724,9 +732,7 @@ define i256 @cttztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP7 -; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 +; CHECK-NEXT: SWAP10 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR @@ -734,7 +740,7 @@ define i256 @cttztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x2 From 5bc053772d4f1410efd8d1d19705b30314adad2e Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 16 Jul 2025 19:31:59 +0200 Subject: [PATCH 156/203] [EVM] Add O3 llc pipeline test --- llvm/test/CodeGen/EVM/O3-pipeline.ll | 160 +++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/O3-pipeline.ll diff --git a/llvm/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll new file mode 100644 index 000000000000..06326351a061 --- /dev/null +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -0,0 +1,160 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 -debug-pass=Structure < %s -o /dev/null 2>&1 | FileCheck %s +target triple = "evm" + +; REQUIRES: asserts + +; CHECK-LABEL: Pass Arguments: +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: Target Pass Configuration +; CHECK-NEXT: Machine Module Information +; CHECK-NEXT: Target Transform Information +; CHECK-NEXT: EVM Address space based Alias Analysis +; CHECK-NEXT: External Alias Analysis +; CHECK-NEXT: Type-Based Alias Analysis +; CHECK-NEXT: Scoped NoAlias Alias Analysis +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Profile summary info +; CHECK-NEXT: Create Garbage Collector Module Metadata +; CHECK-NEXT: Machine Branch Probability Analysis +; CHECK-NEXT: ModulePass Manager +; CHECK-NEXT: Pre-ISel Intrinsic Lowering +; CHECK-NEXT: EVM Lower Intrinsics +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Canonicalize natural loops +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Loop Pass Manager +; CHECK-NEXT: Canonicalize Freeze Instructions in Loops +; CHECK-NEXT: Induction Variable Users +; CHECK-NEXT: Loop Strength Reduction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Merge contiguous icmps into a memcmp +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Expand memcmp() to load/stores +; CHECK-NEXT: Lower Garbage Collection Instructions +; CHECK-NEXT: Shadow Stack GC Lowering +; CHECK-NEXT: Lower constant intrinsics +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Branch Probability Analysis +; CHECK-NEXT: Block Frequency Analysis +; CHECK-NEXT: Constant Hoisting +; CHECK-NEXT: Replace intrinsics with calls to vector library +; CHECK-NEXT: Partially inline calls to library functions +; CHECK-NEXT: Expand vector predication intrinsics +; CHECK-NEXT: Instrument function entry/exit with calls to e.g. mcount() (post inlining) +; CHECK-NEXT: Scalarize Masked Memory Intrinsics +; CHECK-NEXT: Expand reduction intrinsics +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: TLS Variable Hoist +; CHECK-NEXT: EVM module layout +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Final transformations before code generation +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: CodeGen Prepare +; CHECK-NEXT: Lower invoke and unwind, for unwindless code generators +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: EVM mark recursive functions +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Prepare callbr +; CHECK-NEXT: Safe Stack instrumentation pass +; CHECK-NEXT: Insert stack protectors +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) +; CHECK-NEXT: Function Alias Analysis Results +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Post-Dominator Tree Construction +; CHECK-NEXT: Branch Probability Analysis +; CHECK-NEXT: Assignment Tracking Analysis +; CHECK-NEXT: Lazy Branch Probability Analysis +; CHECK-NEXT: Lazy Block Frequency Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: EVM Argument Move +; CHECK-NEXT: Finalize ISel and expand pseudo-instructions +; CHECK-NEXT: Optimize machine instruction PHIs +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Merge disjoint stack slots +; CHECK-NEXT: Local Stack Slot Allocation +; CHECK-NEXT: Remove dead machine instructions +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Early Machine Loop Invariant Code Motion +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Machine Common Subexpression Elimination +; CHECK-NEXT: MachinePostDominator Tree Construction +; CHECK-NEXT: Machine Cycle Info Analysis +; CHECK-NEXT: Machine code sinking +; CHECK-NEXT: Peephole Optimizations +; CHECK-NEXT: Remove dead machine instructions +; CHECK-NEXT: Detect Dead Lanes +; CHECK-NEXT: Init Undef Pass +; CHECK-NEXT: Process Implicit Definitions +; CHECK-NEXT: Remove unreachable machine basic blocks +; CHECK-NEXT: Live Variable Analysis +; CHECK-NEXT: Eliminate PHI nodes for register allocation +; CHECK-NEXT: Two-Address instruction pass +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: Register Coalescer +; CHECK-NEXT: Rename Disconnected Subregister Components +; CHECK-NEXT: Machine Instruction Scheduler +; CHECK-NEXT: Remove Redundant DEBUG_VALUE analysis +; CHECK-NEXT: Fixup Statepoint Caller Saved +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Prologue/Epilogue Insertion & Frame Finalization +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Control Flow Optimizer +; CHECK-NEXT: Post-RA pseudo instruction expansion pass +; CHECK-NEXT: Insert fentry calls +; CHECK-NEXT: Insert XRay ops +; CHECK-NEXT: EVM split critical edges +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: EVM Optimize Live Intervals +; CHECK-NEXT: EVM Single use expressions +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Virtual Register Map +; CHECK-NEXT: Live Stack Slot Analysis +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: EVM backward propagation stackification +; CHECK-NEXT: Stack Slot Coloring +; CHECK-NEXT: EVM finalize stack frames +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: Control Flow Optimizer +; CHECK-NEXT: Machine Sanitizer Binary Metadata +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Stack Frame Layout Analysis +; CHECK-NEXT: EVM Lower jump_unless +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: EVM constant unfolding +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: EVM Assembly +; CHECK-NEXT: Free MachineFunction + +define void @f() { + ret void +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} From 9137a49429786f1f94f757c36dd272c874633950 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 16 Jul 2025 14:17:38 +0200 Subject: [PATCH 157/203] [EVM] Don't preserve analysis in EVMSingleUseExpression Calculate fresh analysis before the stackification. In some cases, register weights can be different between two runs of the LLVM BE because stack-too-deep is detected. This can change which register to spill between two runs and can lead to having bigger spill region in the second run. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMSingleUseExpression.cpp | 3 --- llvm/test/CodeGen/EVM/O3-pipeline.ll | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index dbff24f6c86c..3c04ae94ecff 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -42,9 +42,6 @@ class EVMSingleUseExpression 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/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll index 06326351a061..8adab6f5f86f 100644 --- a/llvm/test/CodeGen/EVM/O3-pipeline.ll +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -128,6 +128,8 @@ target triple = "evm" ; CHECK-NEXT: Live Interval Analysis ; CHECK-NEXT: EVM Optimize Live Intervals ; CHECK-NEXT: EVM Single use expressions +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis ; CHECK-NEXT: Machine Natural Loop Construction ; CHECK-NEXT: Virtual Register Map ; CHECK-NEXT: Live Stack Slot Analysis From c1e5e4c6692f05eb98b685456babf9beb5a12d05 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 16 Jul 2025 14:20:25 +0200 Subject: [PATCH 158/203] [EVM] Change scheduling preference to regpressure Between two stack-too-deep runs, scheduler can reorder loads, so in some cases stackification algorithm can behave differently. Fix this by setting scheduling preference to regpressure so we get identical code before the stackification. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMISelLowering.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index f771def5eba8..30955cca171b 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -29,6 +29,8 @@ EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, const EVMSubtarget &STI) : TargetLowering(TM), Subtarget(&STI) { + setSchedulingPreference(Sched::RegPressure); + // Booleans always contain 0 or 1. setBooleanContents(ZeroOrOneBooleanContent); From 879de3f04a7b90218b6477fc2a9008d908246414 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 22 Jul 2025 10:51:10 +0200 Subject: [PATCH 159/203] [EVM] Add pre-commit test for Don't emit JUMPDEST in entry point of bytecode Signed-off-by: Vladimir Radosavljevic --- .../CodeGen/EVM/jumpdest-in-entry-point.ll | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll diff --git a/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll new file mode 100644 index 000000000000..eafa2cb8bed5 --- /dev/null +++ b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O3 < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +declare void @llvm.evm.return(ptr addrspace(1), i256) + +define void @__entry() noreturn { +; CHECK-LABEL: __entry: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: MLOAD +; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_2: ; %bb1 +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: MSTORE +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + %load = load i256, ptr addrspace(1) null, align 64 + %cmp = icmp eq i256 %load, 0 + br i1 %cmp, label %bb2, label %bb1 + +bb1: + br label %bb2 + +bb2: + %phi = phi i256 [ 0, %entry ], [ %load, %bb1 ] + store i256 %phi, ptr addrspace(1) null, align 64 + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} From 238cd16cce3c12e417c0f8f18f567d7650f08bcf Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 22 Jul 2025 10:50:14 +0200 Subject: [PATCH 160/203] [EVM] Don't emit JUMPDEST in entry point of bytecode We don't need to emit JUMPDEST instruction in the first BB of the __entry function. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 11 ++++++++++- llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll | 1 - 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index c872edba21b4..997d0be6efb8 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -127,9 +127,18 @@ void EVMAsmPrinter::emitFunctionBodyEnd() { FirstFunctIsHandled = true; } void EVMAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { AsmPrinter::emitBasicBlockStart(MBB); + // If this is __entry function, we don't need to emit JUMPDEST + // instruction in the first basic block, as it is the entry point + // of the EVM bytecode. + auto IsEntryPoint = [this](const MachineBasicBlock &MBB) { + return !FirstFunctIsHandled && &MBB == &MF->front() && + MF->getName() == "__entry"; + }; + // Emit JUMPDEST instruction at the beginning of the basic block, if // this is not a block that is only reachable by fallthrough. - if (!EVMKeepRegisters && !AsmPrinter::isBlockOnlyReachableByFallthrough(&MBB)) + if (!EVMKeepRegisters && !IsEntryPoint(MBB) && + !AsmPrinter::isBlockOnlyReachableByFallthrough(&MBB)) emitJumpDest(); } diff --git a/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll index eafa2cb8bed5..132b7c485602 100644 --- a/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll +++ b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll @@ -9,7 +9,6 @@ declare void @llvm.evm.return(ptr addrspace(1), i256) define void @__entry() noreturn { ; CHECK-LABEL: __entry: ; CHECK: ; %bb.0: ; %entry -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: MLOAD ; CHECK-NEXT: DUP1 From 44712fbccc4da9e214dca11d1795f0e64adeadf0 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 21 Jul 2025 16:36:27 +0200 Subject: [PATCH 161/203] [EVM][DAGCombine] Add pre-commit tests for Implement TLI hooks related to select Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/select-const.ll | 471 ++++++++++++++++++++++ llvm/test/CodeGen/EVM/select-normalize.ll | 196 +++++++++ 2 files changed, 667 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/select-const.ll create mode 100644 llvm/test/CodeGen/EVM/select-normalize.ll diff --git a/llvm/test/CodeGen/EVM/select-const.ll b/llvm/test/CodeGen/EVM/select-const.ll new file mode 100644 index 000000000000..9ea493d4e9ef --- /dev/null +++ b/llvm/test/CodeGen/EVM/select-const.ll @@ -0,0 +1,471 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @select_const_int_easy(i1 %a) { +; CHECK-LABEL: select_const_int_easy: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 1, i256 0 + ret i256 %1 +} + +define i256 @select_const_int_one_away(i1 %a) { +; CHECK-LABEL: select_const_int_one_away: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x3 +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: .BB1_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 3, i256 4 + ret i256 %1 +} + +define i256 @select_const_int_pow2_zero(i1 %a) { +; CHECK-LABEL: select_const_int_pow2_zero: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x4 +; CHECK-NEXT: PUSH4 @.BB2_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB2_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB2_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 4, i256 0 + ret i256 %1 +} + +define i256 @select_const_int_harder(i1 %a) { +; CHECK-LABEL: select_const_int_harder: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB3_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x6 +; CHECK-NEXT: PUSH4 @.BB3_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB3_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x26 +; CHECK-NEXT: .BB3_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 6, i256 38 + ret i256 %1 +} + +define i256 @select_eq_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH4 @.BB4_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB4_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB4_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB4_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ne_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB5_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB5_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB5_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB5_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_sgt_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_sgt_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB6_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB6_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB6_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB6_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp sgt i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_slt_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_slt_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB7_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB7_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB7_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB7_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp slt i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_sge_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_sge_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SLT +; CHECK-NEXT: PUSH4 @.BB8_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB8_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB8_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB8_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp sge i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_sle_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_sle_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SGT +; CHECK-NEXT: PUSH4 @.BB9_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB9_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB9_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB9_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp sle i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ugt_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ugt_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB10_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB10_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB10_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB10_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ugt i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ult_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ult_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB11_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB11_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB11_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB11_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ult i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_uge_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_uge_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: PUSH4 @.BB12_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB12_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB12_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB12_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp uge i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_ule_zero_negone(i256 %a, i256 %b) { +; CHECK-LABEL: select_ule_zero_negone: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: GT +; CHECK-NEXT: PUSH4 @.BB13_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB13_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB13_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: .BB13_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ule i256 %a, %b + %2 = select i1 %1, i256 -1, i256 0 + ret i256 %2 +} + +define i256 @select_eq_1_2(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_1_2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH4 @.BB14_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH4 @.BB14_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB14_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: .BB14_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 1, i256 2 + ret i256 %2 +} + +define i256 @select_ne_1_2(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_1_2: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB15_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH4 @.BB15_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB15_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: .BB15_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 1, i256 2 + ret i256 %2 +} + +define i256 @select_eq_2_1(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_2_1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH4 @.BB16_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: PUSH4 @.BB16_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB16_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: .BB16_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 2, i256 1 + ret i256 %2 +} + +define i256 @select_ne_2_1(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_2_1: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB17_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: PUSH4 @.BB17_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB17_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: .BB17_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 2, i256 1 + ret i256 %2 +} + +define i256 @select_eq_10000_10001(i256 %a, i256 %b) { +; CHECK-LABEL: select_eq_10000_10001: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: PUSH4 @.BB18_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH2 0x2711 +; CHECK-NEXT: PUSH4 @.BB18_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB18_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0x2712 +; CHECK-NEXT: .BB18_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp eq i256 %a, %b + %2 = select i1 %1, i256 10001, i256 10002 + ret i256 %2 +} + +define i256 @select_ne_10001_10002(i256 %a, i256 %b) { +; CHECK-LABEL: select_ne_10001_10002: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ +; CHECK-NEXT: PUSH4 @.BB19_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH2 0x2711 +; CHECK-NEXT: PUSH4 @.BB19_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB19_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0x2712 +; CHECK-NEXT: .BB19_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = icmp ne i256 %a, %b + %2 = select i1 %1, i256 10001, i256 10002 + ret i256 %2 +} diff --git a/llvm/test/CodeGen/EVM/select-normalize.ll b/llvm/test/CodeGen/EVM/select-normalize.ll new file mode 100644 index 000000000000..3d0667def52c --- /dev/null +++ b/llvm/test/CodeGen/EVM/select-normalize.ll @@ -0,0 +1,196 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @select_and(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 %a5) { +; CHECK-LABEL: select_and: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: LT +; CHECK-NEXT: PUSH4 @.BB0_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: POP +; CHECK-NEXT: DUP3 +; CHECK-NEXT: .BB0_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB0_4 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.3: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB0_5 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB0_4: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB0_5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %cmp1 = icmp ult i256 %a0, %a1 + %cmp2 = icmp ult i256 %a2, %a3 + %and = and i1 %cmp1, %cmp2 + %select = select i1 %and, i256 %a4, i256 %a5 + ret i256 %select +} + +define i256 @select_or(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 %a5) { +; CHECK-LABEL: select_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP5 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: DUP5 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB1_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB1_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: .BB1_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: LT +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB1_5 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.4: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB1_6 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB1_5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB1_6: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %cmp1 = icmp ult i256 %a0, %a1 + %cmp2 = icmp ult i256 %a2, %a3 + %or = or i1 %cmp1, %cmp2 + %select = select i1 %or, i256 %a4, i256 %a5 + ret i256 %select +} + +define i256 @select_select_to_and(i1 %cond1, i1 %cond2, i256 %a, i256 %b) { +; CHECK-LABEL: select_select_to_and: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH4 @.BB2_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB2_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: .BB2_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB2_5 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.4: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB2_6 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB2_5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB2_6: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %select1 = select i1 %cond1, i256 %a, i256 %b + %select2 = select i1 %cond2, i256 %select1, i256 %b + ret i256 %select2 +} + +define i256 @select_select_to_or(i1 %cond1, i1 %cond2, i256 %a, i256 %b) { +; CHECK-LABEL: select_select_to_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP4 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB3_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB3_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB3_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: .BB3_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB3_5 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.4: +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH4 @.BB3_6 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB3_5: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: .BB3_6: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: JUMP + %select1 = select i1 %cond1, i256 %a, i256 %b + %select2 = select i1 %cond2, i256 %a, i256 %select1 + ret i256 %select2 +} From 7e564b9d5c26d99ffea435f34259551ab6df9a80 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 21 Jul 2025 15:10:42 +0200 Subject: [PATCH 162/203] [EVM][DAGCombine] Implement TLI hooks related to select These hooks will reduce number of select instructions. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMISelLowering.h | 18 ++ .../EVM/dont-avoid-shift-transformations.ll | 7 +- llvm/test/CodeGen/EVM/select-const.ll | 236 +++--------------- llvm/test/CodeGen/EVM/select-normalize.ll | 105 ++------ 4 files changed, 75 insertions(+), 291 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 4f2efbc25d19..4309fc0a6f32 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -115,6 +115,24 @@ class EVMTargetLowering final : public TargetLowering { return false; } + /// Returns true if we should normalize + /// select(N0&N1, X, Y) => select(N0, select(N1, X, Y), Y) and + /// select(N0|N1, X, Y) => select(N0, select(N1, X, Y, Y)) if it is likely + /// that it saves us from materializing N0 and N1 in an integer register. + /// For EVM, selects are expensive, so generating more selects is not + /// beneficial. + bool shouldNormalizeToSelectSequence(LLVMContext &Context, + EVT VT) const override { + return false; + } + + /// Return true if a select of constants (select Cond, C1, C2) should be + /// transformed into simple math ops with the condition value. For example: + /// select Cond, C1, C1-1 --> add (zext Cond), C1-1 + /// For EVM, selects are expensive, so reducing number of selects is + /// beneficial. + bool convertSelectOfConstantsToMath(EVT VT) const override { return true; } + private: const EVMSubtarget *Subtarget; diff --git a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll index 1ca75b3e86f0..7db854e4c5bf 100644 --- a/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll +++ b/llvm/test/CodeGen/EVM/dont-avoid-shift-transformations.ll @@ -119,11 +119,10 @@ define i256 @test7(i256 %x) { ; CHECK-LABEL: test7: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0x2 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 0xFE +; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: SHR -; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP entry: diff --git a/llvm/test/CodeGen/EVM/select-const.ll b/llvm/test/CodeGen/EVM/select-const.ll index 9ea493d4e9ef..6e129aa33c31 100644 --- a/llvm/test/CodeGen/EVM/select-const.ll +++ b/llvm/test/CodeGen/EVM/select-const.ll @@ -22,18 +22,8 @@ define i256 @select_const_int_one_away(i1 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB1_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x3 -; CHECK-NEXT: PUSH4 @.BB1_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB1_2: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 0x4 -; CHECK-NEXT: .BB1_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 3, i256 4 @@ -46,18 +36,8 @@ define i256 @select_const_int_pow2_zero(i1 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB2_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x4 -; CHECK-NEXT: PUSH4 @.BB2_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB2_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB2_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x2 +; CHECK-NEXT: SHL ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 4, i256 0 @@ -92,19 +72,9 @@ define i256 @select_eq_zero_negone(i256 %a, i256 %b) { ; CHECK-LABEL: select_eq_zero_negone: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH4 @.BB4_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB4_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB4_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB4_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp eq i256 %a, %b @@ -117,18 +87,9 @@ define i256 @select_ne_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: EQ -; CHECK-NEXT: PUSH4 @.BB5_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB5_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB5_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB5_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ne i256 %a, %b @@ -141,19 +102,8 @@ define i256 @select_sgt_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SGT -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB6_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB6_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB6_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB6_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp sgt i256 %a, %b @@ -166,19 +116,8 @@ define i256 @select_slt_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SLT -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB7_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB7_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB7_2: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB7_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp slt i256 %a, %b @@ -191,18 +130,9 @@ define i256 @select_sge_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SLT -; CHECK-NEXT: PUSH4 @.BB8_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB8_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB8_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB8_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp sge i256 %a, %b @@ -215,18 +145,9 @@ define i256 @select_sle_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SGT -; CHECK-NEXT: PUSH4 @.BB9_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB9_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB9_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB9_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp sle i256 %a, %b @@ -239,19 +160,8 @@ define i256 @select_ugt_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: GT -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB10_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB10_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB10_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB10_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ugt i256 %a, %b @@ -264,19 +174,8 @@ define i256 @select_ult_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: LT -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB11_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB11_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB11_2: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB11_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ult i256 %a, %b @@ -289,18 +188,9 @@ define i256 @select_uge_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: LT -; CHECK-NEXT: PUSH4 @.BB12_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB12_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB12_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB12_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp uge i256 %a, %b @@ -313,18 +203,9 @@ define i256 @select_ule_zero_negone(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: GT -; CHECK-NEXT: PUSH4 @.BB13_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB13_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB13_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: .BB13_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ule i256 %a, %b @@ -336,18 +217,9 @@ define i256 @select_eq_1_2(i256 %a, i256 %b) { ; CHECK-LABEL: select_eq_1_2: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH4 @.BB14_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: PUSH4 @.BB14_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB14_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ ; CHECK-NEXT: PUSH1 0x2 -; CHECK-NEXT: .BB14_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp eq i256 %a, %b @@ -360,17 +232,9 @@ define i256 @select_ne_1_2(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: EQ -; CHECK-NEXT: PUSH4 @.BB15_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: PUSH4 @.BB15_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB15_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH1 0x2 -; CHECK-NEXT: .BB15_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ne i256 %a, %b @@ -382,18 +246,10 @@ define i256 @select_eq_2_1(i256 %a, i256 %b) { ; CHECK-LABEL: select_eq_2_1: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH4 @.BB16_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x2 -; CHECK-NEXT: PUSH4 @.BB16_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB16_2: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: .BB16_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: EQ +; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp eq i256 %a, %b @@ -405,18 +261,11 @@ define i256 @select_ne_2_1(i256 %a, i256 %b) { ; CHECK-LABEL: select_ne_2_1: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: EQ -; CHECK-NEXT: PUSH4 @.BB17_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x2 -; CHECK-NEXT: PUSH4 @.BB17_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB17_2: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: .BB17_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ne i256 %a, %b @@ -428,18 +277,9 @@ define i256 @select_eq_10000_10001(i256 %a, i256 %b) { ; CHECK-LABEL: select_eq_10000_10001: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SUB -; CHECK-NEXT: PUSH4 @.BB18_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH2 0x2711 -; CHECK-NEXT: PUSH4 @.BB18_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB18_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: EQ ; CHECK-NEXT: PUSH2 0x2712 -; CHECK-NEXT: .BB18_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp eq i256 %a, %b @@ -452,17 +292,9 @@ define i256 @select_ne_10001_10002(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: EQ -; CHECK-NEXT: PUSH4 @.BB19_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH2 0x2711 -; CHECK-NEXT: PUSH4 @.BB19_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB19_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH2 0x2712 -; CHECK-NEXT: .BB19_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = icmp ne i256 %a, %b diff --git a/llvm/test/CodeGen/EVM/select-normalize.ll b/llvm/test/CodeGen/EVM/select-normalize.ll index 3d0667def52c..0e8d464ff0b9 100644 --- a/llvm/test/CodeGen/EVM/select-normalize.ll +++ b/llvm/test/CodeGen/EVM/select-normalize.ll @@ -8,35 +8,27 @@ define i256 @select_and(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 % ; CHECK-LABEL: select_and: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP4 -; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SWAP2 ; CHECK-NEXT: LT -; CHECK-NEXT: PUSH4 @.BB0_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: POP -; CHECK-NEXT: DUP3 -; CHECK-NEXT: .BB0_2: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: LT +; CHECK-NEXT: AND ; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB0_4 +; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.3: +; CHECK-NEXT: ; %bb.1: ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH4 @.BB0_5 +; CHECK-NEXT: PUSH4 @.BB0_3 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_4: +; CHECK-NEXT: .BB0_2: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: POP ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB0_5: +; CHECK-NEXT: .BB0_3: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP %cmp1 = icmp ult i256 %a0, %a1 @@ -50,18 +42,18 @@ define i256 @select_or(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 %a ; CHECK-LABEL: select_or: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: SWAP5 -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: SWAP3 -; CHECK-NEXT: DUP5 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: LT ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: LT +; CHECK-NEXT: OR ; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB1_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: SWAP4 +; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP ; CHECK-NEXT: PUSH4 @.BB1_3 @@ -69,24 +61,8 @@ define i256 @select_or(i256 %a0, i256 %a1, i256 %a2, i256 %a3, i256 %a4, i256 %a ; CHECK-NEXT: .BB1_2: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: POP -; CHECK-NEXT: SWAP3 -; CHECK-NEXT: .BB1_3: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: LT -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB1_5 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.4: -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH4 @.BB1_6 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB1_5: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB1_6: +; CHECK-NEXT: .BB1_3: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP %cmp1 = icmp ult i256 %a0, %a1 @@ -101,42 +77,23 @@ define i256 @select_select_to_and(i1 %cond1, i1 %cond2, i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: SWAP3 ; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SWAP3 +; CHECK-NEXT: AND ; CHECK-NEXT: AND ; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB2_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH4 @.BB2_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB2_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: DUP3 -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: .BB2_3: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB2_5 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.4: ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH4 @.BB2_6 +; CHECK-NEXT: PUSH4 @.BB2_3 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB2_5: +; CHECK-NEXT: .BB2_2: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: POP ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB2_6: +; CHECK-NEXT: .BB2_3: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP %select1 = select i1 %cond1, i256 %a, i256 %b @@ -148,47 +105,25 @@ define i256 @select_select_to_or(i1 %cond1, i1 %cond2, i256 %a, i256 %b) { ; CHECK-LABEL: select_select_to_or: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DUP3 -; CHECK-NEXT: SWAP4 -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SWAP4 -; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: OR ; CHECK-NEXT: AND ; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB3_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: SWAP3 -; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP ; CHECK-NEXT: PUSH4 @.BB3_3 ; CHECK-NEXT: JUMP ; CHECK-NEXT: .BB3_2: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: SWAP3 ; CHECK-NEXT: .BB3_3: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB3_5 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.4: -; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH4 @.BB3_6 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB3_5: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: POP -; CHECK-NEXT: SWAP1 -; CHECK-NEXT: .BB3_6: -; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: JUMP %select1 = select i1 %cond1, i256 %a, i256 %b %select2 = select i1 %cond2, i256 %a, i256 %select1 From 58c60e22ad26fc24306faac1e0cc39728bb645d1 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 23 Jul 2025 18:03:59 +0200 Subject: [PATCH 163/203] [EVM] Remove EVMModuleLayout pass After FE changes, entry function has to be the first in the module, so this pass is not needed anymore. Instead, add assert in EVMAsmPrinter that function with "evm-entry-function" attribute has to be the first function in the module. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/CMakeLists.txt | 1 - llvm/lib/Target/EVM/EVM.h | 2 - llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 5 ++ llvm/lib/Target/EVM/EVMModuleLayout.cpp | 73 ------------------------ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 2 - llvm/test/CodeGen/EVM/O3-pipeline.ll | 2 - llvm/test/CodeGen/EVM/module-layout.ll | 7 +-- 7 files changed, 8 insertions(+), 84 deletions(-) delete mode 100644 llvm/lib/Target/EVM/EVMModuleLayout.cpp diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 18b8301d07ef..c86f17337869 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -56,7 +56,6 @@ add_llvm_target(EVMCodeGen EVMMarkRecursiveFunctions.cpp EVMMCInstLower.cpp EVMMachineFunctionInfo.cpp - EVMModuleLayout.cpp EVMOptimizeLiveIntervals.cpp EVMRegColoring.cpp EVMRegisterInfo.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 7a9783879409..df60301eb3b1 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -57,7 +57,6 @@ FunctionPass *createEVMCodegenPreparePass(); ImmutablePass *createEVMAAWrapperPass(); ImmutablePass *createEVMExternalAAWrapperPass(); ModulePass *createEVMAlwaysInlinePass(); -ModulePass *createEVMModuleLayoutPass(); // ISel and immediate followup passes. FunctionPass *createEVMISelDag(EVMTargetMachine &TM, @@ -93,7 +92,6 @@ void initializeEVMBPStackificationPass(PassRegistry &); void initializeEVMAAWrapperPassPass(PassRegistry &); void initializeEVMExternalAAWrapperPass(PassRegistry &); void initializeEVMAlwaysInlinePass(PassRegistry &); -void initializeEVMModuleLayoutPass(PassRegistry &); void initializeEVMLowerJumpUnlessPass(PassRegistry &); void initializeEVMFinalizeStackFramesPass(PassRegistry &); void initializeEVMMarkRecursiveFunctionsPass(PassRegistry &); diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index 997d0be6efb8..aa9111ffecc7 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -102,6 +102,11 @@ void EVMAsmPrinter::emitFunctionEntryLabel() { } void EVMAsmPrinter::emitFunctionBodyStart() { + if (MF->getFunction().hasFnAttribute("evm-entry-function") && + FirstFunctIsHandled) + report_fatal_error("Entry function '" + MF->getName() + + "' isn't the first function in the module."); + if (const auto *MFI = MF->getInfo(); MFI->getHasPushDeployAddress()) { // TODO: #778. Move the function with PUSHDEPLOYADDRESS to the diff --git a/llvm/lib/Target/EVM/EVMModuleLayout.cpp b/llvm/lib/Target/EVM/EVMModuleLayout.cpp deleted file mode 100644 index 0ed695377986..000000000000 --- a/llvm/lib/Target/EVM/EVMModuleLayout.cpp +++ /dev/null @@ -1,73 +0,0 @@ -//===--- EVMModuleLayout.cpp - Proper arrangement of functions --*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Ensures the '__entry' function is positioned at the beginning of the -// module’s function list. -// -//===----------------------------------------------------------------------===// - -#include "EVM.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" - -#define DEBUG_TYPE "evm-module-layout" - -using namespace llvm; - -namespace { - -class EVMModuleLayout final : public ModulePass { -public: - static char ID; // Pass ID - EVMModuleLayout() : ModulePass(ID) {} - - StringRef getPassName() const override { return "EVM module layout"; } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - ModulePass::getAnalysisUsage(AU); - } - - bool runOnModule(Module &M) override; -}; - -} // end anonymous namespace - -static bool runImpl(Module &M) { - LLVM_DEBUG({ dbgs() << "********** Module: " << M.getName() << '\n'; }); - auto &Functions = M.getFunctionList(); - auto It = find_if(Functions, [](const Function &F) { - return F.getName() == StringRef("__entry"); - }); - if (It == M.begin() || It == M.end()) - return false; - - LLVM_DEBUG({ - dbgs() << "Moving " << It->getName() - << " to the beginning of the functions list\n"; - }); - - Functions.splice(Functions.begin(), Functions, It); - return true; -} - -bool EVMModuleLayout::runOnModule(Module &M) { - if (skipModule(M)) - return false; - return runImpl(M); -} - -char EVMModuleLayout::ID = 0; - -INITIALIZE_PASS(EVMModuleLayout, "evm-module-layout", - "Places the '__entry' function at the beginning of the" - "function list", - false, false) - -ModulePass *llvm::createEVMModuleLayoutPass() { return new EVMModuleLayout; } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 88b38717c017..560bb39679a2 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -65,7 +65,6 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMAAWrapperPassPass(PR); initializeEVMExternalAAWrapperPass(PR); initializeEVMAlwaysInlinePass(PR); - initializeEVMModuleLayoutPass(PR); initializeEVMLowerJumpUnlessPass(PR); initializeEVMFinalizeStackFramesPass(PR); initializeEVMMarkRecursiveFunctionsPass(PR); @@ -238,7 +237,6 @@ bool EVMPassConfig::addPreISel() { } void EVMPassConfig::addCodeGenPrepare() { - addPass(createEVMModuleLayoutPass()); addPass(createEVMCodegenPreparePass()); TargetPassConfig::addCodeGenPrepare(); } diff --git a/llvm/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll index 8adab6f5f86f..4feadf7c2075 100644 --- a/llvm/test/CodeGen/EVM/O3-pipeline.ll +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -55,8 +55,6 @@ target triple = "evm" ; CHECK-NEXT: Expand reduction intrinsics ; CHECK-NEXT: Natural Loop Information ; CHECK-NEXT: TLS Variable Hoist -; CHECK-NEXT: EVM module layout -; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Final transformations before code generation ; CHECK-NEXT: Dominator Tree Construction ; CHECK-NEXT: Natural Loop Information diff --git a/llvm/test/CodeGen/EVM/module-layout.ll b/llvm/test/CodeGen/EVM/module-layout.ll index d9647e15f1c7..4a9e47714fbb 100644 --- a/llvm/test/CodeGen/EVM/module-layout.ll +++ b/llvm/test/CodeGen/EVM/module-layout.ll @@ -1,11 +1,10 @@ -; RUN: llc -O3 < %s | FileCheck %s +; RUN: not --crash llc -O3 < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" declare void @llvm.evm.return(ptr addrspace(1), i256) -; CHECK-LABEL: __entry: -; CHECK-LABEL: fun_fib: +; CHECK: LLVM ERROR: Entry function '__entry' isn't the first function in the module. define private fastcc i256 @fun_fib(i256 %0) noinline { entry: @@ -13,7 +12,7 @@ entry: ret i256 %res } -define void @__entry() noreturn { +define void @__entry() noreturn "evm-entry-function" { entry: %fun_res = tail call fastcc i256 @fun_fib(i256 7) tail call void @llvm.evm.return(ptr addrspace(1) null, i256 %fun_res) From 575acfaf4697d03115c55ad0a0f9263707264fe3 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 23 Jul 2025 18:10:32 +0200 Subject: [PATCH 164/203] [EVM] Don't check for function name to detect entry function After FE changes, "evm-entry-function" attribute is set to the entry function. Instead of checking the function name check for this attribute. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 4 ++-- llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index aa9111ffecc7..1db8252f8155 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -132,12 +132,12 @@ void EVMAsmPrinter::emitFunctionBodyEnd() { FirstFunctIsHandled = true; } void EVMAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { AsmPrinter::emitBasicBlockStart(MBB); - // If this is __entry function, we don't need to emit JUMPDEST + // If this is the entry function, we don't need to emit JUMPDEST // instruction in the first basic block, as it is the entry point // of the EVM bytecode. auto IsEntryPoint = [this](const MachineBasicBlock &MBB) { return !FirstFunctIsHandled && &MBB == &MF->front() && - MF->getName() == "__entry"; + MF->getFunction().hasFnAttribute("evm-entry-function"); }; // Emit JUMPDEST instruction at the beginning of the basic block, if diff --git a/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll index 132b7c485602..ebab82b6d3bf 100644 --- a/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll +++ b/llvm/test/CodeGen/EVM/jumpdest-in-entry-point.ll @@ -6,7 +6,7 @@ target triple = "evm" declare void @llvm.evm.return(ptr addrspace(1), i256) -define void @__entry() noreturn { +define void @__entry() noreturn "evm-entry-function" { ; CHECK-LABEL: __entry: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: PUSH0 From ad449387ad24051f3fe8b2c6f57cd3c32359a6c3 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Tue, 15 Jul 2025 13:46:13 +0200 Subject: [PATCH 165/203] [EVM] Support data sections in the code This is achieved by emitting global variables with constant initializers into the code section. Removed unused patters from TableGen. --- llvm/lib/Target/EVM/EVMAsmPrinter.cpp | 84 ++++++++++++++++++- llvm/lib/Target/EVM/EVMISelLowering.cpp | 10 ++- llvm/lib/Target/EVM/EVMInstrInfo.td | 11 --- llvm/lib/Target/EVM/EVMMCInstLower.cpp | 20 ++++- llvm/lib/Target/EVM/EVMMCInstLower.h | 5 +- llvm/lib/Target/EVM/EVMStackModel.cpp | 14 +--- .../lib/Target/EVM/EVMStackifyCodeEmitter.cpp | 10 ++- .../EVM/MCTargetDesc/EVMMCCodeEmitter.cpp | 47 +++++++---- .../EVM/data-sections-in-code-glob-order.ll | 62 ++++++++++++++ .../test/CodeGen/EVM/data-sections-in-code.ll | 52 ++++++++++++ llvm/test/MC/EVM/glob-symbol-with-offset.ll | 36 ++++++++ 11 files changed, 302 insertions(+), 49 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll create mode 100644 llvm/test/CodeGen/EVM/data-sections-in-code.ll create mode 100644 llvm/test/MC/EVM/glob-symbol-with-offset.ll diff --git a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp index 1db8252f8155..802edb1401eb 100644 --- a/llvm/lib/Target/EVM/EVMAsmPrinter.cpp +++ b/llvm/lib/Target/EVM/EVMAsmPrinter.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "EVM.h" #include "EVMMCInstLower.h" #include "EVMMachineFunctionInfo.h" #include "EVMTargetMachine.h" @@ -22,6 +23,8 @@ #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Module.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInst.h" @@ -49,6 +52,16 @@ class EVMAsmPrinter : public AsmPrinter { StringSet<> WideRelocSymbolsSet; StringMap ImmutablesMap; + // Contains constant global variable initializers in address space AS_CODE, + // which are concatenated into a single block. Duplicate initializers and + // those that are substrings of others are removed. This "data section" + // is emitted at the end of the .text section. + std::string DataSectionBuffer; + MCSymbol *DataSectionSymbol = nullptr; + // Maps each global variable symbol to the offset within the data section + // where its corresponding initializer is located. + DenseMap GlobSymbolToOffsetMap; + // True if there is a function that pushes deploy address. bool ModuleHasPushDeployAddress = false; @@ -68,14 +81,20 @@ class EVMAsmPrinter : public AsmPrinter { void emitEndOfAsmFile(Module &) override; + void emitStartOfAsmFile(Module &) override; + void emitFunctionBodyStart() override; + void emitFunctionBodyEnd() override; + void emitGlobalVariable(const GlobalVariable *GV) override; + private: void emitAssemblySymbol(const MachineInstr *MI); void emitWideRelocatableSymbol(const MachineInstr *MI); void emitLoadImmutableLabel(const MachineInstr *MI); void emitJumpDest(); + void createDataSectionBuffer(const Module &M); }; } // end of anonymous namespace @@ -221,7 +240,7 @@ void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) { } MCInst TmpInst; - MCInstLowering.Lower(MI, TmpInst); + MCInstLowering.Lower(MI, TmpInst, GlobSymbolToOffsetMap, DataSectionSymbol); EmitToStreamer(*OutStreamer, TmpInst); } @@ -328,8 +347,49 @@ void EVMAsmPrinter::emitWideRelocatableSymbol(const MachineInstr *MI) { OutStreamer->switchSection(CurrentSection); } -void EVMAsmPrinter::emitEndOfAsmFile(Module &) { +void EVMAsmPrinter::createDataSectionBuffer(const Module &M) { + SmallVector, 16> Globals; + for (const GlobalVariable &GV : M.globals()) { + if (GV.getAddressSpace() != EVMAS::AS_CODE || !GV.hasInitializer()) + continue; + + const auto *CV = dyn_cast(GV.getInitializer()); + if (!CV) + continue; + + Globals.emplace_back(&GV, CV->getRawDataValues()); + } + // Sort global variables in descending order based on the size of their + // initializers. + stable_sort(Globals, [](const auto &A, const auto &B) { + return A.second.size() > B.second.size(); + }); + + // Construct the data section by concatenating unique initializers, + // eliminating duplicates, and excluding any initializer that is a + // substring of another. + // NOTE: Rather than simply concatenating unique strings, we could attempt + // to compute the Shortest Common Superstring by allowing partial overlaps + // between strings. Although this is an NP-hard problem, we could explore + // an approximate greedy solution. Consider this approach if there are + // real programs that could benefit from the optimization. + DataSectionBuffer.clear(); + raw_string_ostream Stream(DataSectionBuffer); + for (const auto &[_, Init] : Globals) + if (!StringRef(DataSectionBuffer).contains(Init)) + Stream << Init; + + // Compute offsets of each global initializer in the data section. + StringRef DataView(DataSectionBuffer); + for (const auto &[GV, Init] : Globals) { + size_t Offset = DataView.find(Init); + assert(Offset != StringRef::npos && + "Initializer not found in data section"); + GlobSymbolToOffsetMap[getSymbol(GV)] = Offset; + } +} +void EVMAsmPrinter::emitEndOfAsmFile(Module &) { // The deploy and runtime code must end with INVALID instruction to // comply with 'solc'. To ensure this, we append an INVALID // instruction at the end of the .text section. @@ -346,8 +406,14 @@ void EVMAsmPrinter::emitEndOfAsmFile(Module &) { TM.getTargetFeatureString())); OutStreamer->emitInstruction(MCI, *STI); + + // Emit constants to the code. + OutStreamer->emitLabel(DataSectionSymbol); + OutStreamer->emitBinaryData(DataSectionBuffer); + OutStreamer->popSection(); + GlobSymbolToOffsetMap.clear(); WideRelocSymbolsSet.clear(); ImmutablesMap.clear(); ModuleHasPushDeployAddress = false; @@ -360,6 +426,20 @@ void EVMAsmPrinter::emitJumpDest() { EmitToStreamer(*OutStreamer, JumpDest); } +void EVMAsmPrinter::emitStartOfAsmFile(Module &M) { + createDataSectionBuffer(M); + DataSectionSymbol = OutContext.getOrCreateSymbol("code_data_section"); +} + +void EVMAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { + // Constant arrays are handled above. + if (GV->getAddressSpace() == EVMAS::AS_CODE && GV->hasInitializer()) + if (isa(GV->getInitializer())) + return; + + AsmPrinter::emitGlobalVariable(GV); +} + 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 30955cca171b..8b60ad781cf4 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -473,8 +473,16 @@ SDValue EVMTargetLowering::LowerINTRINSIC_VOID(SDValue Op, MemOpISD = EVMISD::MEMCPY_CODE; break; } + + SDValue SrcOp = Op.getOperand(3); + // Support for copying bytes from a data section residing in code memory. + if (MemOpISD == EVMISD::MEMCPY_CODE) + if (const auto *GA = dyn_cast(SrcOp)) + SrcOp = DAG.getMCSymbol(DAG.getTarget().getSymbol(GA->getGlobal()), + MVT::i256); + return DAG.getNode(MemOpISD, DL, MVT::Other, Op.getOperand(0), - Op.getOperand(2), Op.getOperand(3), Op.getOperand(4)); + Op.getOperand(2), SrcOp, Op.getOperand(4)); } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 6eec15caa143..ce9a7779304c 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -29,9 +29,6 @@ def SDT_EVMRet def SDT_EVMSignextend : SDTypeProfile<1, 2, [SDTCisSameAs<0, 1>, SDTCisVT<1, i256>]>; -def SDT_EVMTargetAddrWrapper - : SDTypeProfile<1, 1, [SDTCisPtrTy<0>]>; - def SDT_EVMMemcpy : SDTypeProfile<0, 3, [SDTCisPtrTy<0>, SDTCisPtrTy<1>, SDTCisInt<2>]>; @@ -57,9 +54,6 @@ def EVMret def EVMSignextend : SDNode<"EVMISD::SIGNEXTEND", SDT_EVMSignextend>; -def EVMTargetAddrWrapper - : SDNode<"EVMISD::TARGET_ADDR_WRAPPER", SDT_EVMTargetAddrWrapper>; - def EVMMemcpy_call_data : SDNode<"EVMISD::MEMCPY_CALL_DATA", SDT_EVMMemcpy, [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>; @@ -211,11 +205,6 @@ def CONST_I256 let isAsCheapAsAMove = 1 in def COPY_I256 : NRI<(outs GPR:$res), (ins GPR:$src), [], "COPY_I256 $res, $src">; -def : Pat<(i256 (EVMTargetAddrWrapper tglobaladdr:$addr)), - (CONST_I256 tglobaladdr:$addr)>; -def : Pat<(i256 (EVMTargetAddrWrapper texternalsym:$addr)), - (CONST_I256 texternalsym:$addr)>; - let Uses = [SP], isCall = 1 in { // CALL should take both variadic arguments and produce variadic results, but diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.cpp b/llvm/lib/Target/EVM/EVMMCInstLower.cpp index d4ae7abc03df..1804d4aa6f65 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.cpp +++ b/llvm/lib/Target/EVM/EVMMCInstLower.cpp @@ -87,7 +87,10 @@ MCOperand EVMMCInstLower::LowerSymbolOperand(const MachineOperand &MO, return MCOperand::createExpr(Expr); } -void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { +void EVMMCInstLower::Lower( + const MachineInstr *MI, MCInst &OutMI, + const DenseMap &GlobSymbolToOffsetMap, + const MCSymbol *DataSectionSymbol) { OutMI.setOpcode(MI->getOpcode()); const MCInstrDesc &Desc = MI->getDesc(); for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) { @@ -125,7 +128,6 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { } } break; case MachineOperand::MO_MCSymbol: { - MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VariantKind::VK_None; #ifndef NDEBUG unsigned Opc = MI->getOpcode(); // We handle the linkage-related instructions in the EVMAsmPrinter. @@ -133,8 +135,18 @@ void EVMMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) { Opc != EVM::LINKERSYMBOL_S && Opc != EVM::LOADIMMUTABLE_S); #endif // NDEBUG - MCOp = MCOperand::createExpr( - MCSymbolRefExpr::create(MO.getMCSymbol(), Kind, Ctx)); + if (auto It = GlobSymbolToOffsetMap.find(MO.getMCSymbol()); + It != GlobSymbolToOffsetMap.end()) { + const MCExpr *Expr = MCSymbolRefExpr::create(DataSectionSymbol, Ctx); + if (It->second) + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(It->second, Ctx), Ctx); + + MCOp = MCOperand::createExpr(Expr); + break; + } + MCOp = + MCOperand::createExpr(MCSymbolRefExpr::create(MO.getMCSymbol(), Ctx)); } break; case MachineOperand::MO_MachineBasicBlock: MCOp = MCOperand::createExpr( diff --git a/llvm/lib/Target/EVM/EVMMCInstLower.h b/llvm/lib/Target/EVM/EVMMCInstLower.h index 071fa4dbfb71..120ee8844baf 100644 --- a/llvm/lib/Target/EVM/EVMMCInstLower.h +++ b/llvm/lib/Target/EVM/EVMMCInstLower.h @@ -13,6 +13,7 @@ namespace llvm { class AsmPrinter; +class GlobalValue; class MCContext; class MCInst; class MCOperand; @@ -40,7 +41,9 @@ class LLVM_LIBRARY_VISIBILITY EVMMCInstLower { const VRegRCMap &VRegMapping, const MachineRegisterInfo &MRI) : Ctx(Ctx), Printer(Printer), VRegMapping(VRegMapping), MRI(MRI) {} - void Lower(const MachineInstr *MI, MCInst &OutMI); + void Lower(const MachineInstr *MI, MCInst &OutMI, + const DenseMap &GlobSymbolToOffsetMap, + const MCSymbol *DataSectionSymbol); private: // Encodes the register class in the upper 4 bits along with the register diff --git a/llvm/lib/Target/EVM/EVMStackModel.cpp b/llvm/lib/Target/EVM/EVMStackModel.cpp index 6788a22e7b9f..2e72e8b86811 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.cpp +++ b/llvm/lib/Target/EVM/EVMStackModel.cpp @@ -87,16 +87,10 @@ StackSlot *EVMStackModel::getStackSlot(const MachineOperand &MO) const { Stack EVMStackModel::getSlotsForInstructionUses(const MachineInstr &MI) const { Stack In; for (const auto &MO : reverse(MI.explicit_uses())) { - // All the non-register operands are handled in instruction specific - // handlers. - if (!MO.isReg()) - continue; - - // SP is not used anyhow. - if (MO.getReg() == EVM::SP) - continue; - - In.push_back(getStackSlot(MO)); + if (MO.isReg() && MO.getReg() != EVM::SP) + In.push_back(getStackSlot(MO)); + else if (MO.isMCSymbol()) + In.push_back(getSymbolSlot(MO.getMCSymbol(), &MI)); } return In; } diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp index 89c5cb0a5245..f1a78086c088 100644 --- a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -117,11 +117,13 @@ void EVMStackifyCodeEmitter::CodeEmitter::emitConstant(uint64_t Val) { void EVMStackifyCodeEmitter::CodeEmitter::emitSymbol(const MachineInstr *MI, MCSymbol *Symbol) { - assert(isLinkerPseudoMI(*MI) && "Unexpected symbol instruction"); + assert((isLinkerPseudoMI(*MI) || MI->getOpcode() == EVM::CODECOPY) && + "Unexpected symbol instruction"); + StackHeight += 1; - // This is codegen-only instruction, that will be converted into PUSH4. - auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), - TII->get(EVM::getStackOpcode(MI->getOpcode()))) + unsigned Opc = isLinkerPseudoMI(*MI) ? EVM::getStackOpcode(MI->getOpcode()) + : EVM::PUSH_LABEL; + auto NewMI = BuildMI(*CurMBB, CurMBB->end(), MI->getDebugLoc(), TII->get(Opc)) .addSym(Symbol); verify(NewMI); } diff --git a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp index d51b566a5c4d..4aa567aa6861 100644 --- a/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/MCTargetDesc/EVMMCCodeEmitter.cpp @@ -102,24 +102,39 @@ unsigned EVMMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCSubtargetInfo &STI) const { if (MO.isImm()) { Op = MO.getImm(); - } else if (MO.isExpr()) { - auto Kind = MO.getExpr()->getKind(); - if (Kind == MCExpr::ExprKind::Target) { - const auto *CImmExp = cast(MO.getExpr()); - Op = APInt(Op.getBitWidth(), CImmExp->getString(), /*radix=*/16); - } else if (Kind == MCExpr::ExprKind::SymbolRef) { - 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(1, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); - } - } else { - llvm_unreachable("Unexpected MC operand type"); + return 0; } + if (!MO.isExpr()) + llvm_unreachable("Unable to encode MCOperand"); + + MCExpr::ExprKind Kind = MO.getExpr()->getKind(); + if (Kind == MCExpr::ExprKind::Target) { + const auto *CImmExp = cast(MO.getExpr()); + Op = APInt(Op.getBitWidth(), CImmExp->getString(), /*radix=*/16); + return 0; + } + + // We expect the relocatable immediate operand to be in the + // form: @symbol + imm. + const MCSymbolRefExpr *RefExpr = nullptr; + if (Kind == MCExpr::ExprKind::Binary) { + const auto *BE = cast(MO.getExpr()); + RefExpr = dyn_cast( + isa(BE->getLHS()) ? BE->getLHS() : BE->getRHS()); + } else if (Kind == MCExpr::ExprKind::SymbolRef) { + RefExpr = cast(MO.getExpr()); + } + + if (!RefExpr) + llvm_unreachable("Unexpected MCOperand type"); + + EVM::Fixups Fixup = getFixupForOpc(MI.getOpcode(), RefExpr->getKind()); + // 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(1, MO.getExpr(), MCFixupKind(Fixup), MI.getLoc())); + return 0; } diff --git a/llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll b/llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll new file mode 100644 index 000000000000..1833a6ee81cf --- /dev/null +++ b/llvm/test/CodeGen/EVM/data-sections-in-code-glob-order.ll @@ -0,0 +1,62 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@data1 = private unnamed_addr addrspace(4) constant [5 x i8] c"xQm7A" +@data2 = private unnamed_addr addrspace(4) constant [5 x i8] c"T2bLp" +@data3 = private unnamed_addr addrspace(4) constant [5 x i8] c"rZ8Wd" +@data4 = private unnamed_addr addrspace(4) constant [5 x i8] c"NfC3y" +@data5 = private unnamed_addr addrspace(4) constant [5 x i8] c"qHs0J" + +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define void @test() noreturn { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+5 +; CHECK-NEXT: PUSH1 0x8 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+10 +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+15 +; CHECK-NEXT: PUSH1 0x18 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+20 +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK: INVALID +; CHECK-LABEL: code_data_section: +; CHECK-NEXT: .byte 0x78, 0x51, 0x6d, 0x37 +; CHECK-NEXT: .byte 0x41, 0x54, 0x32, 0x62 +; CHECK-NEXT: .byte 0x4c, 0x70, 0x72, 0x5a +; CHECK-NEXT: .byte 0x38, 0x57, 0x64, 0x4e +; CHECK-NEXT: .byte 0x66, 0x43, 0x33, 0x79 +; CHECK-NEXT: .byte 0x71, 0x48, 0x73, 0x30 +; CHECK-NEXT: .byte 0x4a + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) @data1, i256 5, i1 false) + %dst2 = inttoptr i256 8 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst2, ptr addrspace(4) @data2, i256 5, i1 false) + %dst3 = inttoptr i256 16 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst3, ptr addrspace(4) @data3, i256 5, i1 false) + %dst4 = inttoptr i256 24 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst4, ptr addrspace(4) @data4, i256 5, i1 false) + %dst5 = inttoptr i256 32 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst5, ptr addrspace(4) @data5, i256 5, i1 false) + call void @llvm.evm.revert(ptr addrspace(1) null, i256 128) + unreachable +} diff --git a/llvm/test/CodeGen/EVM/data-sections-in-code.ll b/llvm/test/CodeGen/EVM/data-sections-in-code.ll new file mode 100644 index 000000000000..3cc146344acd --- /dev/null +++ b/llvm/test/CodeGen/EVM/data-sections-in-code.ll @@ -0,0 +1,52 @@ +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@data1 = private unnamed_addr addrspace(4) constant [13 x i8] c"hello world\0A\00" +@data2 = private unnamed_addr addrspace(4) constant [13 x i8] c"hello world\0A\00" +@data3 = private unnamed_addr addrspace(4) constant [5 x i8] c"world" +@data4 = private unnamed_addr addrspace(4) constant [7 x i8] c"another" + +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define void @test() noreturn { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: PUSH4 @code_data_section +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0xD +; CHECK-NEXT: PUSH4 @code_data_section +; CHECK-NEXT: PUSH1 0x20 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: PUSH4 @code_data_section+6 +; CHECK-NEXT: PUSH1 0x40 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x7 +; CHECK-NEXT: PUSH4 @code_data_section+13 +; CHECK-NEXT: PUSH1 0x60 +; CHECK-NEXT: CODECOPY +; CHECK-NEXT: PUSH1 0x80 +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: REVERT +; CHECK: INVALID +; CHECK-LABEL: code_data_section: +; CHECK-NEXT: .byte 0x68, 0x65, 0x6c, 0x6c +; CHECK-NEXT: .byte 0x6f, 0x20, 0x77, 0x6f +; CHECK-NEXT: .byte 0x72, 0x6c, 0x64, 0x0a +; CHECK-NEXT: .byte 0x00, 0x61, 0x6e, 0x6f +; CHECK-NEXT: .byte 0x74, 0x68, 0x65, 0x72 + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) @data1, i256 12, i1 false) + %dst2 = inttoptr i256 32 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst2, ptr addrspace(4) @data2, i256 13, i1 false) + %dst3 = inttoptr i256 64 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst3, ptr addrspace(4) @data3, i256 5, i1 false) + %dst4 = inttoptr i256 96 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst4, ptr addrspace(4) @data4, i256 7, i1 false) + call void @llvm.evm.revert(ptr addrspace(1) null, i256 128) + unreachable +} diff --git a/llvm/test/MC/EVM/glob-symbol-with-offset.ll b/llvm/test/MC/EVM/glob-symbol-with-offset.ll new file mode 100644 index 000000000000..53688f986bf4 --- /dev/null +++ b/llvm/test/MC/EVM/glob-symbol-with-offset.ll @@ -0,0 +1,36 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -filetype=obj %s -o - | llvm-objdump --disassemble - | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@data1 = private unnamed_addr addrspace(4) constant [13 x i8] c"hello world\0A\00" +@data2 = private unnamed_addr addrspace(4) constant [5 x i8] c"world" + +declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) + +define void @test() noreturn { + +; CHECK: 00000000 : +; CHECK-NEXT: 0: 5b JUMPDEST +; CHECK-NEXT: 1: 60 0d PUSH1 0xD +; CHECK-NEXT: 3: 60 13 PUSH1 0x13 +; CHECK-NEXT: 5: 5f PUSH0 +; CHECK-NEXT: 6: 39 CODECOPY +; CHECK-NEXT: 7: 60 05 PUSH1 0x5 +; CHECK-NEXT: 9: 60 19 PUSH1 0x19 +; CHECK-NEXT: b: 60 40 PUSH1 0x40 +; CHECK-NEXT: d: 39 CODECOPY +; CHECK-NEXT: e: 60 80 PUSH1 0x80 +; CHECK-NEXT: 10: 5f PUSH0 +; CHECK-NEXT: 11: fd REVERT +; CHECK-NEXT: 12: fe INVALID +; CHECK: 00000013 : +; CHECK-NEXT: 13: 68 65 6c 6c 6f 20 77 6f 72 6c +; CHECK-NEXT: 1d: 64 0a 00 + + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) null, ptr addrspace(4) @data1, i256 13, i1 false) + %dst = inttoptr i256 64 to ptr addrspace(1) + call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) %dst, ptr addrspace(4) @data2, i256 5, i1 false) + call void @llvm.evm.revert(ptr addrspace(1) null, i256 128) + unreachable +} From c41452c2b4f80e560793f62462cce3e4e5084a80 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 28 Jul 2025 11:33:39 +0200 Subject: [PATCH 166/203] [EVM] Add pre-commit test for Prefer to emit PUSH0 over a DUP Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/push0-over-dup.ll | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/push0-over-dup.ll diff --git a/llvm/test/CodeGen/EVM/push0-over-dup.ll b/llvm/test/CodeGen/EVM/push0-over-dup.ll new file mode 100644 index 000000000000..f9a0ff1c0437 --- /dev/null +++ b/llvm/test/CodeGen/EVM/push0-over-dup.ll @@ -0,0 +1,16 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define { i256, i256 } @test() { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: JUMP + ret { i256, i256 } zeroinitializer +} From 5993bdc3b0d85a135421b0b8df66396ed634e6c1 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 28 Jul 2025 11:14:44 +0200 Subject: [PATCH 167/203] [EVM] Prefer to emit PUSH0 over a DUP Prefer to emit PUSH0 instead of DUP, as it is cheaper. Signed-off-by: Vladimir Radosavljevic --- .../lib/Target/EVM/EVMStackifyCodeEmitter.cpp | 7 +++++ .../EVM/bps-recursive-func-compress-stack.mir | 28 +++++++++---------- llvm/test/CodeGen/EVM/bps-spills-1.mir | 2 +- .../EVM/no-small-constant-unfolding.ll | 2 +- llvm/test/CodeGen/EVM/push0-over-dup.ll | 2 +- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp index f1a78086c088..5fdb3e3b11c7 100644 --- a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -408,6 +408,13 @@ void EVMStackifyCodeEmitter::emitStackPermutations(const Stack &TargetStack) { [&](const StackSlot *Slot) { assert(CurrentStack.size() == Emitter.stackHeight()); + // Prefer to emit PUSH0 instead of DUP, as it is cheaper. + if (isa(Slot) && + cast(Slot)->getValue().isZero()) { + Emitter.emitConstant(0); + return; + } + // Dup the slot, if already on stack and reachable. auto SlotIt = llvm::find(llvm::reverse(CurrentStack), Slot); if (SlotIt != CurrentStack.rend()) { diff --git a/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir b/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir index 92522b386693..78468a60ea28 100644 --- a/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir +++ b/llvm/test/CodeGen/EVM/bps-recursive-func-compress-stack.mir @@ -140,7 +140,7 @@ body: | ; CHECK-NEXT: POP_S ; CHECK-NEXT: POP_S ; CHECK-NEXT: PUSH0_S - ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH0_S ; CHECK-NEXT: SSTORE_S ; CHECK-NEXT: DUP7_S ; CHECK-NEXT: SSTORE_S @@ -168,19 +168,19 @@ body: | ; CHECK-NEXT: SSTORE_S ; CHECK-NEXT: PUSH_LABEL ; CHECK-NEXT: PUSH0_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S - ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S + ; CHECK-NEXT: PUSH0_S ; CHECK-NEXT: PseudoCALL @_testRemoveAndInsertBack_rt_707, ; CHECK-NEXT: PseudoRET %3:gpr = ARGUMENT 0, implicit $arguments diff --git a/llvm/test/CodeGen/EVM/bps-spills-1.mir b/llvm/test/CodeGen/EVM/bps-spills-1.mir index be673dbb904d..ae2843cb14fa 100644 --- a/llvm/test/CodeGen/EVM/bps-spills-1.mir +++ b/llvm/test/CodeGen/EVM/bps-spills-1.mir @@ -614,7 +614,7 @@ body: | ; CHECK-NEXT: liveins: $value_stack ; CHECK-NEXT: {{ $}} ; CHECK-NEXT: PUSH0_S - ; CHECK-NEXT: DUP1_S + ; CHECK-NEXT: PUSH0_S ; CHECK-NEXT: SWAP2_S ; CHECK-NEXT: EQ_S ; CHECK-NEXT: PseudoJUMPI %bb.35 diff --git a/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll b/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll index 61b84017de22..9ff6f98276b8 100644 --- a/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll +++ b/llvm/test/CodeGen/EVM/no-small-constant-unfolding.ll @@ -15,7 +15,7 @@ define void @test1() #1 { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: tail call void @llvm.evm.return(ptr addrspace(1) null, i256 0) diff --git a/llvm/test/CodeGen/EVM/push0-over-dup.ll b/llvm/test/CodeGen/EVM/push0-over-dup.ll index f9a0ff1c0437..977981328a21 100644 --- a/llvm/test/CodeGen/EVM/push0-over-dup.ll +++ b/llvm/test/CodeGen/EVM/push0-over-dup.ll @@ -9,7 +9,7 @@ define { i256, i256 } @test() { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: DUP1 +; CHECK-NEXT: PUSH0 ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: JUMP ret { i256, i256 } zeroinitializer From 3e455a631a1adb905c2313a3f53c64d0ef63d0ea Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 9 Jul 2025 22:42:38 +0200 Subject: [PATCH 168/203] [EVM][ISel] Improve BYTE instruction selection This replaces 'AND (SRL imm, v), 0xFF' -> 'BYTE (31 - imm / 8), v', where imm % 8 == 0, and imm / 8 < 32. Fixes issue #824. --- llvm/lib/Target/EVM/EVMInstrInfo.td | 30 +++++++ .../CodeGen/EVM/bitmanipulation-intrinsics.ll | 90 ++++++++----------- .../EVM/folding-lshr-with-and-to-byte.ll | 90 +++++++++++++++++++ llvm/test/CodeGen/EVM/zero_any_extload.ll | 8 +- 4 files changed, 163 insertions(+), 55 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index ce9a7779304c..40536fe874c6 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -125,6 +125,18 @@ def negate_imm : SDNodeXFormgetTargetConstant(-neg, SDLoc(N), MVT::i256); }]>; +// Transform that maps the shift amount to BYTE‘s index (31 - Val / 8). +def byte_shift : SDNodeXForm(N)->getZExtValue(); + uint64_t ByteIdx = Val / 8; + return CurDAG->getTargetConstant(31 - ByteIdx, SDLoc(N), MVT::i256); +}]>; + +// Immediate predicate: imm divisible by 8 and within [0, 31 * 8]. +def byte_shift_imm : PatLeaf<(imm), [{ + uint64_t Val = N->getZExtValue(); + return (Val % 8) == 0 && Val <= 31 * 8; +}]>; //===----------------------------------------------------------------------===// // Pattern fragments for memory instructions. @@ -351,6 +363,24 @@ defm BYTE [(set GPR:$dst, (int_evm_byte GPR:$idx, GPR:$val))], "BYTE", "$dst, $idx, $val", 0x1a, 3>; +def : Pat<(and (srl i256:$val, byte_shift_imm:$sh), (i256 255)), + (BYTE (CONST_I256 (byte_shift byte_shift_imm:$sh)), $val)>; + +// When the shift amount is 248, the node 'AND $val, 255' gets optimized away +// in the lowered SelectionDAG, preventing the above pattern from matching +// which results to the following instructions: +// +// PUSH1 0xF8 +// SHR +// +// However, using the BYTE instruction instead is often both cheaper in gas +// and uses one less byte: +// +// PUSH0 +// BYTE +// +// So we explicitly match this case with a separate pattern: +def : Pat<(srl i256:$val, (i256 248)), (BYTE (CONST_I256 0), $val)>; //===----------------------------------------------------------------------===// // EVM shift instructions. diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll index 6377e70a84f9..340ddd0fb98c 100644 --- a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -15,8 +15,8 @@ define i256 @bitreversetest(i256 %v) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 0xF8 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE ; CHECK-NEXT: PUSH2 0xFF00 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0xE8 @@ -280,8 +280,8 @@ define i256 @bswaptest(i256 %v) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: DUP1 -; CHECK-NEXT: PUSH1 0xF8 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE ; CHECK-NEXT: PUSH2 0xFF00 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0xE8 @@ -511,17 +511,15 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-LABEL: ctpoptest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 ; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 -; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP6 ; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 ; CHECK-NEXT: DUP1 -; CHECK-NEXT: SWAP10 +; CHECK-NEXT: SWAP8 ; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND @@ -538,7 +536,7 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP9 +; CHECK-NEXT: SWAP7 ; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR @@ -556,26 +554,24 @@ define i256 @ctpoptest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP7 +; CHECK-NEXT: SWAP5 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 0x78 -; CHECK-NEXT: SHR -; CHECK-NEXT: AND -; CHECK-NEXT: SWAP4 +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP3 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 0x78 -; CHECK-NEXT: SHR -; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -588,16 +584,14 @@ define i256 @ctlztest(i256 %v) { ; CHECK-LABEL: ctlztest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 ; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 -; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP6 ; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 -; CHECK-NEXT: SWAP9 +; CHECK-NEXT: SWAP7 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR ; CHECK-NEXT: OR @@ -631,7 +625,7 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: OR ; CHECK-NEXT: NOT ; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -; CHECK-NEXT: DUP10 +; CHECK-NEXT: DUP8 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR @@ -649,7 +643,7 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP10 +; CHECK-NEXT: SWAP8 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR @@ -666,26 +660,24 @@ define i256 @ctlztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP7 +; CHECK-NEXT: SWAP5 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 0x78 -; CHECK-NEXT: SHR -; CHECK-NEXT: AND -; CHECK-NEXT: SWAP4 +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP3 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 0x78 -; CHECK-NEXT: SHR -; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -698,23 +690,21 @@ define i256 @cttztest(i256 %v) { ; CHECK-LABEL: cttztest: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0xFF ; CHECK-NEXT: PUSH32 0x101010101010101010101010101010101010101010101010101010101010101 ; CHECK-NEXT: PUSH16 0xF0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 -; CHECK-NEXT: DUP3 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH32 0x3333333333333333333333333333333333333333333333333333333333333333 -; CHECK-NEXT: DUP8 +; CHECK-NEXT: DUP6 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: PUSH16 0x55555555555555555555555555555555 -; CHECK-NEXT: SWAP10 +; CHECK-NEXT: SWAP8 ; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: NOT ; CHECK-NEXT: AND ; CHECK-NEXT: PUSH16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -; CHECK-NEXT: DUP10 +; CHECK-NEXT: DUP8 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x81 ; CHECK-NEXT: SHR @@ -732,7 +722,7 @@ define i256 @cttztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP10 +; CHECK-NEXT: SWAP8 ; CHECK-NEXT: DUP3 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: SHR @@ -749,26 +739,24 @@ define i256 @cttztest(i256 %v) { ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: AND ; CHECK-NEXT: ADD -; CHECK-NEXT: SWAP7 +; CHECK-NEXT: SWAP5 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 0x78 -; CHECK-NEXT: SHR -; CHECK-NEXT: AND -; CHECK-NEXT: SWAP4 +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP3 ; CHECK-NEXT: DUP1 ; CHECK-NEXT: PUSH1 0x4 ; CHECK-NEXT: SHR ; CHECK-NEXT: ADD ; CHECK-NEXT: AND ; CHECK-NEXT: MUL -; CHECK-NEXT: PUSH1 0x78 -; CHECK-NEXT: SHR -; CHECK-NEXT: AND +; CHECK-NEXT: PUSH1 0x10 +; CHECK-NEXT: BYTE ; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP diff --git a/llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll b/llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll new file mode 100644 index 000000000000..64bf565ccd42 --- /dev/null +++ b/llvm/test/CodeGen/EVM/folding-lshr-with-and-to-byte.ll @@ -0,0 +1,90 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; Check that we do the following transformation: +; AND (SRL imm, v), 0xFF' -> 'BYTE (31 - imm / 8), v', +; where imm % 8 == 0, and imm / 8 < 32. + +define i256 @byte_lshr_0(i256 %0) { +; CHECK-LABEL: byte_lshr_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %r = and i256 %0, 255 + ret i256 %r +} + +define i256 @byte_lshr_8(i256 %0) { +; CHECK-LABEL: byte_lshr_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1E +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 8 + %r = and i256 %s, 255 + ret i256 %r +} + +define i256 @byte_lshr_16(i256 %0) { +; CHECK-LABEL: byte_lshr_16: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1D +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 16 + %r = and i256 %s, 255 + ret i256 %r +} + +define i256 @byte_lshr_248(i256 %0) { +; CHECK-LABEL: byte_lshr_248: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 248 + %r = and i256 %s, 255 + ret i256 %r +} + +; Should not apply: imm = 9 (not divisible by 8) +define i256 @shift_not_multiple_of_8(i256 %0) { +; CHECK-LABEL: shift_not_multiple_of_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x9 +; CHECK-NEXT: SHR +; CHECK-NEXT: AND +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 9 + %r = and i256 %s, 255 + ret i256 %r +} + +; Should not apply: imm = 256 (256 / 8 > 31) +define i256 @shift_too_large(i256 %0) { +; CHECK-LABEL: shift_too_large: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %s = lshr i256 %0, 256 + %r = and i256 %s, 255 + ret i256 %r +} diff --git a/llvm/test/CodeGen/EVM/zero_any_extload.ll b/llvm/test/CodeGen/EVM/zero_any_extload.ll index 938bc74fee7c..de8f3edc5b81 100644 --- a/llvm/test/CodeGen/EVM/zero_any_extload.ll +++ b/llvm/test/CodeGen/EVM/zero_any_extload.ll @@ -9,8 +9,8 @@ define i8 @load_anyext_i8(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 0xF8 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP @@ -79,8 +79,8 @@ define i256 @load_zeroext_i8(ptr addrspace(1) %ptr) nounwind { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: MLOAD -; CHECK-NEXT: PUSH1 0xF8 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: BYTE ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP From 634452c26e4676d56bcb5b522da0115c4bc52452 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 17 Jul 2025 11:57:10 +0200 Subject: [PATCH 169/203] [EVM][DAGCombine] Expand SELECT to arithmetic when possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The EVM has no conditional move, so this patch replaces certain `select` nodes with straight‑line arithmetic: | Source pattern | Rewritten form | Proof (IR equivalent) | |-----------------|------------------|-------------------------------------| | select C, X, 0 | X * C | https://alive2.llvm.org/ce/z/MqUacX | | select C, 0, Y | (1 − C) * Y | https://alive2.llvm.org/ce/z/tB_ww7 | | select C, X, 1 | X * C + (1 − C) | https://alive2.llvm.org/ce/z/Uje2ts | | select C, 1, Y | C + (1 − C) * Y | https://alive2.llvm.org/ce/z/XrBMpA | | select C, X, ‑1 | (C - 1) | X | https://alive2.llvm.org/ce/z/e5oNmW | | select C, ‑1, Y | -C | Y | https://alive2.llvm.org/ce/z/6-86BJ | This patch also canonicalises select C, (op Y, X), Y -> op Y, (select C, X, 0) -> op Y, C * X iff 0 is neutral element for op. Alive2 proofs: i256, sub, no undef inputs – https://alive2.llvm.org/ce/z/-BPfbO i4, sub, undef inputs allowed – https://alive2.llvm.org/ce/z/6fhhZx --- llvm/lib/Target/EVM/EVMISelLowering.cpp | 127 ++++++++ llvm/lib/Target/EVM/EVMISelLowering.h | 3 + llvm/test/CodeGen/EVM/dag-combine-select.ll | 335 ++++++++++++++++++++ 3 files changed, 465 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/dag-combine-select.ll diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index 8b60ad781cf4..c08b556df8f5 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -90,6 +90,9 @@ EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM, setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); + // Custom DAGCombine patterns. + setTargetDAGCombine(ISD::SELECT); + setJumpIsExpensive(true); setMaximumJumpTableSize(0); } @@ -766,3 +769,127 @@ MachineBasicBlock *EVMTargetLowering::emitSelect(MachineInstr &MI, MI.eraseFromParent(); // The pseudo instruction is gone now. return BB; } + +// Fold +// (select C, (op Y, X), Y) -> (op Y, (select C, X, NEUTRAL)) +// and +// (select C, Y, (op Y, X)) -> (op Y, (select not C, X, NEUTRAL)) +// when 0 is the neutral element for op. +// +// That lets a later combine turn (select C, X, 0) into (C * X). +// +// Examples: +// (select C, (sub Y, X), Y) -> (sub Y, (select C, X, 0)) +// (select C, (or Y, X), Y) -> (or Y, (select C, X, 0)) +// (select C, (or X, Y), Y) -> (or Y, (select C, X, 0)) ; commutativity +// handled. +static SDValue tryFoldSelectIntoOp(SDNode *N, SelectionDAG &DAG, SDValue TrueV, + SDValue FalseV, bool Swapped) { + + if (!TrueV.hasOneUse() || isa(FalseV)) + return SDValue(); + + bool Commutative = true; + switch (TrueV.getOpcode()) { + default: + return SDValue(); + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + case ISD::SUB: + Commutative = false; + break; + case ISD::ADD: + case ISD::OR: + case ISD::XOR: + break; + } + + if (FalseV != TrueV.getOperand(0) && + (!Commutative || FalseV != TrueV.getOperand(1))) + return SDValue(); + + EVT VT = N->getValueType(0); + SDLoc DL(N); + SDValue OtherOp = + FalseV == TrueV.getOperand(0) ? TrueV.getOperand(1) : TrueV.getOperand(0); + EVT OtherOpVT = OtherOp.getValueType(); + SDValue IdentityOperand = DAG.getConstant(0, DL, OtherOpVT); + + if (Swapped) + std::swap(OtherOp, IdentityOperand); + SDValue NewSel = + DAG.getSelect(DL, OtherOpVT, N->getOperand(0), OtherOp, IdentityOperand); + return DAG.getNode(TrueV.getOpcode(), DL, VT, FalseV, NewSel); +} + +SDValue EVMTargetLowering::combineSELECT(SDNode *N, + DAGCombinerInfo &DCI) const { + // Perform combines only after DAG legalisation. + if (!DCI.isAfterLegalizeDAG()) + return SDValue(); + + SelectionDAG &DAG = DCI.DAG; + SDValue CondV = N->getOperand(0); + SDValue TrueV = N->getOperand(1); + SDValue FalseV = N->getOperand(2); + SDLoc DL(N); + MVT VT = N->getSimpleValueType(0); + + const auto freeze = [&DAG](const SDValue V) { return DAG.getFreeze(V); }; + const auto iszero = [&DAG, DL, VT](const SDValue V) { + return SDValue(DAG.getMachineNode(EVM::ISZERO, DL, VT, V), 0); + }; + + // X* means freeze(X) + // fold (Cond ? X : 0) -> (X* * Cond) + if (isNullConstant(FalseV)) + return DAG.getNode(ISD::MUL, DL, VT, freeze(TrueV), CondV); + + // fold (Cond ? 0 : Y) -> (~Cond * Y*) + if (isNullConstant(TrueV)) + return DAG.getNode(ISD::MUL, DL, VT, iszero(CondV), freeze(FalseV)); + + // fold (Cond ? X : 1) -> ((X* * Cond*) + ~Cond*) + if (isOneConstant(FalseV)) + return DAG.getNode( + ISD::ADD, DL, VT, iszero(freeze(CondV)), + DAG.getNode(ISD::MUL, DL, VT, freeze(TrueV), freeze(CondV))); + + // fold (Cond ? 1 : Y) -> (Cond* + ~Cond* * Y*) + if (isOneConstant(TrueV)) + return DAG.getNode( + ISD::ADD, DL, VT, freeze(CondV), + DAG.getNode(ISD::MUL, DL, VT, freeze(FalseV), iszero(freeze(CondV)))); + + // fold (Cond ? X : -1) -> (Cond - 1) | X*) + if (isAllOnesConstant(FalseV)) { + const SDValue Const1 = DAG.getConstant(1, DL, VT); + return DAG.getNode(ISD::OR, DL, VT, + DAG.getNode(ISD::SUB, DL, VT, CondV, Const1), + freeze(TrueV)); + } + + // fold (Cond ? -1 : Y) -> (-Cond | Y*) + if (isAllOnesConstant(TrueV)) { + SDValue Neg = DAG.getNegative(CondV, DL, VT); + return DAG.getNode(ISD::OR, DL, VT, Neg, DAG.getFreeze(FalseV)); + } + + if (SDValue V = tryFoldSelectIntoOp(N, DAG, TrueV, FalseV, /*Swapped=*/false)) + return V; + // NOLINTNEXTLINE(readability-suspicious-call-argument) + return tryFoldSelectIntoOp(N, DAG, FalseV, TrueV, /*Swapped=*/true); +} + +SDValue EVMTargetLowering::PerformDAGCombine(SDNode *N, + DAGCombinerInfo &DCI) const { + switch (N->getOpcode()) { + default: + break; + case ISD::SELECT: + return combineSELECT(N, DCI); + } + + return SDValue(); +} diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 4309fc0a6f32..8158652cdb8a 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -139,6 +139,9 @@ class EVMTargetLowering final : public TargetLowering { void ReplaceNodeResults(SDNode *N, SmallVectorImpl &Results, SelectionDAG &DAG) const override; + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + SDValue combineSELECT(SDNode *N, DAGCombinerInfo &DCI) const; + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; SDValue LowerBSWAP(SDValue BSWAP, SelectionDAG &DAG) const; diff --git a/llvm/test/CodeGen/EVM/dag-combine-select.ll b/llvm/test/CodeGen/EVM/dag-combine-select.ll new file mode 100644 index 000000000000..05103d202278 --- /dev/null +++ b/llvm/test/CodeGen/EVM/dag-combine-select.ll @@ -0,0 +1,335 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -stop-after=finalize-isel < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; select C, X, 0 -> X * C +define i256 @sel_x_or_0(i1 %cond, i256 %x) { + ; CHECK-LABEL: name: sel_x_or_0 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL + ; CHECK-NEXT: RET killed [[MUL]] +%val = select i1 %cond, i256 %x, i256 0 +ret i256 %val +} + +; select C, 0, Y -> not C * Y +define i256 @sel_0_or_y(i1 %cond, i256 %y) { + ; CHECK-LABEL: name: sel_0_or_y + ; CHECK: [[ISZERO:%[0-9]+]]:gpr = ISZERO + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[ISZERO]], killed {{.*}} + ; CHECK-NEXT: RET killed [[MUL]], implicit-def dead $arguments +%val = select i1 %cond, i256 0, i256 %y +ret i256 %val +} + +; select C, X, 1 -> C * X + not C +define i256 @sel_x_or_1(i1 %cond, i256 %x) { + ; CHECK-LABEL: name: sel_x_or_1 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, [[COND:%[0-9]+]] + ; CHECK-NEXT: [[ISZERO:%[0-9]+]]:gpr = ISZERO [[COND]] + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD killed [[ISZERO]], killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] +%val = select i1 %cond, i256 %x, i256 1 +ret i256 %val +} + +; select C, 1, Y -> C + not C * Y +define i256 @sel_1_or_y(i1 %cond, i256 %y) { + ; CHECK-LABEL: name: sel_1_or_y + ; CHECK: [[ISZERO:%[0-9]+]]:gpr = ISZERO [[COND:%[0-9]+]] + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed [[ISZERO]] + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD [[COND]], killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] +%val = select i1 %cond, i256 1, i256 %y +ret i256 %val +} + +; select C, X, -1 -> (C - 1) | X* +define i256 @sel_x_or_m1(i1 %cond, i256 %x) { + ; CHECK-LABEL: name: sel_x_or_m1 + ; CHECK: [[CONST_1:%[0-9]+]]:gpr = CONST_I256 i256 1 + ; CHECK-NEXT: [[AND:%[0-9]+]]:gpr = AND {{.*}}, [[CONST_1]] + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB killed [[AND]], [[CONST_1]] + ; CHECK-NEXT: [[X:%[0-9]+]]:gpr = COPY killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR killed [[SUB]], killed [[X]] + ; CHECK-NEXT: RET killed [[OR]] + %val = select i1 %cond, i256 %x, i256 -1 + ret i256 %val +} + +; select C, -1, Y -> -C | Y* +define i256 @sel_m1_or_y(i1 %cond, i256 %y) { + ; CHECK-LABEL: name: sel_m1_or_y + ; CHECK: [[CONST_1:%[0-9]+]]:gpr = CONST_I256 i256 1 + ; CHECK-NEXT: [[AND:%[0-9]+]]:gpr = AND {{.*}}, killed [[CONST_1]] + ; CHECK-NEXT: [[CONST_0:%[0-9]+]]:gpr = CONST_I256 i256 0 + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB killed [[CONST_0]], killed [[AND]] + ; CHECK-NEXT: [[Y:%[0-9]+]]:gpr = COPY killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR killed [[SUB]], killed [[Y]] + ; CHECK-NEXT: RET killed [[OR]] + %val = select i1 %cond, i256 -1, i256 %y + ret i256 %val +} + +; select C, (xor Y, X), Y -> xor Y, (select C, X, 0) -> xor Y, X * C +define i256 @select_xor(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_xor + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[XOR:%[0-9]+]]:gpr = XOR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[XOR]] + %c = xor i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %B + ret i256 %2 +} + +; select C, Y, (xor Y, X) -> xor Y, (select not C, X, 0) -> xor Y, X * not C +define i256 @select_xor_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_xor_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[XOR:%[0-9]+]]:gpr = XOR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[XOR]] + %c = xor i256 %A, %B + %res = select i1 %cond, i256 %B, i256 %c + ret i256 %res +} + +; commutability check +define i256 @select_xor_2(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_xor_2 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[XOR:%[0-9]+]]:gpr = XOR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[XOR]] + %c = xor i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %A + ret i256 %2 +} + +; select C, (or Y, X), Y -> or Y, (select C, X, 0) -> or Y, X * C +define i256 @select_or(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_or + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[OR]] + %c = or i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %B + ret i256 %2 +} + +; select C, Y, (or Y, X) -> or Y, (select not C, X, 0) -> or Y, X * not C +define i256 @select_or_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_or_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[OR]] + %c = or i256 %A, %B + %res = select i1 %cond, i256 %B, i256 %c + ret i256 %res +} + +; commutability check +define i256 @select_or_2(i256 %A, i256 %B, i1 %cond) { + ; CHECK-LABEL: name: select_or_2 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[OR:%[0-9]+]]:gpr = OR {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[OR]] + %c = or i256 %B, %A + %2 = select i1 %cond, i256 %c, i256 %A + ret i256 %2 +} + +; select C, (add Y, X), Y -> add Y, (select C, X, 0) -> add Y, X * C +define i256 @select_add(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_add + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] + %c = add i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (add Y, X) -> add Y, (select not C, X, 0) -> add Y, X * not C +define i256 @select_add_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_add_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] + %c = add i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; commutability check +define i256 @select_add_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_add_2 + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[ADD:%[0-9]+]]:gpr = ADD {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[ADD]] + %c = add i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; select C, (sub Y, X), Y -> sub Y, (select C, X, 0) -> sub Y, X * C +define i256 @select_sub(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_sub + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[SUB]] + %c = sub i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (sub Y, X) -> sub Y, (select not C, X, 0) -> sub Y, X * not C +define i256 @select_sub_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_sub + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; CHECK-NEXT: [[SUB:%[0-9]+]]:gpr = SUB {{.*}}, killed [[MUL]] + ; CHECK-NEXT: RET killed [[SUB]] + %c = sub i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_sub_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_sub_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = sub i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; negative - select(and) expands normally +define i256 @select_and(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_and + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = and i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; negative - select(and) expands normally +define i256 @select_and_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_and_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = and i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; select C, (shl Y, X), Y -> shl Y, (select C, X, 0) -> shl Y, X * C +define i256 @select_shl(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_shl + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; SHL shift, val + ; CHECK-NEXT: [[SHL:%[0-9]+]]:gpr = SHL killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHL]] + %c = shl i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (shl Y, X) -> shl Y, (select not C, X, 0) +define i256 @select_shl_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_shl_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SHL:%[0-9]+]]:gpr = SHL killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHL]] + %c = shl i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_shl_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_shl_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = shl i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; select C, (ashr Y, X), Y -> ashr Y, (select C, X, 0) -> ashr Y, X * C +define i256 @select_ashr(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_ashr + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; SAR shift, val + ; CHECK-NEXT: [[SAR:%[0-9]+]]:gpr = SAR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SAR]] + %c = ashr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (ashr Y, X) -> ashr Y, (select not C, X, 0) +define i256 @select_ashr_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_ashr_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SAR:%[0-9]+]]:gpr = SAR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SAR]] + %c = ashr i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_ashr_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_ashr_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = ashr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} + +; select C, (lshr Y, X), Y -> shl Y, (select C, X, 0) +define i256 @select_lshr(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_lshr + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL {{.*}}, killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SHR:%[0-9]+]]:gpr = SHR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHR]] + %c = lshr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %A + ret i256 %res +} + +; select C, Y, (lshr Y, X) -> lshr Y, (select not C, X, 0) +define i256 @select_lshr_swapped(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_lshr_swapped + ; CHECK: [[NOTC:%[0-9]+]]:gpr = ISZERO killed {{.*}} + ; CHECK: [[MUL:%[0-9]+]]:gpr = MUL killed [[NOTC]], killed {{.*}} + ; SHR shift, val + ; CHECK-NEXT: [[SHR:%[0-9]+]]:gpr = SHR killed [[MUL]], {{.*}} + ; CHECK-NEXT: RET killed [[SHR]] + %c = lshr i256 %A, %B + %res = select i1 %cond, i256 %A, i256 %c + ret i256 %res +} + +; not commutative - normal select expansion +define i256 @select_lshr_2(i1 %cond, i256 %A, i256 %B) { + ; CHECK-LABEL: name: select_lshr_2 + ; CHECK-NOT: MUL + ; CHECK: JUMPI + %c = lshr i256 %A, %B + %res = select i1 %cond, i256 %c, i256 %B + ret i256 %res +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} From cb01d2e49136a91e5d10d0260ada5ea9a49c23dc Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 28 Jul 2025 15:36:11 +0200 Subject: [PATCH 170/203] [EVM] Update branch-folder-after-stackification.ll test This test originally used select instructions to verify the impact of the BranchFolder pass after stackification. However, if selects are lowered to arithmetic operations, the intended coverage is lost. To ensure the test continues to validate the desired functionality, it now uses compare and branch instructions instead of selects. Signed-off-by: Vladimir Radosavljevic --- .../EVM/branch-folder-after-stackification.ll | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll index 1cd4640b1e5d..1d8d470c3ac7 100644 --- a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -6,40 +6,51 @@ target triple = "evm" define i256 @test(i256 %arg) { ; CHECK-LABEL: test: -; CHECK: ; %bb.0: +; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: DUP2 ; CHECK-NEXT: SLT -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: PUSH1 0xA +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0x14 ; CHECK-NEXT: PUSH4 @.BB0_3 ; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB0_2: +; CHECK-NEXT: .BB0_2: ; %bb1 ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: PUSH1 0x14 -; CHECK-NEXT: .BB0_3: +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: PUSH1 0xA +; CHECK-NEXT: .BB0_3: ; %bb2 ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 -; CHECK-NEXT: SGT -; CHECK-NEXT: ISZERO +; CHECK-NEXT: SLT ; CHECK-NEXT: PUSH4 @.BB0_5 ; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.4: +; CHECK-NEXT: ; %bb.4: ; %bb3 ; CHECK-NEXT: POP ; CHECK-NEXT: PUSH1 0x5 -; CHECK-NEXT: .BB0_5: +; CHECK-NEXT: .BB0_5: ; %bb4 ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP - %cmp1 = icmp sgt i256 %arg, 0 - %cmp2 = icmp slt i256 %arg, 0 - %select1 = select i1 %cmp2, i256 10, i256 20 - %select2 = select i1 %cmp1, i256 5, i256 %select1 - ret i256 %select2 +entry: + %cmp1 = icmp slt i256 %arg, 0 + br i1 %cmp1, label %bb1, label %bb2 + +bb1: + br label %bb2 + +bb2: + %phi1 = phi i256 [ 20, %entry ], [ 10, %bb1 ] + %cmp2 = icmp sgt i256 %arg, 0 + br i1 %cmp2, label %bb3, label %bb4 + +bb3: + br label %bb4 + +bb4: + %phi2 = phi i256 [ %phi1, %bb2 ], [ 5, %bb3 ] + ret i256 %phi2 } From 567203af269489f69c9260952b04e964fbc01b36 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 24 Jul 2025 14:49:36 +0200 Subject: [PATCH 171/203] [EVM][DAGCombine] Add pre-commit test for Expand SELECT of constant to arithmetic Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/select-const.ll | 180 ++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/llvm/test/CodeGen/EVM/select-const.ll b/llvm/test/CodeGen/EVM/select-const.ll index 6e129aa33c31..d88600fa9e06 100644 --- a/llvm/test/CodeGen/EVM/select-const.ll +++ b/llvm/test/CodeGen/EVM/select-const.ll @@ -301,3 +301,183 @@ define i256 @select_ne_10001_10002(i256 %a, i256 %b) { %2 = select i1 %1, i256 10001, i256 10002 ret i256 %2 } + +define i256 @select_196_184(i1 %a) { +; CHECK-LABEL: select_196_184: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB20_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0xC4 +; CHECK-NEXT: PUSH4 @.BB20_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB20_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: .BB20_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 196, i256 184 + ret i256 %1 +} + +define i256 @select_184_196(i1 %a) { +; CHECK-LABEL: select_184_196: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB21_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: PUSH4 @.BB21_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB21_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC4 +; CHECK-NEXT: .BB21_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 184, i256 196 + ret i256 %1 +} + +define i256 @select_n196_n184(i1 %a) { +; CHECK-LABEL: select_n196_n184: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB22_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0xC3 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB22_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB22_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xB7 +; CHECK-NEXT: NOT +; CHECK-NEXT: .BB22_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 -196, i256 -184 + ret i256 %1 +} + +define i256 @select_n184_n196(i1 %a) { +; CHECK-LABEL: select_n184_n196: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB23_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: PUSH1 0xB7 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH4 @.BB23_3 +; CHECK-NEXT: JUMP +; CHECK-NEXT: .BB23_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC3 +; CHECK-NEXT: NOT +; CHECK-NEXT: .BB23_3: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 -184, i256 -196 + ret i256 %1 +} + +define i256 @select_var_12345(i1 %a, i256 %b) { +; CHECK-LABEL: select_var_12345: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 @.BB24_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: .BB24_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 %b, i256 12345 + ret i256 %1 +} + +define i256 @select_12345_var(i1 %a, i256 %b) { +; CHECK-LABEL: select_12345_var: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB25_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: .BB25_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 12345, i256 %b + ret i256 %1 +} + +define i256 @select_var_n12345(i1 %a, i256 %b) { +; CHECK-LABEL: select_var_n12345: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: PUSH4 @.BB26_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH2 0x3038 +; CHECK-NEXT: NOT +; CHECK-NEXT: .BB26_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 %b, i256 -12345 + ret i256 %1 +} + +define i256 @select_n12345_var(i1 %a, i256 %b) { +; CHECK-LABEL: select_n12345_var: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: ISZERO +; CHECK-NEXT: PUSH4 @.BB27_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: POP +; CHECK-NEXT: PUSH2 0x3038 +; CHECK-NEXT: NOT +; CHECK-NEXT: .BB27_2: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %1 = select i1 %a, i256 -12345, i256 %b + ret i256 %1 +} From 13cd87812f00eefa9926b4edadc5bb0c454f4fbf Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Wed, 23 Jul 2025 12:12:47 +0200 Subject: [PATCH 172/203] [EVM][DAGCombine] Expand SELECT of constant to arithmetic This patch implements following optimization: | Source pattern | Rewritten form | Proof (IR equivalent) | |--------------------------|--------------------|-------------------------------------| | select C, X, C2 | C2 + (X - C2) * C | https://alive2.llvm.org/ce/z/V2Cd9h | | select C, C1, Y | Y + (C1 - Y) * C | https://alive2.llvm.org/ce/z/g3ki-b | | select C, C1, C2 C1 > C2 | C2 + (C1 - C2) * C | https://alive2.llvm.org/ce/z/cOCHY_ | | select C, C1, C2 C1 < C2 | C2 - (C2 - C1) * C | https://alive2.llvm.org/ce/z/P8jkjd | When we have select of two constants, we are doing two different transformations to avoid generating negative constants, which are expensive in EVM. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMISelLowering.cpp | 43 +++++++ llvm/test/CodeGen/EVM/select-const.ll | 143 +++++++++--------------- 2 files changed, 93 insertions(+), 93 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index c08b556df8f5..ecca571fb7b0 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -876,6 +876,49 @@ SDValue EVMTargetLowering::combineSELECT(SDNode *N, return DAG.getNode(ISD::OR, DL, VT, Neg, DAG.getFreeze(FalseV)); } + // fold (Cond ? C1 : C2) -> (C2 + (C1 - C2) * Cond) if (C1 > C2) + // fold (Cond ? C1 : C2) -> (C2 - (C2 - C1) * Cond) if (C1 < C2) + // + // We distinguish between C1 > C2 and C1 < C2 to avoid generating + // negative constants, which are expensive in EVM. + if (isa(TrueV) && isa(FalseV)) { + APInt C1Val = cast(TrueV)->getAPIntValue(); + APInt C2Val = cast(FalseV)->getAPIntValue(); + + // This should never happen, but just in case. + if (C1Val == C2Val) + return TrueV; + + unsigned Opc = 0; + SDValue Scale; + if (C1Val.sgt(C2Val)) { + Opc = ISD::ADD; + Scale = DAG.getConstant(C1Val - C2Val, DL, VT); + } else { + // Use sub to avoid generating negative constants. + Opc = ISD::SUB; + Scale = DAG.getConstant(C2Val - C1Val, DL, VT); + } + + SDValue Mul = DAG.getNode(ISD::MUL, DL, VT, CondV, Scale); + return DAG.getNode(Opc, DL, VT, FalseV, Mul); + } + + // fold (Cond ? X : C) -> (C + (X* - C) * Cond) + // fold (Cond ? C : Y) -> (Y* + (C - Y*) * Cond) + if (isa(TrueV) || isa(FalseV)) { + if (!isa(TrueV)) + TrueV = freeze(TrueV); + else if (!isa(FalseV)) + FalseV = freeze(FalseV); + else + llvm_unreachable("Unexpected select operands"); + + SDValue Sub = DAG.getNode(ISD::SUB, DL, VT, TrueV, FalseV); + SDValue Mul = DAG.getNode(ISD::MUL, DL, VT, CondV, Sub); + return DAG.getNode(ISD::ADD, DL, VT, FalseV, Mul); + } + if (SDValue V = tryFoldSelectIntoOp(N, DAG, TrueV, FalseV, /*Swapped=*/false)) return V; // NOLINTNEXTLINE(readability-suspicious-call-argument) diff --git a/llvm/test/CodeGen/EVM/select-const.ll b/llvm/test/CodeGen/EVM/select-const.ll index d88600fa9e06..62f060633d22 100644 --- a/llvm/test/CodeGen/EVM/select-const.ll +++ b/llvm/test/CodeGen/EVM/select-const.ll @@ -48,20 +48,13 @@ define i256 @select_const_int_harder(i1 %a) { ; CHECK-LABEL: select_const_int_harder: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x26 +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB3_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0x6 -; CHECK-NEXT: PUSH4 @.BB3_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB3_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0x26 -; CHECK-NEXT: .BB3_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0x5 +; CHECK-NEXT: SHL +; CHECK-NEXT: XOR ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 6, i256 38 @@ -306,20 +299,13 @@ define i256 @select_196_184(i1 %a) { ; CHECK-LABEL: select_196_184: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: PUSH1 0xB8 +; CHECK-NEXT: SWAP2 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB20_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0xC4 -; CHECK-NEXT: PUSH4 @.BB20_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB20_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0xB8 -; CHECK-NEXT: .BB20_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 196, i256 184 @@ -330,20 +316,13 @@ define i256 @select_184_196(i1 %a) { ; CHECK-LABEL: select_184_196: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB21_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0xB8 -; CHECK-NEXT: PUSH4 @.BB21_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB21_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL ; CHECK-NEXT: PUSH1 0xC4 -; CHECK-NEXT: .BB21_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 184, i256 196 @@ -354,22 +333,14 @@ define i256 @select_n196_n184(i1 %a) { ; CHECK-LABEL: select_n196_n184: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB22_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0xC3 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB22_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB22_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL ; CHECK-NEXT: PUSH1 0xB7 ; CHECK-NEXT: NOT -; CHECK-NEXT: .BB22_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 -196, i256 -184 @@ -380,22 +351,13 @@ define i256 @select_n184_n196(i1 %a) { ; CHECK-LABEL: select_n184_n196: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH1 0xC +; CHECK-NEXT: PUSH1 0xC4 +; CHECK-NEXT: SWAP2 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB23_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: PUSH1 0xB7 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH4 @.BB23_3 -; CHECK-NEXT: JUMP -; CHECK-NEXT: .BB23_2: -; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0xC3 -; CHECK-NEXT: NOT -; CHECK-NEXT: .BB23_3: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 -184, i256 -196 @@ -406,15 +368,15 @@ define i256 @select_var_12345(i1 %a, i256 %b) { ; CHECK-LABEL: select_var_12345: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: DUP1 +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH4 @.BB24_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH2 0x3039 -; CHECK-NEXT: .BB24_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 %b, i256 12345 @@ -425,16 +387,14 @@ define i256 @select_12345_var(i1 %a, i256 %b) { ; CHECK-LABEL: select_12345_var: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: DUP2 +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB25_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH2 0x3039 -; CHECK-NEXT: .BB25_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 12345, i256 %b @@ -445,16 +405,15 @@ define i256 @select_var_n12345(i1 %a, i256 %b) { ; CHECK-LABEL: select_var_n12345: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH2 0x3039 +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: ADD +; CHECK-NEXT: SWAP1 ; CHECK-NEXT: PUSH1 0x1 ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH4 @.BB26_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: POP -; CHECK-NEXT: PUSH2 0x3038 -; CHECK-NEXT: NOT -; CHECK-NEXT: .BB26_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: MUL +; CHECK-NEXT: SUB ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 %b, i256 -12345 @@ -465,17 +424,15 @@ define i256 @select_n12345_var(i1 %a, i256 %b) { ; CHECK-LABEL: select_n12345_var: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH1 0x1 -; CHECK-NEXT: AND -; CHECK-NEXT: ISZERO -; CHECK-NEXT: PUSH4 @.BB27_2 -; CHECK-NEXT: JUMPI -; CHECK-NEXT: ; %bb.1: -; CHECK-NEXT: POP +; CHECK-NEXT: DUP2 ; CHECK-NEXT: PUSH2 0x3038 ; CHECK-NEXT: NOT -; CHECK-NEXT: .BB27_2: -; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SUB +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: PUSH1 0x1 +; CHECK-NEXT: AND +; CHECK-NEXT: MUL +; CHECK-NEXT: ADD ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP %1 = select i1 %a, i256 -12345, i256 %b From ee2692bf3dba4b3c6a4bbfc33b9d194be7f5db39 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 30 Jul 2025 15:10:35 +0200 Subject: [PATCH 173/203] [EVM] Add pre-commit test for Select (setcc x, 0, ne) as ISZERO ISZERO --- .../CodeGen/EVM/setcc-with-0-selection.ll | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/setcc-with-0-selection.ll diff --git a/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll b/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll new file mode 100644 index 000000000000..b0b116d18821 --- /dev/null +++ b/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define i1 @test_eq(i256 %x) { +; CHECK-LABEL: test_eq: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %cmp = icmp eq i256 %x, 0 + ret i1 %cmp +} + +define i1 @test_ne(i256 %x) { +; CHECK-LABEL: test_ne: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: JUMP + %cmp = icmp ne i256 %x, 0 + ret i1 %cmp +} From e1bd77e3d9e9148576ecd5097d18f22b4babc973 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 30 Jul 2025 15:17:08 +0200 Subject: [PATCH 174/203] [EVM][ISel] Select (setcc x, 0, ne) as ISZERO ISZERO Before the patch it was selected as `PUSH0 EQ ISZERO`. --- llvm/lib/Target/EVM/EVMInstrInfo.td | 1 + llvm/test/CodeGen/EVM/jumps-are-expensive.ll | 6 ++---- llvm/test/CodeGen/EVM/setcc-with-0-selection.ll | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 40536fe874c6..661449ac23dd 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -338,6 +338,7 @@ def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETULE), def : Pat<(setcc GPR:$rs0, GPR:$rs1, SETUGE), (ISZERO (ULT GPR:$rs0, GPR:$rs1))>; +def : Pat<(setcc GPR:$rs0, 0, SETNE), (ISZERO (ISZERO GPR:$rs0))>; //===----------------------------------------------------------------------===// // EVM bitwise instructions. diff --git a/llvm/test/CodeGen/EVM/jumps-are-expensive.ll b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll index b873258b3af5..95bf38e6ada6 100644 --- a/llvm/test/CodeGen/EVM/jumps-are-expensive.ll +++ b/llvm/test/CodeGen/EVM/jumps-are-expensive.ll @@ -11,12 +11,10 @@ define i256 @test(i256 %a, i256 %b) { ; CHECK: ; %bb.0: ; %bb1 ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: ISZERO ; CHECK-NEXT: SWAP1 -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: ISZERO ; CHECK-NEXT: AND ; CHECK-NEXT: PUSH4 @.BB0_2 diff --git a/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll b/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll index b0b116d18821..ea8fb84d7f54 100644 --- a/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll +++ b/llvm/test/CodeGen/EVM/setcc-with-0-selection.ll @@ -18,8 +18,7 @@ define i1 @test_ne(i256 %x) { ; CHECK-LABEL: test_ne: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: ISZERO ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: JUMP From 2a12b133f2cec055518cd051030e121b5a82916a Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 11 Jul 2025 14:16:53 +0200 Subject: [PATCH 175/203] [EVM] Add support for calculating module size Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/CMakeLists.txt | 1 + .../lib/Target/EVM/EVMCalculateModuleSize.cpp | 139 +++++++ llvm/lib/Target/EVM/EVMCalculateModuleSize.h | 29 ++ llvm/lib/Target/EVM/EVMInstrInfo.td | 37 +- llvm/unittests/Target/EVM/BytecodeSize.cpp | 361 ++++++++++++++++++ llvm/unittests/Target/EVM/CMakeLists.txt | 1 + 6 files changed, 561 insertions(+), 7 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp create mode 100644 llvm/lib/Target/EVM/EVMCalculateModuleSize.h create mode 100644 llvm/unittests/Target/EVM/BytecodeSize.cpp diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index c86f17337869..a9cdfd01579e 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -42,6 +42,7 @@ add_llvm_target(EVMCodeGen EVMArgumentMove.cpp EVMAsmPrinter.cpp EVMBackwardPropagationStackification.cpp + EVMCalculateModuleSize.cpp EVMConstantUnfolding.cpp EVMCodegenPrepare.cpp EVMFinalizeStackFrames.cpp diff --git a/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp b/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp new file mode 100644 index 000000000000..9d6b9d97c96f --- /dev/null +++ b/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp @@ -0,0 +1,139 @@ +//===----- EVMCalculateModuleSize.cpp - Calculate module size --*- 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 +// +//===----------------------------------------------------------------------===// +// +// A utility set for determining the overall size of a module. +// +//===----------------------------------------------------------------------===// + +#include "EVMCalculateModuleSize.h" +#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +// This is the copied from AsmPrinter::isBlockOnlyReachableByFallthrough. +static bool isBlockOnlyReachableByFallthrough(const MachineBasicBlock *MBB) { + // If this is a landing pad, it isn't a fall through. If it has no preds, + // then nothing falls through to it. + if (MBB->isEHPad() || MBB->pred_empty()) + return false; + + // If there isn't exactly one predecessor, it can't be a fall through. + if (MBB->pred_size() > 1) + return false; + + // The predecessor has to be immediately before this block. + MachineBasicBlock *Pred = *MBB->pred_begin(); + if (!Pred->isLayoutSuccessor(MBB)) + return false; + + // If the block is completely empty, then it definitely does fall through. + if (Pred->empty()) + return true; + + // Check the terminators in the previous blocks + for (const auto &MI : Pred->terminators()) { + // If it is not a simple branch, we are in a table somewhere. + if (!MI.isBranch() || MI.isIndirectBranch()) + return false; + + // If we are the operands of one of the branches, this is not a fall + // through. Note that targets with delay slots will usually bundle + // terminators with the delay slot instruction. + for (ConstMIBundleOperands OP(MI); OP.isValid(); ++OP) { + if (OP->isJTI()) + return false; + if (OP->isMBB() && OP->getMBB() == MBB) + return false; + } + } + + return true; +} + +// Return the size of an instruction in bytes. For some pseudo instructions, +// don't use the size from the instruction description, since during code +// generation, some instructions will be relaxed to smaller instructions. +static unsigned getInstSize(const MachineInstr &MI, + const TargetInstrInfo *TII) { + // Skip debug instructions. + if (MI.isDebugInstr()) + return 0; + + unsigned Size = 0; + switch (MI.getOpcode()) { + default: + Size = MI.getDesc().getSize(); + break; + case EVM::PseudoCALL: + // In case that function call has a return label, we will emit JUMPDEST, + // so take it into account. + if (MI.getNumExplicitOperands() > 1) + Size = TII->get(EVM::JUMPDEST_S).getSize(); + LLVM_FALLTHROUGH; + case EVM::PseudoJUMPI: + case EVM::PseudoJUMP: + // We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S, + // since a 16-bit immediate covers the 24,576-byte EVM runtime code cap + // (EIP-170). If a wider immediate were ever required, the contract + // already exceeds the cap, so the push width is moot. + Size += TII->get(EVM::PUSH2_S).getSize() + TII->get(EVM::JUMP_S).getSize(); + break; + case EVM::PUSH_LABEL: + // We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S, + // since a 16-bit immediate covers the 24,576-byte EVM runtime code cap + // (EIP-170). If a wider immediate were ever required, the contract + // already exceeds the cap, so the push width is moot. + Size = TII->get(EVM::PUSH2_S).getSize(); + break; + } + return Size; +} + +uint64_t llvm::EVM::calculateFunctionCodeSize(const MachineFunction &MF) { + uint64_t Size = 0; + const auto *TII = MF.getSubtarget().getInstrInfo(); + + // If the function has a PUSHDEPLOYADDRESS, it starts with a PUSH20. + if (const auto *MFI = MF.getInfo(); + MFI->getHasPushDeployAddress()) + Size += TII->get(EVM::PUSH20).getSize(); + + for (const MachineBasicBlock &MBB : MF) { + // If the block is not only reachable by fallthrough, it starts with + // a JUMPDEST instruction. + if (!isBlockOnlyReachableByFallthrough(&MBB)) + Size += TII->get(EVM::JUMPDEST_S).getSize(); + + Size += std::accumulate(MBB.begin(), MBB.end(), 0, + [&TII](unsigned Sum, const MachineInstr &MI) { + return Sum + getInstSize(MI, TII); + }); + } + return Size; +} + +uint64_t llvm::EVM::calculateModuleCodeSize(Module &M, + const MachineModuleInfo &MMI) { + uint64_t TotalSize = 0; + for (Function &F : M) { + MachineFunction *MF = MMI.getMachineFunction(F); + if (!MF) + continue; + TotalSize += llvm::EVM::calculateFunctionCodeSize(*MF); + } + + // Take into account INVALID instruction at the end of the .text section. + TotalSize++; + return TotalSize; +} diff --git a/llvm/lib/Target/EVM/EVMCalculateModuleSize.h b/llvm/lib/Target/EVM/EVMCalculateModuleSize.h new file mode 100644 index 000000000000..9acdf82684bb --- /dev/null +++ b/llvm/lib/Target/EVM/EVMCalculateModuleSize.h @@ -0,0 +1,29 @@ +//===----- EVMCalculateModuleSize.h - Calculate module size ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// A utility set for determining the overall size of a module. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H +#define LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H + +#include + +namespace llvm { +class MachineFunction; +class MachineModuleInfo; +class Module; + +namespace EVM { +uint64_t calculateFunctionCodeSize(const MachineFunction &MF); +uint64_t calculateModuleCodeSize(Module &M, const MachineModuleInfo &MMI); +} // namespace EVM +} // namespace llvm + +#endif // LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 661449ac23dd..c14315f4d227 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -244,7 +244,10 @@ def FCALL } // Uses = [SP], isCall = 1 let isCall = 1 in def PseudoCALL - : EVMPseudo<(outs), (ins jmptarget:$callee, variable_ops), [], true>; + : EVMPseudo<(outs), (ins jmptarget:$callee, variable_ops), [], true> { + // PUSH4_S + JUMP_S. + let Size = 6; +} //===----------------------------------------------------------------------===// // EVM arithmetic instructions. @@ -411,13 +414,19 @@ defm JUMPI : I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)], "JUMPI", "$dst, $cond", 0x57, 10>; def JUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst, GPR:$cond), []>; -def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; +def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true> { + // PUSH4_S + JUMPI_S. + let Size = 6; +} def PseudoJUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; let isBarrier = 1 in { defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", "$dst", 0x56, 8>; - def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>; + def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true> { + // PUSH4_S + JUMP_S. + let Size = 6; + } } // isBarrier = 1 } // isBranch = 1, isTerminator = 1 @@ -829,6 +838,8 @@ defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], true, "", 0, 0> { let isCodeGenOnly = 1; + // PUSH4_S. + let Size = 5; } def PUSH_FRAME : NI<(outs), (ins i256imm:$imm), [], true, "", 0, 0> { @@ -1162,22 +1173,34 @@ def PUSH32_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH32$imm", let isAsCheapAsAMove = 1, isReMaterializable = 1, isCodeGenOnly = 1, hasSideEffects = 0 in { let BaseName = "DATASIZE" in { def DATASIZE : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; - def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; + def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0> { + // PUSH4_S + let Size = 5; + } } let BaseName = "DATAOFFSET" in { def DATAOFFSET : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>; - def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>; + def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0> { + // PUSH4_S + let Size = 5; + } } let BaseName = "LINKERSYMBOL" in { def LINKERSYMBOL : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; - def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>; + def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0> { + // PUSH20_S + let Size = 21; + } } let BaseName = "LOADIMMUTABLE" in { def LOADIMMUTABLE : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>; - def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>; + def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0> { + // PUSH32_S + let Size = 33; + } } } diff --git a/llvm/unittests/Target/EVM/BytecodeSize.cpp b/llvm/unittests/Target/EVM/BytecodeSize.cpp new file mode 100644 index 000000000000..e382e7926a8a --- /dev/null +++ b/llvm/unittests/Target/EVM/BytecodeSize.cpp @@ -0,0 +1,361 @@ +//===- BytecodeSize.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 +// +//===----------------------------------------------------------------------===// + +#include "EVMCalculateModuleSize.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +std::unique_ptr createTargetMachine() { + auto TT(Triple::normalize("evm--")); + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error); + return std::unique_ptr(static_cast( + TheTarget->createTargetMachine(TT, "", "", TargetOptions(), std::nullopt, + std::nullopt, CodeGenOptLevel::Default))); +} + +class BytecodeSizeTest : public testing::Test { +protected: + static const char *MIRString; + LLVMContext Context; + std::unique_ptr TM; + std::unique_ptr MMI; + std::unique_ptr Parser; + std::unique_ptr M; + + static void SetUpTestCase() { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + } + + void SetUp() override { + TM = createTargetMachine(); + std::unique_ptr MBuffer = + MemoryBuffer::getMemBuffer(MIRString); + Parser = createMIRParser(std::move(MBuffer), Context); + if (!Parser) + report_fatal_error("null MIRParser"); + M = Parser->parseIRModule(); + if (!M) + report_fatal_error("parseIRModule failed"); + M->setTargetTriple(TM->getTargetTriple().getTriple()); + M->setDataLayout(TM->createDataLayout()); + MMI = std::make_unique(TM.get()); + if (Parser->parseMachineFunctions(*M, *MMI)) + report_fatal_error("parseMachineFunctions failed"); + } +}; + +TEST_F(BytecodeSizeTest, Test) { + uint64_t Size = EVM::calculateModuleCodeSize(*M, *MMI); + ASSERT_TRUE(Size == 131); +} + +const char *BytecodeSizeTest::MIRString = R"MIR( +--- | + ; ModuleID = '/Users/pavelkopyl/projects/mlab/era-compiler-tester/tests/llvm/evm/complex/egcd.ll' + source_filename = "/Users/pavelkopyl/projects/mlab/era-compiler-tester/tests/llvm/evm/complex/egcd.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + ; Function Attrs: nocallback noduplicate nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) + declare i256 @llvm.evm.pushdeployaddress() #0 + + ; Function Attrs: noreturn + define void @egcd() #1 { + bb: + %deploy_addr = tail call i256 @llvm.evm.pushdeployaddress() + %addr_arg = inttoptr i256 0 to ptr addrspace(2) + %arg = call i256 @llvm.evm.calldataload(ptr addrspace(2) %addr_arg) + %addr_arg1 = inttoptr i256 32 to ptr addrspace(2) + %arg1 = call i256 @llvm.evm.calldataload(ptr addrspace(2) %addr_arg1) + %addr1 = inttoptr i256 32 to ptr addrspace(1) + store i256 %deploy_addr, ptr addrspace(1) %addr1, align 4 + %addr2 = inttoptr i256 64 to ptr addrspace(1) + store i256 0, ptr addrspace(1) %addr2, align 4 + %i = icmp eq i256 %arg1, 0 + br i1 %i, label %bb20, label %bb4.preheader + + bb4.preheader: ; preds = %bb + br label %bb4 + + bb4: ; preds = %bb4.preheader, %bb4 + %i5 = phi i256 [ %i9, %bb4 ], [ 1, %bb4.preheader ] + %i6 = phi i256 [ %i7, %bb4 ], [ %arg, %bb4.preheader ] + %i7 = phi i256 [ %i13, %bb4 ], [ %arg1, %bb4.preheader ] + %i8 = phi i256 [ %i17, %bb4 ], [ 1, %bb4.preheader ] + %i9 = phi i256 [ %i15, %bb4 ], [ 0, %bb4.preheader ] + %i10 = phi i256 [ %i8, %bb4 ], [ 0, %bb4.preheader ] + %i11 = sdiv i256 %i6, %i7 + %i12 = mul nsw i256 %i11, %i7 + %i13 = sub nsw i256 %i6, %i12 + %i14 = mul nsw i256 %i11, %i9 + %i15 = sub nsw i256 %i5, %i14 + %i16 = mul nsw i256 %i11, %i8 + %i17 = sub nsw i256 %i10, %i16 + %i18 = icmp eq i256 %i13, 0 + br i1 %i18, label %bb19, label %bb4 + + bb19: ; preds = %bb4 + store i256 %i9, ptr addrspace(1) %addr1, align 4 + store i256 %i8, ptr addrspace(1) %addr2, align 4 + br label %bb20 + + bb20: ; preds = %bb19, %bb + %i21 = phi i256 [ %i7, %bb19 ], [ %arg, %bb ] + store i256 %i21, ptr addrspace(1) null, align 4 + call void @llvm.evm.return(ptr addrspace(1) null, i256 96) + unreachable + } + + ; Function Attrs: noreturn nounwind + declare void @llvm.evm.return(ptr addrspace(1), i256) #2 + + ; Function Attrs: nounwind memory(argmem: read) + declare i256 @llvm.evm.calldataload(ptr addrspace(2)) #3 + + attributes #0 = { nocallback noduplicate nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) } + attributes #1 = { noreturn } + attributes #2 = { noreturn nounwind } + attributes #3 = { nounwind memory(argmem: read) } + +... +--- +name: egcd +alignment: 1 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: false +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } + - { id: 9, class: gpr, preferred-register: '' } + - { id: 10, class: gpr, preferred-register: '' } + - { id: 11, class: gpr, preferred-register: '' } + - { id: 12, class: gpr, preferred-register: '' } + - { id: 13, class: gpr, preferred-register: '' } + - { id: 14, class: gpr, preferred-register: '' } + - { id: 15, class: gpr, preferred-register: '' } + - { id: 16, class: gpr, preferred-register: '' } + - { id: 17, class: gpr, preferred-register: '' } + - { id: 18, class: gpr, preferred-register: '' } + - { id: 19, class: gpr, preferred-register: '' } + - { id: 20, class: gpr, preferred-register: '' } + - { id: 21, class: gpr, preferred-register: '' } + - { id: 22, class: gpr, preferred-register: '' } + - { id: 23, class: gpr, preferred-register: '' } + - { id: 24, class: gpr, preferred-register: '' } + - { id: 25, class: gpr, preferred-register: '' } + - { id: 26, class: gpr, preferred-register: '' } + - { id: 27, class: gpr, preferred-register: '' } + - { id: 28, class: gpr, preferred-register: '' } + - { id: 29, class: gpr, preferred-register: '' } + - { id: 30, class: gpr, preferred-register: '' } + - { id: 31, class: gpr, preferred-register: '' } + - { id: 32, class: gpr, preferred-register: '' } + - { id: 33, class: gpr, preferred-register: '' } + - { id: 34, class: gpr, preferred-register: '' } + - { id: 35, class: gpr, preferred-register: '' } + - { id: 36, class: gpr, preferred-register: '' } + - { id: 37, class: gpr, preferred-register: '' } + - { id: 38, class: gpr, preferred-register: '' } + - { id: 39, class: gpr, preferred-register: '' } + - { id: 40, class: gpr, preferred-register: '' } + - { id: 41, class: gpr, preferred-register: '' } + - { id: 42, class: gpr, preferred-register: '' } +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: true +body: | + bb.0.bb: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $arguments, $value_stack + + PUSH0_S + SWAP1_S + PUSH0_S + CALLDATALOAD_S + SWAP1_S + PUSH1_S i256 32 + CALLDATALOAD_S + SWAP1_S + PUSH0_S + PUSH1_S i256 64 + MSTORE_S + PUSH1_S i256 32 + MSTORE_S + PUSH1_S i256 32 + CALLDATALOAD_S + PseudoJUMPI %bb.2 + + bb.1: + successors: %bb.5(0x80000000) + liveins: $value_stack + + POP_S + POP_S + POP_S + PUSH0_S + CALLDATALOAD_S + PseudoJUMP %bb.5 + + bb.2.bb4.preheader: + successors: %bb.3(0x80000000) + liveins: $value_stack + + PUSH1_S i256 1 + SWAP3_S + SWAP2_S + SWAP3_S + SWAP2_S + PUSH1_S i256 1 + SWAP3_S + PUSH0_S + SWAP2_S + + bb.3.bb4: + successors: %bb.4(0x00000800), %bb.6(0x7ffff800) + liveins: $value_stack + + SWAP4_S + SWAP3_S + SWAP5_S + DUP6_S + DUP1_S + DUP3_S + SDIV_S + SWAP4_S + DUP6_S + DUP6_S + MUL_S + SWAP1_S + SUB_S + SWAP3_S + DUP7_S + DUP6_S + MUL_S + SWAP1_S + SUB_S + SWAP4_S + MUL_S + SWAP1_S + SUB_S + DUP1_S + SWAP3_S + DUP6_S + DUP8_S + SWAP2_S + DUP7_S + SWAP6_S + PseudoJUMP_UNLESS %bb.4 + + bb.6: + successors: %bb.3(0x80000000) + liveins: $value_stack + + SWAP3_S + SWAP6_S + POP_S + SWAP3_S + SWAP6_S + POP_S + SWAP6_S + POP_S + PseudoJUMP %bb.3 + + bb.4.bb19: + successors: %bb.5(0x80000000) + liveins: $value_stack + + POP_S + POP_S + POP_S + POP_S + POP_S + POP_S + PUSH1_S i256 64 + MSTORE_S + PUSH1_S i256 32 + MSTORE_S + + bb.5.bb20: + liveins: $value_stack + + PUSH0_S + MSTORE_S + PUSH1_S i256 96 + PUSH0_S + RETURN_S + +... +)MIR"; + +} // anonymous namespace diff --git a/llvm/unittests/Target/EVM/CMakeLists.txt b/llvm/unittests/Target/EVM/CMakeLists.txt index ab5689adb0a8..f9e30b1527b8 100644 --- a/llvm/unittests/Target/EVM/CMakeLists.txt +++ b/llvm/unittests/Target/EVM/CMakeLists.txt @@ -18,6 +18,7 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_target_unittest(EVMTests + BytecodeSize.cpp SpillRegion.cpp StackShuffler.cpp StackModel.cpp From a52f17a17eebb32a51de9c17a0c363bca2663a7a Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Mon, 14 Jul 2025 12:27:01 +0200 Subject: [PATCH 176/203] [EVM] ConstantUnfolding: add fallback to size optimization mode when module bytecode exceeds the 24KB limit Key changes introduced: - Transitioned from a MachineFunction pass to a Module-level pass - Implemented module-wide code size estimation (not 100% exact but sufficiently accurate for our purpose) - Applied transformations by iterating over functions within the module. For each function: - Handled both loop and non-loop basic blocks using the default heuristic (i.e., based on the presence of the OptSize attribute) - If the module size exceeds the defined limit, switched to a fallback mode that treats all functions as if they have OptSize flag. - Repeatedly traversed all functions, targeting only basic blocks at a specific loop nesting depth (starting from depth 0 for non-loop blocks), and re-evaluated module size. The process stops once the size drops below the limit, or reaching the loop depth limit. This enables a balanced distribution of runtime gas regressions across functions within the module, resulting from aggressive constant unfolding. --- llvm/lib/Target/EVM/EVM.h | 7 +- llvm/lib/Target/EVM/EVMConstantUnfolding.cpp | 685 +++++++++++------- llvm/lib/Target/EVM/EVMInstrFormats.td | 23 +- llvm/lib/Target/EVM/EVMInstrInfo.h | 17 +- llvm/lib/Target/EVM/EVMInstrInfo.td | 6 +- llvm/test/CodeGen/EVM/O3-pipeline.ll | 5 +- .../CodeGen/EVM/bitmanipulation-intrinsics.ll | 8 +- .../EVM/constant-unfolding-Oz-fallback.ll | 78 ++ .../test/CodeGen/EVM/constant-unfolding-Oz.ll | 35 +- llvm/test/CodeGen/EVM/constant-unfolding.ll | 13 + .../CodeGen/EVM/force-constant-unfolding.ll | 2 +- 11 files changed, 550 insertions(+), 329 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index df60301eb3b1..3589454f1011 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -42,13 +42,8 @@ namespace EVMCOST { unsigned constexpr SWAP = 3; unsigned constexpr DUP = 3; unsigned constexpr POP = 2; -unsigned constexpr PUSH0 = 2; unsigned constexpr PUSH = 3; unsigned constexpr MLOAD = 3; -unsigned constexpr SHIFT = 3; -unsigned constexpr ADD = 3; -unsigned constexpr SUB = 3; -unsigned constexpr NOT = 3; } // namespace EVMCOST // LLVM IR passes. @@ -75,7 +70,7 @@ FunctionPass *createEVMBPStackification(); FunctionPass *createEVMLowerJumpUnless(); ModulePass *createEVMFinalizeStackFrames(); ModulePass *createEVMMarkRecursiveFunctionsPass(); -FunctionPass *createEVMConstantUnfolding(); +ModulePass *createEVMConstantUnfolding(); // PassRegistry initialization declarations. void initializeEVMCodegenPreparePass(PassRegistry &); diff --git a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp index e3f8cfa28c98..97ad99e8515c 100644 --- a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp +++ b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp @@ -24,209 +24,239 @@ //===----------------------------------------------------------------------===// #include "EVM.h" +#include "EVMCalculateModuleSize.h" #include "EVMInstrInfo.h" -#include "EVMMachineFunctionInfo.h" +#include "EVMSubtarget.h" #include "MCTargetDesc/EVMMCTargetDesc.h" #include "TargetInfo/EVMTargetInfo.h" #include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineDominators.h" #include "llvm/CodeGen/MachineLoopInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassInstrumentation.h" #include "llvm/InitializePasses.h" #include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" #include using namespace llvm; #define DEBUG_TYPE "evm-constant-unfolding" +#define PASS_NAME "EVM constant unfolding" -STATISTIC(NumOfUnfoldings, "Number of unfolded constants"); +STATISTIC(StatNumOfUnfoldings, "Number of unfolded constants"); +STATISTIC(StatCodeSizeReduction, "Total size reduction across all functions"); // The initial values of the following options were determined experimentally // to allow constant unfolding in non-OptForSize mode without noticeably // impacting performance. static cl::opt - SizeGainThreshold("evm-const-unfolding-size-gain-threshold", cl::Hidden, - cl::init(2.5), - cl::desc("Minimum original-to-unfolded size ratio" - "required for constant unfolding when" - "optimizing for speed")); + SizeReductionThreshold("evm-const-unfolding-size-reduction-threshold", + cl::Hidden, cl::init(2.5), + cl::desc("Minimum original-to-unfolded size ratio" + "required for constant unfolding when" + "optimizing for speed")); -static cl::opt - InstrNumLimitUnfolInto("evm-const-unfolding-inst-num-limit", cl::Hidden, - cl::init(3), - cl::desc("Maximum number of instructions an original" - "instruction can be unfolded into")); - -static cl::opt GasWeight("evm-const-unfolding-gas-weight", cl::Hidden, - cl::init(2), - cl::desc("Gas weight used as a factor in the" - "transformation cost function")); +static cl::opt InstrNumLimitUnfoldInto( + "evm-const-unfolding-inst-num-limit", cl::Hidden, cl::init(3), + cl::desc("Maximum number of instructions an original" + "instruction can be unfolded into")); static cl::opt LoopDepthLimit("evm-const-loop-depth-limit", cl::Hidden, cl::init(2), cl::desc("The maximum loop depth at which constant" "unfolding is still considered beneficial")); +static cl::opt + CodeSizeLimit("evm-bytecode-sizelimit", cl::Hidden, cl::init(24576), + cl::desc("EVM contract bytecode size limit")); + namespace { -class EVMConstantUnfolding final : public MachineFunctionPass { +using InstrsPerLoopDepthTy = SmallVector>; + +// Estimates the execution cost of EVM-style stack operations. +// Tracks instruction count, gas cost, and unfolded bytecode size. +// It abstracts gas accounting for pushes and simple arithmetic/logical +// operations. +class StackCostModel { public: - static char ID; + StackCostModel() = default; - EVMConstantUnfolding() : MachineFunctionPass(ID) {} + void accountInstr(unsigned Opc, const TargetInstrInfo *TII) { + ++InstrCount; + const MCInstrDesc &Desc = TII->get(Opc); + ByteSize += Desc.getSize(); + Gas += EVMInstrInfo::getGasCost(Desc); + } -private: - // Estimates the execution cost of EVM-style stack operations. - // Tracks instruction count, gas cost, and unfolded bytecode size. - // It abstracts gas accounting for pushes and simple arithmetic/logical - // operations. - class StackCostModel { - public: - StackCostModel() = default; - - void push(const APInt &Val) { - Gas += (Val.isZero() ? EVMCOST::PUSH0 : EVMCOST::PUSH); - ByteSize += getPushSize(Val); - ++InstrCount; - } + unsigned getInstrCount() const { return InstrCount; } + unsigned getByteSize() const { return ByteSize; } + unsigned getGas() const { return Gas; } - void shift() { accountInstr(EVMCOST::SHIFT); } +private: + unsigned InstrCount{0}; + unsigned ByteSize{0}; + unsigned Gas{0}; +}; - void add() { accountInstr(EVMCOST::ADD); } +// Builds and applies a sequence of machine instructions required to +// unfold a constant. Instruction generation is deferred using lambdas, +// allowing the TransformationCandidate object to be reused for repeated +// constants. As new instructions are added, the StackCostModel is used +// to track the accumulated cost. +class TransformationCandidate { +public: + TransformationCandidate(LLVMContext &Context, const TargetInstrInfo *TII) + : Context(Context), TII(TII) {} - void sub() { accountInstr(EVMCOST::SUB); } + void addShl() { + constexpr unsigned Opc = EVM::SHL_S; + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this](MachineInstr &MI) { insertInstr(MI, Opc); }); + } - void bit_not() { accountInstr(EVMCOST::NOT); } + void addShr() { + constexpr unsigned Opc = EVM::SHR_S; + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this](MachineInstr &MI) { insertInstr(MI, Opc); }); + } - unsigned getInstrCount() const { return InstrCount; } - unsigned getByteSize() const { return ByteSize; } - unsigned getGas() const { return Gas; } + void addNot() { + constexpr unsigned Opc = EVM::NOT_S; + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this](MachineInstr &MI) { insertInstr(MI, Opc); }); + } - // Get the size of the PUSH instruction required for - // the immediate value. - static unsigned getPushSize(const APInt &Val) { - return 1 + (alignTo(Val.getActiveBits(), 8) / 8); - } + void addImm(const APInt &Val) { + unsigned Opc = EVM::getStackOpcode(EVM::getPUSHOpcode(Val)); + CostModel.accountInstr(Opc, TII); + BuildItems.push_back([this, Opc, Val = Val](MachineInstr &MI) { + insertImmInstr(MI, Opc, Val); + }); + } - private: - void accountInstr(unsigned GasCost) { - ++InstrCount; - ++ByteSize; - Gas += GasCost; - } + // Applies queued build instruction steps to replace a given instruction. + void apply(MachineInstr &MI) const { + for (const auto &func : BuildItems) + func(MI); + } - unsigned InstrCount = 0; - unsigned ByteSize = 0; - unsigned Gas = 0; - }; - - // Builds and applies a sequence of machine instructions required to - // unfold a constant. Instruction generation is deferred using lambdas, - // allowing the TransformationBuilder object to be reused for repeated - // constants. As new instructions are added, the StackCostModel is used - // to track the accumulated cost. - class TransformationBuilder { - public: - TransformationBuilder(LLVMContext &Context, const TargetInstrInfo *TII) - : Context(Context), TII(TII) {} - - void addSub() { - CostModel.sub(); - BuildItems.push_back( - [this](MachineInstr &MI) { insertInstr(MI, EVM::SUB_S); }); - } + const StackCostModel &getCost() const { return CostModel; } - void addShl() { - CostModel.shift(); - BuildItems.push_back( - [this](MachineInstr &MI) { insertInstr(MI, EVM::SHL_S); }); - } +private: + using BuildFunction = std::function; - void addShr() { - CostModel.shift(); - BuildItems.push_back( - [this](MachineInstr &MI) { insertInstr(MI, EVM::SHR_S); }); - } + LLVMContext &Context; + const TargetInstrInfo *TII{}; - void addNot() { - CostModel.bit_not(); - BuildItems.push_back( - [this](MachineInstr &MI) { insertInstr(MI, EVM::NOT_S); }); - } + StackCostModel CostModel; + SmallVector BuildItems; - void addImm(const APInt &Val) { - CostModel.push(Val); - BuildItems.push_back( - [this, Val = Val](MachineInstr &MI) { buildImm(MI, Val); }); - } + void insertInstr(MachineInstr &MI, unsigned Opc) { + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(Opc)); + } - // Applies queued build instruction steps to replace a given instruction. - void apply(MachineInstr &MI) const { - for (const auto &func : BuildItems) - func(MI); - } + void insertImmInstr(MachineInstr &MI, unsigned Opc, const APInt &Val) { + auto NewMI = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(Opc)); + if (Opc != EVM::PUSH0_S) + NewMI.addCImm(ConstantInt::get(Context, Val)); + } +}; - const StackCostModel &getCost() const { return CostModel; } +// Discovers, applies, and caches optimal constant unfolding +// transformations. +class ConstantUnfolder { +public: + explicit ConstantUnfolder(LLVMContext *Context) : Context(Context) {} - private: - using BuildFunction = std::function; + unsigned getCodeSizeReduction() const { return OverallCodeReductionSize; } - LLVMContext &Context; - const TargetInstrInfo *TII; + bool tryToUnfoldConstant(MachineInstr &MI, bool OptForSize, + const TargetInstrInfo *TII); - StackCostModel CostModel; - SmallVector BuildItems; +private: + LLVMContext *Context{}; - void insertInstr(MachineInstr &MI, unsigned Opcode) { - BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(Opcode)); - } + // The 'second' field can be set to 0 or 1, indicating whether to + // optimize for performance or size. + using TransformationKey = std::pair; + DenseMap> + TransformationCache; - void buildImm(MachineInstr &MI, const APInt &Val) { - unsigned PushOpc = EVM::getPUSHOpcode(Val); - auto NewMI = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), - TII->get(EVM::getStackOpcode(PushOpc))); - if (PushOpc != EVM::PUSH0) - NewMI.addCImm(ConstantInt::get(Context, Val)); - } - }; + unsigned OverallCodeReductionSize{0}; - MachineFunction *MF = nullptr; - const TargetInstrInfo *TII = nullptr; - DenseMap> TransformationCache; + const TransformationCandidate * + findOptimalTransformation(const APInt &Imm, bool OptForSize, + const TargetInstrInfo *TII); - StringRef getPassName() const override { return "EVM constant unfolding"; } + void reduceCodeSizeOn(unsigned Size) { + OverallCodeReductionSize += Size; + ++StatNumOfUnfoldings; + } +}; - bool runOnMachineFunction(MachineFunction &MF) override; +class EVMConstantUnfolding final : public ModulePass { +public: + static char ID; - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.addRequired(); - MachineFunctionPass::getAnalysisUsage(AU); + EVMConstantUnfolding() : ModulePass(ID) { + initializeEVMConstantUnfoldingPass(*PassRegistry::getPassRegistry()); } - const TransformationBuilder *findOptimalTransfomtation(const APInt &Imm); +private: + StringRef getPassName() const override { return PASS_NAME; } + + bool runOnModule(Module &M) override; - bool tryUnfoldConstant(MachineInstr &MI); + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addPreserved(); + AU.setPreservesAll(); + ModulePass::getAnalysisUsage(AU); + } }; } // end anonymous namespace char EVMConstantUnfolding::ID = 0; -INITIALIZE_PASS_BEGIN(EVMConstantUnfolding, DEBUG_TYPE, "Constant unfolding", - false, false) -INITIALIZE_PASS_DEPENDENCY(MachineLoopInfoWrapperPass) -INITIALIZE_PASS_END(EVMConstantUnfolding, DEBUG_TYPE, "Constant unfolding", - false, false) +INITIALIZE_PASS_BEGIN(EVMConstantUnfolding, DEBUG_TYPE, PASS_NAME, false, false) +INITIALIZE_PASS_DEPENDENCY(MachineModuleInfoWrapperPass) +INITIALIZE_PASS_END(EVMConstantUnfolding, DEBUG_TYPE, PASS_NAME, false, false) -FunctionPass *llvm::createEVMConstantUnfolding() { +ModulePass *llvm::createEVMConstantUnfolding() { return new EVMConstantUnfolding(); } -const EVMConstantUnfolding::TransformationBuilder * -EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { - if (auto It = TransformationCache.find(Imm); +static bool isBetterCandidate(const TransformationCandidate &A, + const TransformationCandidate &B, + bool OptForSize) { + static constexpr unsigned Weight = 2; + const StackCostModel &CostA = A.getCost(); + const StackCostModel &CostB = B.getCost(); + unsigned ScoreA = 0; + unsigned ScoreB = 0; + + if (OptForSize) { + ScoreA = (CostA.getByteSize() * Weight) + CostA.getGas(); + ScoreB = (CostB.getByteSize() * Weight) + CostB.getGas(); + } else { + ScoreA = CostA.getByteSize() + (Weight * CostA.getGas()); + ScoreB = CostB.getByteSize() + (Weight * CostB.getGas()); + } + if (ScoreA != ScoreB) + return ScoreA < ScoreB; + + return CostA.getByteSize() < CostB.getByteSize(); +} + +const TransformationCandidate * +ConstantUnfolder::findOptimalTransformation(const APInt &Imm, bool OptForSize, + const TargetInstrInfo *TII) { + if (auto It = TransformationCache.find({Imm, OptForSize}); It != TransformationCache.end()) { - LLVM_DEBUG({ dbgs() << " Retrieving transformation from the cache\n"; }); + LLVM_DEBUG( + { dbgs() << " Retrieving transformation from the cache\n"; }); + return It->second.get(); } @@ -248,9 +278,7 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { assert(ValLen == (Imm.getBitWidth() - TrailZ - LeadZ)); assert(Val.isNegative()); - bool OptForSize = MF->getFunction().hasOptSize(); - - SmallVector, 8> Transformations; + SmallVector, 8> Transformations; // 1. A transformation that represents an immediate value as: // @@ -272,13 +300,9 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { // -------------------- // Optional // PUSH1 shiftr // 2 // SHL // 1 - // --------- - // [2 ... 8] + AbsValLen // { - // Not and left/right shift - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); assert(!Val.abs().isZero()); Tr->addImm(Val.abs() - 1); Tr->addNot(); @@ -307,13 +331,10 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { // NOT // 1 // PUSH1 shift // 2 // SHR // 1 - // --------- - // 5 // if (IsMask && !TrailZ) { assert(ValLen != 256); - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); Tr->addImm(APInt::getZero(256)); Tr->addNot(); Tr->addImm(APInt(256, 256 - ValLen)); @@ -336,12 +357,9 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { // PUSH Val // 1 + ValLen // PUSH1 shift // 2 // SHL // 1 - // --------- - // 4 + ValLen // if (TrailZ) { - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); Tr->addImm(Val); Tr->addImm(APInt(256, TrailZ)); Tr->addShl(); @@ -363,12 +381,9 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { // Cost: // PUSH // 1 InvImm // NOT // 1 - // --------- - // 2 + InvImm // { - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); Tr->addImm(~Imm); Tr->addNot(); Transformations.emplace_back(std::move(Tr)); @@ -391,12 +406,9 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { // PUSH1 shift // 2 // SHL // 1 // NOT // 1 - // --------- - // 5 + InvImmVal // { - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); APInt ImmNot = ~Imm; unsigned Tz = ImmNot.countTrailingZeros(); unsigned Lz = ImmNot.countLeadingZeros(); @@ -411,174 +423,307 @@ EVMConstantUnfolding::findOptimalTransfomtation(const APInt &Imm) { // 6. No transformation, leave immediate as is. { - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); Tr->addImm(Imm); Transformations.emplace_back(std::move(Tr)); } - auto *OptIt = std::min_element( - Transformations.begin(), Transformations.end(), - [OptForSize](const auto &TrA, const auto &TrB) { - const StackCostModel &CostA = TrA->getCost(); - const StackCostModel &CostB = TrB->getCost(); - // When optimizing for size, our primary goal is - // to minimize the total size of the instructions. - if (OptForSize) { - // First check, if sizes differ. - if (CostA.getByteSize() != CostB.getByteSize()) - return CostA.getByteSize() < CostB.getByteSize(); - - // Then the number of operations. - return CostA.getInstrCount() < CostB.getInstrCount(); - } - // Optimizing for speed. - // Expressing total cost in terms of both size and gas - // enables more performance-friendly transformation choices. - unsigned SizeGasA = CostA.getByteSize() + (GasWeight * CostA.getGas()); - unsigned SizeGasB = CostB.getByteSize() + (GasWeight * CostB.getGas()); - if (SizeGasA != SizeGasB) - return SizeGasA < SizeGasB; - - return CostA.getInstrCount() < CostB.getInstrCount(); - }); + // Find optimal transformation. + auto *OptIt = std::min_element(Transformations.begin(), Transformations.end(), + [OptForSize](const auto &A, const auto &B) { + return isBetterCandidate(*A, *B, OptForSize); + }); #ifndef NDEBUG - LLVM_DEBUG({ dbgs() << " Candidate transformations:\n"; }); + LLVM_DEBUG({ dbgs() << " Available transformations:\n"; }); for (const auto &Tr : Transformations) { const StackCostModel &Cost = Tr->getCost(); LLVM_DEBUG({ - dbgs() << " [size: " << Cost.getByteSize() - << ", OpNum: " << Cost.getInstrCount() - << ", Gas: " << Cost.getGas() << "]\n"; + dbgs() << " [size: " << Cost.getByteSize() + << ", instr count: " << Cost.getInstrCount() + << ", gas: " << Cost.getGas() << "]\n"; }); } #endif // NDEBUG - const TransformationBuilder *Tr = OptIt->get(); + const TransformationCandidate *Tr = OptIt->get(); [[maybe_unused]] auto Res = - TransformationCache.try_emplace(Imm, std::move(*OptIt)); + TransformationCache.try_emplace({Imm, OptForSize}, std::move(*OptIt)); assert(Res.second); return Tr; } -bool EVMConstantUnfolding::tryUnfoldConstant(MachineInstr &MI) { +static bool isProfitableToTranform(const APInt &Imm, const StackCostModel &Cost, + bool OptForSize) { + if (OptForSize) + return true; + + unsigned OrigSize = (alignTo(Imm.getActiveBits(), 8) / 8) + 1; + // When optimizing for speed, only unfold constants if it reduces the size + // by a factor of at least the 'SizeReductionThreshold' and keeps the + // instruction count to 'InstrNumLimitUnfoldInto' or fewer. + if ((static_cast(OrigSize) / static_cast(Cost.getByteSize()) < + SizeReductionThreshold) || + Cost.getInstrCount() > InstrNumLimitUnfoldInto) + return false; + + return true; +} + +bool ConstantUnfolder::tryToUnfoldConstant(MachineInstr &MI, bool OptForSize, + const TargetInstrInfo *TII) { const APInt Imm = MI.getOperand(0).getCImm()->getValue().zext(256); + unsigned OrigSize = (alignTo(Imm.getActiveBits(), 8) / 8) + 1; assert(Imm.getActiveBits() > 4 * 8); // Check for the -1 value early if (Imm.isAllOnes()) { - auto Tr = std::make_unique( - MF->getFunction().getContext(), TII); + auto Tr = std::make_unique(*Context, TII); Tr->addImm(APInt::getZero(256)); Tr->addNot(); Tr->apply(MI); MI.eraseFromParent(); - ++NumOfUnfoldings; + assert(OrigSize > Tr->getCost().getByteSize()); + reduceCodeSizeOn(OrigSize - Tr->getCost().getByteSize()); - LLVM_DEBUG({ dbgs() << " Transforming -1 to ~0\n"; }); + LLVM_DEBUG({ dbgs() << " Transforming -1 to ~0\n"; }); return true; } - bool OptForSize = MF->getFunction().hasOptSize(); - const TransformationBuilder *OptTransformation = - findOptimalTransfomtation(Imm); + const TransformationCandidate *OptTransformation = + findOptimalTransformation(Imm, OptForSize, TII); const StackCostModel &OptCost = OptTransformation->getCost(); LLVM_DEBUG({ - dbgs() << " Transformation candidate:\n [size: " << OptCost.getByteSize() - << ", OpNum: " << OptCost.getInstrCount() - << ", Gas: " << OptCost.getGas() << "]\n"; + dbgs() << " Optimal transformation:\n" + << " [size: " << OptCost.getByteSize() + << ", instr count: " << OptCost.getInstrCount() + << ", gas: " << OptCost.getGas() << "]\n"; }); if (OptCost.getInstrCount() == 1) { - LLVM_DEBUG({ dbgs() << " Skipping identity transformation\n"; }); + LLVM_DEBUG({ dbgs() << " Skipping identity transformation\n"; }); return false; } - bool Changed = false; - if (OptForSize) { + if (isProfitableToTranform(Imm, OptCost, OptForSize)) { OptTransformation->apply(MI); - Changed = true; - } else { - unsigned OrigSize = (alignTo(Imm.getActiveBits(), 8) / 8) + 1; - // When optimizing for speed, only unfold constants if it reduces the size - // by a factor of at least the 'SizeGainThreshold' and keeps the instruction - // count to 'InstrNumLimitUnfolInto' or fewer. - if ((static_cast(OrigSize) / - static_cast(OptCost.getByteSize()) > - SizeGainThreshold) && - OptCost.getInstrCount() <= InstrNumLimitUnfolInto) { - OptTransformation->apply(MI); - Changed = true; - } else { - LLVM_DEBUG({ - dbgs() << " Unfolding is omitted as its effect does not meet the" - << "required gain threshold\n"; - }); + MI.eraseFromParent(); + assert(OrigSize > OptCost.getByteSize()); + reduceCodeSizeOn(OrigSize - OptCost.getByteSize()); + return true; + } + + LLVM_DEBUG({ + dbgs() << " Transformation is omitted as its effect does not meet the" + << " required reduction threshold\n"; + }); + + return false; +} + +static bool shouldSkip(const MachineInstr &MI) { + if (!EVMInstrInfo::isPush(&MI)) + return true; + + // It’s not practical to check small constants, since the default + // instructions are cheapest in those cases. The limit is based on the + // fact that the most compact transformation for representing a + // constant requires at least 5 bytes. + switch (MI.getOpcode()) { + case EVM::PUSH0_S: + case EVM::PUSH1_S: + case EVM::PUSH2_S: + case EVM::PUSH3_S: + case EVM::PUSH4_S: { + return true; + } + default: + return false; + } +} + +// This helper class organizes a MF’s instructions by their loop nesting depth, +// as reported by MachineLoopInfo. It builds a SmallVector of buckets, where +// each bucket contains the MIs for a specific loop depth (0 = out of loop, +// 1 - top level loop, etc.). +class LoopDepthInstrCache { +public: + LoopDepthInstrCache(MachineFunction &MF, const MachineLoopInfo &MLI) { + unsigned MaxLoopDepth = 0; + for (auto *ML : MLI.getLoopsInPreorder()) + MaxLoopDepth = std::max(MaxLoopDepth, ML->getLoopDepth()); + + InstrsPerLoopDepth.resize(MaxLoopDepth + 1); + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (shouldSkip(MI)) + continue; + + unsigned Depth = MLI.getLoopDepth(&MBB); + assert(Depth < InstrsPerLoopDepth.size()); + InstrsPerLoopDepth[Depth].push_back(&MI); + } } + + LLVM_DEBUG({ + dbgs() << "Instructions in function: " << MF.getName() << '\n'; + for (unsigned Depth = 0; Depth < InstrsPerLoopDepth.size(); ++Depth) { + dbgs() << " loop depth: " << Depth << '\n'; + for (const MachineInstr *MI : InstrsPerLoopDepth[Depth]) + dbgs() << " " << *MI << '\n'; + } + }); } - if (Changed) { - MI.eraseFromParent(); - ++NumOfUnfoldings; + LoopDepthInstrCache(const LoopDepthInstrCache &) = delete; + LoopDepthInstrCache &operator=(const LoopDepthInstrCache &) = delete; + LoopDepthInstrCache(LoopDepthInstrCache &&) noexcept = default; + LoopDepthInstrCache &operator=(LoopDepthInstrCache &&) noexcept = default; + ~LoopDepthInstrCache() = default; + + const InstrsPerLoopDepthTy &getInstructionsPerLoopDepth() const { + return InstrsPerLoopDepth; + } + + SmallVector getAllInstructions() const { + SmallVector Instrs; + for (const SmallVector &Bucket : InstrsPerLoopDepth) + Instrs.append(Bucket.begin(), Bucket.end()); + + return Instrs; + } + + DenseSet &getVisited() { return Visited; } + +private: + InstrsPerLoopDepthTy InstrsPerLoopDepth; + DenseSet Visited; +}; + +static bool processInstructions(ConstantUnfolder &Unfolder, + const SmallVector &Instrs, + DenseSet &Visited, + bool OptForSize, const TargetInstrInfo *TII) { + bool Changed = false; + for (MachineInstr *MI : Instrs) { + if (Visited.count(MI)) + continue; + + LLVM_DEBUG({ dbgs() << " Checking " << *MI; }); + + if (Unfolder.tryToUnfoldConstant(*MI, OptForSize, TII)) { + Visited.insert(MI); + Changed = true; + } } return Changed; } -bool EVMConstantUnfolding::runOnMachineFunction(MachineFunction &Mf) { - MF = &Mf; +static bool runImpl(Module &M, MachineModuleInfo &MMI) { bool Changed = false; + ConstantUnfolder Unfolder(&M.getContext()); + + const unsigned ModuleCodeSize = EVM::calculateModuleCodeSize(M, MMI); + + // Collect PUSH instructions to process. + DenseMap> + InstrCacheMap; + + for (Function &F : M) { + MachineFunction *MF = MMI.getMachineFunction(F); + if (!MF) + continue; + + // Compute MachineLoopInfo on the fly, as it's not available on the + // Module pass level. + auto OwnedMDT = std::make_unique(); + OwnedMDT->getBase().recalculate(*MF); + MachineDominatorTree *MDT = OwnedMDT.get(); + + auto OwnedMLI = std::make_unique(); + OwnedMLI->analyze(MDT->getBase()); + const MachineLoopInfo *MLI = OwnedMLI.get(); + + [[maybe_unused]] auto It = InstrCacheMap.try_emplace( + MF, std::make_unique(*MF, *MLI)); + assert(It.second); + } + LLVM_DEBUG({ - dbgs() << "\n********** Constant unfolding **********\n" - << "********** Function: " << Mf.getName() << '\n'; + dbgs() << "*** Running constant unfolding in the default mode ***\n"; + dbgs() << "*** Initial module size: " << ModuleCodeSize << " ***\n"; }); - TII = MF->getSubtarget().getInstrInfo(); - MachineLoopInfo *MLI = &getAnalysis().getLI(); - for (MachineBasicBlock &MBB : *MF) { - unsigned LoopDepth = MLI->getLoopDepth(&MBB); - if (LoopDepth > LoopDepthLimit) { - LLVM_DEBUG({ - dbgs() << "Skipping block " << MBB.getName() - << " due to exceeding the loop depth limit: " << LoopDepthLimit - << '\n'; - }); - continue; - } + // First, process all PUSH instructions in the default mode, selecting + // unfolding heuristics based on whether the OptSize flag is set for + // the MachineFunction. + for (auto &[MF, Cache] : InstrCacheMap) { + LLVM_DEBUG({ dbgs() << " Checking function: " << MF->getName() << '\n'; }); - for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) { - if (!EVMInstrInfo::isPush(&MI)) - continue; + bool OptForSize = MF->getFunction().hasOptSize(); + const EVMInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + Changed |= processInstructions(Unfolder, Cache->getAllInstructions(), + Cache->getVisited(), OptForSize, TII); + } + + unsigned CodeSizeReduction = Unfolder.getCodeSizeReduction(); + if (ModuleCodeSize < CodeSizeLimit + CodeSizeReduction) { + StatCodeSizeReduction = CodeSizeReduction; + return Changed; + } - LLVM_DEBUG({ dbgs() << "Checking " << MI; }); - - // It’s not practical to check small constants, since the default - // instructions are cheapest in those cases. The limit is based on the - // fact that the most compact transformation for representing a - // constant is SmallValueAndLeftShift which requires at least 5 bytes. - switch (MI.getOpcode()) { - case EVM::PUSH0_S: - case EVM::PUSH1_S: - case EVM::PUSH2_S: - case EVM::PUSH3_S: - case EVM::PUSH4_S: { - LLVM_DEBUG({ dbgs() << " Skipped due to bit-wise small constant\n"; }); + LLVM_DEBUG({ + dbgs() << "*** Current module size is " + << ModuleCodeSize - CodeSizeReduction + << ", which still exceeds the limit, falling back to " + "size-minimization mode ***\n"; + }); + + // First, process all PUSH instructions in the default mode, selecting + // unfolding heuristics based on whether the OptSize flag is set for + // the MachineFunction. + for (unsigned LoopDepth = 0; LoopDepth <= LoopDepthLimit; ++LoopDepth) { + LLVM_DEBUG({ + dbgs() << "*** Running constant unfolding in " + "size-minimization mode at loop depth " + << LoopDepth << " ***\n"; + }); + + for (auto &[MF, Cache] : InstrCacheMap) { + LLVM_DEBUG( + { dbgs() << " Checking function: " << MF->getName() << '\n'; }); + + const InstrsPerLoopDepthTy &InstrsPerLoopDepth = + Cache->getInstructionsPerLoopDepth(); + if (LoopDepth >= InstrsPerLoopDepth.size()) continue; - } - default: - break; - } - Changed |= tryUnfoldConstant(MI); + const EVMInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + Changed |= + processInstructions(Unfolder, InstrsPerLoopDepth[LoopDepth], + Cache->getVisited(), /*OptForSize=*/true, TII); + + CodeSizeReduction = Unfolder.getCodeSizeReduction(); + if (ModuleCodeSize < CodeSizeLimit + CodeSizeReduction) { + StatCodeSizeReduction = CodeSizeReduction; + return Changed; + } } + + LLVM_DEBUG({ + dbgs() << "*** Current module size is " + << ModuleCodeSize - CodeSizeReduction << " ***\n"; + }); } - // Clear the transformation cache, since in some cases, EVMConstantUnfolding - // is not destroyed after the run, and can be reused for other functions. In - // that case, we don't want to reuse the cache from the previous runs. - TransformationCache.clear(); return Changed; } + +bool EVMConstantUnfolding::runOnModule(Module &M) { + LLVM_DEBUG({ dbgs() << "********** " << PASS_NAME << " **********\n"; }); + + return runImpl(M, getAnalysis().getMMI()); +} diff --git a/llvm/lib/Target/EVM/EVMInstrFormats.td b/llvm/lib/Target/EVM/EVMInstrFormats.td index 2442cafa6815..ac0cf9fd67cf 100644 --- a/llvm/lib/Target/EVM/EVMInstrFormats.td +++ b/llvm/lib/Target/EVM/EVMInstrFormats.td @@ -25,8 +25,8 @@ def AS_storage : ASList<[AS.STORAGE]>; def AS_tstorage : ASList<[AS.TSTORAGE]>; // EVM general instruction format. -class EVMInst - : StackRel, RegisterRel, Instruction { +class EVMInst cost> + : StackRel, RegisterRel, Instruction { // Instruction encoding. Bitwidth corresponds to the maximum // size of possible EVM insturction. // This is 'PUSH32 Imm'. 8 bits for opcode, and 256 bits for @@ -36,32 +36,31 @@ class EVMInst bit IsPush = 0; bit StackBased = stack; string BaseName = NAME; - int GasCost = cost; + bits<16> BaseGasCost = cost; let Namespace = "EVM"; let Pattern = []; let AsmString = asmstr; let TSFlags{0} = stack; let TSFlags{1} = IsPush; + let TSFlags{17 -2} = BaseGasCost; } // Normal instructions. Default instantiation of a EVMInst. -class NI pattern, bit stack, - string asmstr = "", bits<8> inst = 0, int cost = 0> - : EVMInst { +class NI pattern, bit stack, string asmstr = "", + bits<8> inst = 0, bits<16> cost = 0> : EVMInst { dag OutOperandList = oops; dag InOperandList = iops; let Pattern = pattern; let Size = 1; let Opc = inst; - let Inst{7-0} = Opc; - let GasCost = cost; + let Inst{7 -0} = Opc; let Defs = !if(stack, [], [ARGUMENTS]); } // Generates both register and stack based versions of one actual instruction. -multiclass I pattern_r, - string opcstr, string argstr_r, bits<8> inst, - int cost = 0, dag oops_s = (outs), dag iops_s = (ins), string argstr_s = ""> { +multiclass I pattern_r, string opcstr, + string argstr_r, bits<8> inst, bits<16> cost, dag oops_s = (outs), + dag iops_s = (ins), string argstr_s = ""> { let isCodeGenOnly = 1 in def "" : NI; let BaseName = NAME, Defs = [] in def _S @@ -83,7 +82,7 @@ class EVMPseudo pattern, bit stack = 0> } class PushBase pattern, bit stack, - string asmstr = "", bits<8> inst = 0, int cost = 0> + string asmstr = "", bits<8> inst, bits<16> cost> : NI { let IsPush = 1; } diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.h b/llvm/lib/Target/EVM/EVMInstrInfo.h index 1ad5f1d830de..5b0699529bfa 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.h +++ b/llvm/lib/Target/EVM/EVMInstrInfo.h @@ -31,6 +31,9 @@ enum { // TSF flag to check if this is a PUSH instruction. IsPushPos = 1, IsPushMask = 0x1, + // Mask/offset representing instruction's base gas cost. + GasCostPos = 2, + GasCostMask = 0xFFFF, }; } // namespace EVMII @@ -84,17 +87,21 @@ class EVMInstrInfo final : public EVMGenInstrInfo { reverseBranchCondition(SmallVectorImpl &Cond) const override; /// TSFlags extraction - static unsigned getTSFlag(const MachineInstr *MI, unsigned Pos, - unsigned Mask) { - return (MI->getDesc().TSFlags >> Pos) & Mask; + static uint64_t getTSFlag(const MCInstrDesc &Desc, unsigned Pos, + uint64_t Mask) { + return (Desc.TSFlags >> Pos) & Mask; } static bool isStack(const MachineInstr *MI) { - return getTSFlag(MI, EVMII::IsStackPos, EVMII::IsStackMask); + return getTSFlag(MI->getDesc(), EVMII::IsStackPos, EVMII::IsStackMask); } static bool isPush(const MachineInstr *MI) { - return getTSFlag(MI, EVMII::IsPushPos, EVMII::IsPushMask); + return getTSFlag(MI->getDesc(), EVMII::IsPushPos, EVMII::IsPushMask); + } + + static uint64_t getGasCost(const MCInstrDesc &Desc) { + return getTSFlag(Desc, EVMII::GasCostPos, EVMII::GasCostMask); } }; diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index c14315f4d227..45727d4b7e2f 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -834,8 +834,6 @@ foreach I = {1-16} in { "SWAP"#I, "", !add(I, 0x8F), 3>; } -defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; - def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], true, "", 0, 0> { let isCodeGenOnly = 1; // PUSH4_S. @@ -847,6 +845,10 @@ def PUSH_FRAME : NI<(outs), (ins i256imm:$imm), [], true, "", 0, 0> { } // Define register PUSH* instructions + +let IsPush = 1 in +defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>; + foreach I = {1 - 32} in { def PUSH#I : PushBase<(outs), (ins i256imm:$imm), [], false, "PUSH"#I#" $imm", !add(I, 0x5F), 3> { diff --git a/llvm/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll index 4feadf7c2075..caad4552138b 100644 --- a/llvm/test/CodeGen/EVM/O3-pipeline.ll +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -145,9 +145,8 @@ target triple = "evm" ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: Stack Frame Layout Analysis ; CHECK-NEXT: EVM Lower jump_unless -; CHECK-NEXT: MachineDominator Tree Construction -; CHECK-NEXT: Machine Natural Loop Construction -; CHECK-NEXT: EVM constant unfolding +; CHECK-NEXT: EVM constant unfolding +; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: EVM Assembly diff --git a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll index 340ddd0fb98c..e5156c87e18e 100644 --- a/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll +++ b/llvm/test/CodeGen/EVM/bitmanipulation-intrinsics.ll @@ -98,7 +98,9 @@ define i256 @bitreversetest(i256 %v) { ; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH16 0xFF000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP6 ; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHR @@ -363,7 +365,9 @@ define i256 @bswaptest(i256 %v) { ; CHECK-NEXT: PUSH1 0x18 ; CHECK-NEXT: SHR ; CHECK-NEXT: AND -; CHECK-NEXT: PUSH16 0xFF000000000000000000000000000000 +; CHECK-NEXT: PUSH1 0xFF +; CHECK-NEXT: PUSH1 0x78 +; CHECK-NEXT: SHL ; CHECK-NEXT: DUP6 ; CHECK-NEXT: PUSH1 0x8 ; CHECK-NEXT: SHR diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll b/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll new file mode 100644 index 000000000000..e71dd8822f8f --- /dev/null +++ b/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll @@ -0,0 +1,78 @@ +; RUN: llc -O3 --debug-only=evm-constant-unfolding -evm-bytecode-sizelimit=80 < %s 2>&1 | FileCheck %s +; REQUIRES: asserts +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK: *** Running constant unfolding in the default mode *** +; CHECK-NEXT: *** Initial module size: 136 *** +; CHECK: Checking PUSH32_S i256 35408467139433450592217433187231851964531694900788300625387963629091585785856 +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216134069776894924259991723442175 +; CHECK: Skipping identity transformation +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216132831836855638879716824317951 +; CHECK: Skipping identity transformation + +; CHECK: *** Current module size is 111, which still exceeds the limit, falling back to size-minimization mode *** +; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 0 *** + +; CHECK: *** Current module size is 111 *** +; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 1 *** +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216134069776894924259991723442175 + +; CHECK: *** Current module size is 103 *** +; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 2 *** +; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216132831836855638879716824317951 + +; CHECK: *** Current module size is 95 *** + +define i256 @test(i256 %p) { + +; CHECK-LABEL: .BB0_2: +; CHECK: PUSH12 0x520000000000000000000000 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHR + +; CHECK-LABEL: .BB0_4: +; CHECK: PUSH4 0x4E487B71 +; CHECK-NEXT: PUSH1 0xE0 +; CHECK-NEXT: SHL + +; CHECK-LABEL: .BB0_6: +; CHECK: PUSH12 0x560000000000000000000000 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHL +; CHECK-NEXT: PUSH1 0x29 +; CHECK-NEXT: SHR + +entry: + %p.cmp = icmp eq i256 %p, 35 + br i1 %p.cmp, label %exit, label %outer.cond + +outer.cond: + %i = phi i256 [0, %entry], [%i.next, %outer.inc] + %i.cmp = icmp slt i256 %i, 52656145834278593348959013841835216134069776894924259991723442176 + br i1 %i.cmp, label %inner.cond, label %exit + +inner.cond: + %j = phi i256 [0, %outer.cond], [%j.next, %inner.inc] + %j.cmp = icmp slt i256 %j, 52656145834278593348959013841835216132831836855638879716824317952 + br i1 %j.cmp, label %inner.body, label %outer.inc + +inner.body: + br label %inner.inc + +inner.inc: + %j.next = add i256 %j, 1 + br label %inner.cond + +outer.inc: + %i.next = add i256 %i, 1 + br label %outer.cond + +exit: + ret i256 35408467139433450592217433187231851964531694900788300625387963629091585785856 + +} diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll b/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll index 17de50bb1b8b..b2d9d3468a7b 100644 --- a/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll +++ b/llvm/test/CodeGen/EVM/constant-unfolding-Oz.ll @@ -42,12 +42,9 @@ define void @test3() #1 { ; CHECK-LABEL: test3: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 0xD0 +; CHECK-NEXT: PUSH6 0xFFFFFFFFFFFF +; CHECK-NEXT: PUSH1 0xB0 ; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 0x20 -; CHECK-NEXT: SHR ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: @@ -60,12 +57,9 @@ define void @test4() #1 { ; CHECK-LABEL: test4: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 0xC8 +; CHECK-NEXT: PUSH7 0xFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH1 0xA8 ; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 0x20 -; CHECK-NEXT: SHR ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: @@ -172,12 +166,7 @@ define void @test13() #1 { ; CHECK-LABEL: test13: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 0xC5 -; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 0xC0 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH8 0xFFFFFFFFFFFFFFE0 ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: @@ -190,12 +179,7 @@ define void @test14() #1 { ; CHECK-LABEL: test14: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 0xB5 -; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 0xB0 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH10 0xFFFFFFFFFFFFFFFFFFE0 ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: @@ -208,12 +192,7 @@ define void @test15() #1 { ; CHECK-LABEL: test15: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH5 0x5000000000 -; CHECK-NEXT: NOT -; CHECK-NEXT: PUSH1 0x98 -; CHECK-NEXT: SHL -; CHECK-NEXT: PUSH1 0x98 -; CHECK-NEXT: SHR +; CHECK-NEXT: PUSH13 0xFFFFFFFFFFFFFFFFAFFFFFFFFF ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: diff --git a/llvm/test/CodeGen/EVM/constant-unfolding.ll b/llvm/test/CodeGen/EVM/constant-unfolding.ll index 6e91e612ed66..4c014ffc92e1 100644 --- a/llvm/test/CodeGen/EVM/constant-unfolding.ll +++ b/llvm/test/CodeGen/EVM/constant-unfolding.ll @@ -96,4 +96,17 @@ entry: unreachable } +; 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +define void @test7() #1 { +; CHECK-LABEL: test7: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: RETURN +entry: + tail call void @llvm.evm.return(ptr addrspace(1) null, i256 1461501637330902918203684832716283019655932542975) + unreachable +} + attributes #1 = { nofree nounwind noreturn } diff --git a/llvm/test/CodeGen/EVM/force-constant-unfolding.ll b/llvm/test/CodeGen/EVM/force-constant-unfolding.ll index 27148b4efe0d..4ba3f7817b9e 100644 --- a/llvm/test/CodeGen/EVM/force-constant-unfolding.ll +++ b/llvm/test/CodeGen/EVM/force-constant-unfolding.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 -; RUN: llc -O3 --evm-const-unfolding-size-gain-threshold=0 --evm-const-unfolding-inst-num-limit=10 < %s | FileCheck %s +; RUN: llc -O3 --evm-const-unfolding-size-reduction-threshold=0 --evm-const-unfolding-inst-num-limit=10 < %s | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" From a381428b1352e773f68239edd2b391477c0318f7 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 15 Aug 2025 16:50:49 +0200 Subject: [PATCH 177/203] [EVM] Refine intrinsic memory effects for improved alias analysis accuracy EVM opcodes can be classified based on how their behavior or output depends on the transaction state: - Readnone (Pure) - Volatile (State-Dependent) - Side-Effecting (State-Changing) (Reference: EVM opcodes categorization) This patch adjusts the memory attributes of LLVM intrinsics corresponding to these opcodes. At the LLVM IR level, the transaction-scoped EVM state is modeled as reads/writes to inaccessible memory. This state does not include the heap, which is modeled separately via regular LLVM pointer parameters. State-dependent intrinsics are now marked as reading from inaccessible memory. State-changing intrinsics are marked as both reading from and writing to it. To capture memory dependencies between plain loads/stores to storage (or transient storage) and context (CALL* or CREATE* like) intrinsics, we extended EVM alias analysis to determine aliasing between the call and the memory location in a custom way. --- llvm/include/llvm/IR/IntrinsicsEVM.td | 497 +++++++++++++---------- llvm/lib/Analysis/MemoryLocation.cpp | 19 +- llvm/lib/Analysis/VMAliasAnalysis.cpp | 5 + llvm/lib/Target/EVM/EVMAliasAnalysis.cpp | 116 +++++- llvm/lib/Target/EVM/EVMAliasAnalysis.h | 29 +- llvm/test/CodeGen/EVM/aa-eval.ll | 217 +++++++++- llvm/test/CodeGen/EVM/aa-memory-opt.ll | 224 +++++++++- llvm/test/CodeGen/EVM/cse-intrinsic.ll | 108 +++-- 8 files changed, 922 insertions(+), 293 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index af750b3679a2..3a29f8c076b9 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -23,85 +23,85 @@ def AS { let TargetPrefix = "evm" in { -// Ethereum Virtual Machine (EVM) opcodes and corresponding intrinsics can be grouped -// by the runtime effects i.e., how their behavior or outputs are affected by state -// changes during a transaction: +// Ethereum Virtual Machine (EVM) opcodes and corresponding intrinsics can be +// grouped by the runtime effects i.e., how their behavior or outputs are +// affected by state changes during a transaction: // // - 'Readnone' -// These do not read or write any persistent world-state once execution begins. -// Their results depend only on provided inputs or fixed environment data -// (constant during the transaction). They have no lasting side effects and -// return the same result given the same inputs. +// These do not read or write any persistent world-state once execution +// begins. Their results depend only on provided inputs or fixed +// environment data (constant during the transaction). They have no +// lasting side effects and return the same result given the same inputs. // // - 'Volatile (State-Dependent)' -// These read data from the world state or execution environment that may change -// within the same transaction (for example, due to prior storage writes, value -// transfers, or external calls). Re-invoking them after a state mutation can -// yield a different result. They themselves do not permanently modify state. -// Such intrinsics have 'IntrInaccessibleMemOnly' attribute, or -// 'IntrReadMem, IntrInaccessibleMemOnly' attributes to model dependency from -// the State. -// NOTE: we assume (to be confirmed) that the heap is not part of the State; -// therefore, it cannot be modified in a volatile manner. +// These read data from the world state or execution environment that may +// change within the same transaction (for example, due to prior storage +// writes, value transfers, or external calls). Re-invoking them after a +// state mutation can yield a different result. They themselves do not +// permanently modify state. Such intrinsics have +// 'IntrInaccessibleMemOnly' attribute, or 'IntrReadMem, +// IntrInaccessibleMemOnly' attributes to model dependency from the State. +// NOTE: we assume (to be confirmed) that the heap is not part of the +// State; therefore, it cannot be modified in a volatile manner. // // - 'Side-Effecting (State-Changing)' -// These opcodes cause persistent changes in world state - writing to storage, -// emitting logs, creating or destroying contracts, or transferring Ether. -// Their execution produces lasting side effects recorded on-chain (storage -// updates, logs, new accounts, etc.). -// Such intrinsics have empty memory attributes list which means they can -// arbitrary change the State. - -// TODO: Further improvements of intrinsics' attributes, #807 +// These opcodes cause persistent changes in world state - writing to +// storage, emitting logs, creating or destroying contracts, or +// transferring Ether. Their execution produces lasting side effects +// recorded on-chain (storage updates, logs, new accounts, etc.). Such +// intrinsics have no declared memory attributes, but their behavior is +// refined through EVM alias analysis.. // Arithmetical operations. -def int_evm_div - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_div : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_sdiv - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_sdiv : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_mod - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_mod : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_smod - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_smod : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_shl - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_shl : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_shr - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_shr : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_sar - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_sar : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; def int_evm_addmod - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], - [IntrNoMem, IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; def int_evm_mulmod - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], - [IntrNoMem, IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_exp - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; +def int_evm_exp : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; def int_evm_sha3 - : Intrinsic<[llvm_i256_ty], - [LLVMQualPointerType, llvm_i256_ty], - [IntrReadMem,IntrArgMemOnly, IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem, IntrArgMemOnly, IntrNoCallback, + NoCapture>, IntrWillReturn]>; def int_evm_signextend - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], [IntrNoMem, IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; -def int_evm_byte - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], - [IntrNoMem, IntrWillReturn]>; +def int_evm_byte : Intrinsic<[llvm_i256_ty], [llvm_i256_ty, llvm_i256_ty], + [IntrNoMem, IntrWillReturn]>; // Memory operations. -def int_evm_mstore8 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], - [IntrWriteMem, IntrArgMemOnly]>; +def int_evm_mstore8 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrWriteMem, IntrArgMemOnly, NoCapture>, + IntrNoCallback, IntrWillReturn]>; // Special getters that must not be relocated. def int_evm_msize : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; @@ -111,237 +111,286 @@ def int_evm_pc : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; // Getting values from the context. -def int_evm_address : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_address + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_origin : Intrinsic<[llvm_i256_ty], [], - [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_origin + : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; -def int_evm_caller : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_caller + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_balance : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], - [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; -def int_evm_callvalue : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_callvalue + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; -def int_evm_calldatasize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_calldatasize + : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; def int_evm_calldataload - : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], - [IntrReadMem, IntrArgMemOnly]>; + : Intrinsic<[llvm_i256_ty], [LLVMQualPointerType], + [IntrReadMem, IntrArgMemOnly, IntrNoCallback, + IntrWillReturn]>; -def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], [IntrNoMem, IntrWillReturn]>; +def int_evm_codesize : Intrinsic<[llvm_i256_ty], [], + [IntrNoMem, IntrNoCallback, IntrWillReturn]>; -def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; +def int_evm_gasprice : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; -def int_evm_extcodesize - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_extcodesize : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; -// This should have 'read of inaccessible' memory along with read/write memory accessed through -// its pointer-typed arguments. Since we can't express that precisely here, we represent -// it as read/write access to arbitrary memory. +// TODO: #878. This intrinsic’s declaration implies that it reads/writes to +// inaccessible memory (a limitation of the TableGen declaration format). +// Actually, it only reads from inaccessible memory. Its memory properties +// can be refined in the custom implementation of EVM AA. def int_evm_extcodecopy - : Intrinsic<[], [llvm_i256_ty, LLVMQualPointerType, - LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; + : Intrinsic<[], + [llvm_i256_ty, LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, WriteOnly>, + ReadOnly>, NoCapture>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; -def int_evm_returndatasize - : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_returndatasize : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; -def int_evm_extcodehash - : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; +def int_evm_extcodehash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; def int_evm_blockhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], - [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; def int_evm_blobhash : Intrinsic<[llvm_i256_ty], [llvm_i256_ty], - [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; - -def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_number : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_selfbalance - : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, IntrWillReturn]>; - -def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], [IntrReadMem, IntrInaccessibleMemOnly, - IntrWillReturn]>; - -// Logging. -// Log intrinsics should have 'read/write of inaccessible' memory along with read memory -// accessed through its pointer-typed arguments. Since we can't express that precisely -// here, we represent it as read/write access to arbitrary memory. -// Note: Attempting to mark them with both IntrReadMem and IntrHasSideEffects won't work, -// because IntrHasSideEffects does not override IntrReadMem. As a result, intrinsics will -// lack the 'HasSideEffects' flag entirely, making them subject to DCE. -def int_evm_log0 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], - [IntrWillReturn]>; - -def int_evm_log1 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], - [IntrWillReturn]>; - -def int_evm_log2 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty], - [IntrWillReturn]>; - -def int_evm_log3 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], - [IntrWillReturn]>; - -def int_evm_log4 : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, - llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], - [IntrWillReturn]>; - -// System calls -// The above considerations also apply to xCALL intrinsics. + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_coinbase : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_timestamp : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_number : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_difficulty : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_gaslimit : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_chainid : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_selfbalance : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_basefee : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_blobbasefee : Intrinsic<[llvm_i256_ty], [], + [IntrReadMem, IntrInaccessibleMemOnly, + IntrNoCallback, IntrWillReturn]>; + +// Logging +def int_evm_log0 + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log1 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log2 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log3 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +def int_evm_log4 + : Intrinsic<[], + [LLVMQualPointerType, llvm_i256_ty, llvm_i256_ty, + llvm_i256_ty, llvm_i256_ty, llvm_i256_ty], + [IntrInaccessibleMemOrArgMemOnly, ReadOnly>, + NoCapture>, IntrNoCallback, IntrWillReturn]>; + +// System calls. +// The following intrinsics do not explicitly specify memory attributes, +// which by default implies arbitrary memory effects. Their actual +// (refined) memory effects are determined in EVMAAResult::getModRefInfo(). +// Here, we only indicate which pointer arguments read from or write to +// heap memory, simplifying the implementation of +// EVMAAResult::getArgModRefInfo(). def int_evm_create - : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], - [IntrWillReturn]>; - -def int_evm_call - : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; + +def int_evm_call : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; def int_evm_callcode - : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty, + LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; def int_evm_delegatecall - : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty, - LLVMQualPointerType, llvm_i256_ty], [IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; def int_evm_create2 - : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, LLVMQualPointerType, - llvm_i256_ty, llvm_i256_ty], [IntrWillReturn]>; + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty, + llvm_i256_ty], + [ReadOnly>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; def int_evm_staticcall - : Intrinsic<[llvm_i256_ty], - [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, - llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], - [IntrWillReturn]>; - + : Intrinsic<[llvm_i256_ty], + [llvm_i256_ty, llvm_i256_ty, LLVMQualPointerType, + llvm_i256_ty, LLVMQualPointerType, llvm_i256_ty], + [ReadOnly>, WriteOnly>, + NoCapture>, NoCapture>, + IntrNoCallback, IntrWillReturn]>; -def int_evm_selfdestruct: Intrinsic<[], [llvm_i256_ty], []>; +def int_evm_selfdestruct : Intrinsic<[], [llvm_i256_ty], []>; -def int_evm_stop: Intrinsic<[], [], [IntrNoReturn]>; +def int_evm_stop : Intrinsic<[], [], [IntrNoReturn]>; -def int_evm_invalid: Intrinsic<[], [], [IntrNoReturn]>; +def int_evm_invalid : Intrinsic<[], [], [IntrNoReturn]>; // Return with error. -// These should ideally have the IntrReadMem attribute, but doing so causes DSE -// to incorrectly eliminate necessary stores. -def int_evm_return: Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], - [IntrNoReturn]>; - -def int_evm_revert: Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], - [IntrNoReturn]>; +// 'Return' and 'Revert' exhibit slightly different behaviors. +// In EVM AA, 'Return' is treated as aliasing with the Storage/Transient +// address spaces, whereas 'Revert' is not. This distinction allows the +// elimination of stores to Storage that occur before a 'Revert'. +def int_evm_return + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem, ReadOnly>, IntrNoReturn]>; + +def int_evm_revert + : Intrinsic<[], [LLVMQualPointerType, llvm_i256_ty], + [IntrReadMem, IntrArgMemOnly, ReadOnly>, + IntrNoReturn]>; def int_evm_memmoveas1as1 - : Intrinsic<[], - [LLVMQualPointerType, LLVMQualPointerType, - llvm_i256_ty, llvm_i1_ty], - [IntrArgMemOnly, IntrWillReturn, IntrNoFree, - IntrNoCallback, - NoCapture>, NoCapture>, - WriteOnly>, ReadOnly>, - ImmArg>]>; + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + WriteOnly>, ReadOnly>, + ImmArg>]>; def int_evm_memcpyas1as2 - : Intrinsic<[], - [LLVMQualPointerType, LLVMQualPointerType, - llvm_i256_ty, llvm_i1_ty], - [IntrArgMemOnly, IntrWillReturn, IntrNoFree, - IntrNoCallback, - NoCapture>, NoCapture>, - NoAlias>, NoAlias>, - WriteOnly>, ReadOnly>, - ImmArg>]>; + : Intrinsic<[], + [LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; def int_evm_memcpyas1as3 - : Intrinsic<[], - [LLVMQualPointerType, LLVMQualPointerType, - llvm_i256_ty, llvm_i1_ty], - [IntrArgMemOnly, IntrWillReturn, IntrNoFree, - IntrNoCallback, - NoCapture>, NoCapture>, - NoAlias>, NoAlias>, - WriteOnly>, ReadOnly>, - ImmArg>]>; + : Intrinsic<[], + [LLVMQualPointerType, + LLVMQualPointerType, llvm_i256_ty, + llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + WriteOnly>, ReadOnly>, + ImmArg>]>; def int_evm_memcpyas1as4 - : Intrinsic<[], - [LLVMQualPointerType, LLVMQualPointerType, - llvm_i256_ty, llvm_i1_ty], - [IntrArgMemOnly, IntrWillReturn, IntrNoFree, - IntrNoCallback, - NoCapture>, NoCapture>, - NoAlias>, NoAlias>, - WriteOnly>, ReadOnly>, - ImmArg>]>; + : Intrinsic<[], + [LLVMQualPointerType, LLVMQualPointerType, + llvm_i256_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback, + NoCapture>, NoCapture>, + NoAlias>, NoAlias>, + 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, IntrWillReturn] ->; +def int_evm_datasize + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; // 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, IntrWillReturn] ->; - +def int_evm_dataoffset + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; // Linking of libraries. // Inserts a library address placeholder to be replaced with // a library address at linkage time. -def int_evm_linkersymbol : DefaultAttrsIntrinsic< - [llvm_i256_ty], [llvm_metadata_ty], - [IntrNoMem, IntrWillReturn] ->; +def int_evm_linkersymbol + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; // The intrinsic call should not be optimized out, even though // its result is not used. For this, IntrInaccessibleMemOnly is // used. -def int_evm_pushdeployaddress : DefaultAttrsIntrinsic< - [llvm_i256_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn, - IntrNoDuplicate] ->; +def int_evm_pushdeployaddress + : DefaultAttrsIntrinsic<[llvm_i256_ty], [], + [IntrInaccessibleMemOnly, IntrWillReturn, + IntrNoDuplicate]>; // Immutables support. // Loads immutable value. Should be used only in runtime code. -def int_evm_loadimmutable : DefaultAttrsIntrinsic< - [llvm_i256_ty], [llvm_metadata_ty], - [IntrNoMem, IntrWillReturn] ->; +def int_evm_loadimmutable + : DefaultAttrsIntrinsic<[llvm_i256_ty], [llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; } // TargetPrefix = "evm" diff --git a/llvm/lib/Analysis/MemoryLocation.cpp b/llvm/lib/Analysis/MemoryLocation.cpp index b9cb22ffae2d..75a1786b1c65 100644 --- a/llvm/lib/Analysis/MemoryLocation.cpp +++ b/llvm/lib/Analysis/MemoryLocation.cpp @@ -176,8 +176,6 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, // location. auto T = Call->getModule()->getTargetTriple(); if (Triple(T).isEVM()) { - // For EVM intrinsics, the memory size argument always immediately - // follows the memory argument, meaning its index is ArgIdx + 1. auto GetMemLocation = [Call, Arg, &AATags](unsigned MemSizeArgIdx) { const auto *LenCI = dyn_cast(Call->getArgOperand(MemSizeArgIdx)); @@ -212,6 +210,23 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, assert((ArgIdx == 0) && "Invalid argument index for calldataload"); return MemoryLocation(Arg, LocationSize::precise(32), AATags); } + case Intrinsic::evm_revert: { + assert((ArgIdx == 0) && "Invalid argument index for revert"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_extcodecopy: { + assert((ArgIdx == 1 || ArgIdx == 2) && + "Invalid argument index for extcodecopy"); + return GetMemLocation(3); + } + case Intrinsic::evm_log0: + case Intrinsic::evm_log1: + case Intrinsic::evm_log2: + case Intrinsic::evm_log3: + case Intrinsic::evm_log4: { + assert((ArgIdx == 0) && "Invalid argument index for log"); + return GetMemLocation(ArgIdx + 1); + } default: llvm_unreachable("Unexpected intrinsic for EVM target"); break; diff --git a/llvm/lib/Analysis/VMAliasAnalysis.cpp b/llvm/lib/Analysis/VMAliasAnalysis.cpp index 510e64c72f66..7c512631049f 100644 --- a/llvm/lib/Analysis/VMAliasAnalysis.cpp +++ b/llvm/lib/Analysis/VMAliasAnalysis.cpp @@ -156,6 +156,11 @@ AliasResult VMAAResult::alias(const MemoryLocation &LocA, // If heap locations are the same, they either must or partially alias based // on the size of locations. if (StartAVal == StartBVal) { + // If either of the memory references is empty, it doesn't matter what the + // pointer values are. + if (LocA.Size.isZero() || LocB.Size.isZero()) + return AliasResult::NoAlias; + if (LocA.Size == LocB.Size) return AliasResult::MustAlias; return AliasResult::PartialAlias; diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp index 4170c96a6fe2..00f97ce613e9 100644 --- a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp @@ -12,6 +12,8 @@ #include "EVMAliasAnalysis.h" #include "EVM.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IntrinsicsEVM.h" #include "llvm/IR/Module.h" using namespace llvm; @@ -36,6 +38,108 @@ ImmutablePass *llvm::createEVMExternalAAWrapperPass() { return new EVMExternalAAWrapper(); } +EVMAAResult::EVMAAResult(const DataLayout &DL) + : VMAAResult(DL, {EVMAS::AS_STORAGE, EVMAS::AS_TSTORAGE}, {EVMAS::AS_HEAP}, + EVMAS::MAX_ADDRESS) {} + +ModRefInfo EVMAAResult::getArgModRefInfo(const CallBase *Call, + unsigned ArgIdx) { + if (Call->doesNotAccessMemory(ArgIdx)) + return ModRefInfo::NoModRef; + + if (Call->onlyWritesMemory(ArgIdx)) + return ModRefInfo::Mod; + + if (Call->onlyReadsMemory(ArgIdx)) + return ModRefInfo::Ref; + + return ModRefInfo::ModRef; +} + +static MemoryLocation getMemLocForArgument(const CallBase *Call, + unsigned ArgIdx) { + AAMDNodes AATags = Call->getAAMetadata(); + const Value *Arg = Call->getArgOperand(ArgIdx); + const auto *II = cast(Call); + + auto GetMemLocation = [Call, Arg, &AATags](unsigned MemSizeArgIdx) { + const auto *LenCI = + dyn_cast(Call->getArgOperand(MemSizeArgIdx)); + if (LenCI && LenCI->getValue().getActiveBits() <= 64) + return MemoryLocation(Arg, LocationSize::precise(LenCI->getZExtValue()), + AATags); + return MemoryLocation::getAfter(Arg, AATags); + }; + + switch (II->getIntrinsicID()) { + case Intrinsic::evm_return: { + assert((ArgIdx == 0) && "Invalid argument index for return"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_create: + case Intrinsic::evm_create2: { + assert((ArgIdx == 1) && "Invalid argument index for create/create2"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_call: + case Intrinsic::evm_callcode: { + assert((ArgIdx == 3 || ArgIdx == 5) && + "Invalid argument index for call/callcode"); + return GetMemLocation(ArgIdx + 1); + } + case Intrinsic::evm_delegatecall: + case Intrinsic::evm_staticcall: { + assert((ArgIdx == 2 || ArgIdx == 4) && + "Invalid argument index for delegatecall/staticcall"); + return GetMemLocation(ArgIdx + 1); + } + default: + llvm_unreachable("Unexpected intrinsic for EVM/EVM target"); + break; + } +} + +ModRefInfo EVMAAResult::getModRefInfo(const CallBase *Call, + const MemoryLocation &Loc, + AAQueryInfo &AAQI) { + const auto *II = dyn_cast(Call); + if (!II) + return AAResultBase::getModRefInfo(Call, Loc, AAQI); + + unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace(); + switch (II->getIntrinsicID()) { + case Intrinsic::evm_return: + if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) + return ModRefInfo::Ref; + break; + case Intrinsic::evm_create: + case Intrinsic::evm_create2: + case Intrinsic::evm_call: + case Intrinsic::evm_callcode: + case Intrinsic::evm_delegatecall: + case Intrinsic::evm_staticcall: + if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) + return ModRefInfo::ModRef; + break; + default: + return AAResultBase::getModRefInfo(Call, Loc, AAQI); + } + + ModRefInfo Result = ModRefInfo::NoModRef; + for (const auto &I : llvm::enumerate(Call->args())) { + const Value *Arg = I.value(); + if (!Arg->getType()->isPointerTy()) + continue; + unsigned ArgIdx = I.index(); + MemoryLocation ArgLoc = getMemLocForArgument(Call, ArgIdx); + AliasResult ArgAlias = VMAAResult::alias(ArgLoc, Loc, AAQI, Call); + if (ArgAlias != AliasResult::NoAlias) + Result |= getArgModRefInfo(Call, ArgIdx); + } + + return Result; +} + EVMAAWrapperPass::EVMAAWrapperPass() : ImmutablePass(ID) { initializeEVMAAWrapperPassPass(*PassRegistry::getPassRegistry()); } @@ -45,16 +149,10 @@ void EVMAAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { } bool EVMAAWrapperPass::doInitialization(Module &M) { - SmallDenseSet StorageAS = {EVMAS::AS_STORAGE, EVMAS::AS_TSTORAGE}; - SmallDenseSet HeapAS = {EVMAS::AS_HEAP}; - Result = std::make_unique(M.getDataLayout(), StorageAS, HeapAS, - EVMAS::MAX_ADDRESS); + Result = std::make_unique(M.getDataLayout()); return false; } -VMAAResult EVMAA::run(Function &F, AnalysisManager &AM) { - SmallDenseSet StorageAS = {EVMAS::AS_STORAGE, EVMAS::AS_TSTORAGE}; - SmallDenseSet HeapAS = {EVMAS::AS_HEAP}; - return VMAAResult(F.getParent()->getDataLayout(), StorageAS, HeapAS, - EVMAS::MAX_ADDRESS); +EVMAAResult EVMAA::run(Function &F, AnalysisManager &AM) { + return EVMAAResult(F.getParent()->getDataLayout()); } diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.h b/llvm/lib/Target/EVM/EVMAliasAnalysis.h index 1f65703037ad..a6f38db39c95 100644 --- a/llvm/lib/Target/EVM/EVMAliasAnalysis.h +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.h @@ -16,6 +16,24 @@ #include "llvm/Analysis/VMAliasAnalysis.h" namespace llvm { + +/// EVM-specific AA result. Note that we override certain non-virtual methods +/// from AAResultBase, as clarified in its documentation. +class EVMAAResult : public VMAAResult { +public: + explicit EVMAAResult(const DataLayout &DL); + + ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, + AAQueryInfo &AAQI); + + ModRefInfo getArgModRefInfo(const CallBase *Call, unsigned ArgIdx); + + ModRefInfo getModRefInfo(const CallBase *Call1, const CallBase *Call2, + AAQueryInfo &AAQI) { + return AAResultBase::getModRefInfo(Call1, Call2, AAQI); + } +}; + /// Analysis pass providing a never-invalidated alias analysis result. class EVMAA : public AnalysisInfoMixin { friend AnalysisInfoMixin; @@ -23,21 +41,20 @@ class EVMAA : public AnalysisInfoMixin { static AnalysisKey Key; public: - using Result = VMAAResult; - VMAAResult run(Function &F, AnalysisManager &AM); + using Result = EVMAAResult; + EVMAAResult run(Function &F, AnalysisManager &AM); }; -/// Legacy wrapper pass to provide the VMAAResult object. +/// Legacy wrapper pass to provide the EVMAAResult object. class EVMAAWrapperPass : public ImmutablePass { - std::unique_ptr Result; + std::unique_ptr Result; public: static char ID; EVMAAWrapperPass(); - VMAAResult &getResult() { return *Result; } - const VMAAResult &getResult() const { return *Result; } + EVMAAResult &getResult() { return *Result; } bool doFinalization(Module &M) override { Result.reset(); diff --git a/llvm/test/CodeGen/EVM/aa-eval.ll b/llvm/test/CodeGen/EVM/aa-eval.ll index 1479f7c6f883..c474a47c5b20 100644 --- a/llvm/test/CodeGen/EVM/aa-eval.ll +++ b/llvm/test/CodeGen/EVM/aa-eval.ll @@ -1,9 +1,19 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 -; RUN: opt -passes=aa-eval -aa-pipeline=basic-aa,evm-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s +; RUN: opt -passes=aa-eval -aa-pipeline=evm-aa,basic-aa -print-all-alias-modref-info -disable-output < %s 2>&1 | FileCheck %s target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" +declare void @llvm.evm.extcodecopy(i256, ptr addrspace(1), ptr addrspace(4), i256) +declare i256 @llvm.evm.create(i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.callcode(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.delegatecall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.create2(i256, ptr addrspace(1), i256, i256) +declare i256 @llvm.evm.staticcall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare void @llvm.evm.return(ptr addrspace(1), i256) +declare void @llvm.evm.revert(ptr addrspace(1), i256) + ; CHECK-LABEL: Function: test_offset_i8_noalias ; CHECK: NoAlias: i8 addrspace(1)* %inttoptr1, i8 addrspace(1)* %inttoptr2 define void @test_offset_i8_noalias(ptr addrspace(1) %addr) { @@ -256,3 +266,208 @@ define void @test_as6_mustalias() { store i256 1, ptr addrspace(6) %ptr2, align 64 ret void } + +; CHECK-LABEL: Function: test_as6_as5_alias_create +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create +define i256 @test_as6_as5_alias_create(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_create +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.create +define i256 @test_as1_alias_create(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 33) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_create +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create2 +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create2 +define i256 @test_as6_as5_alias_create2(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 1, i256 0) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_create2 +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.create2 +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.create2 +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.create2 +define i256 @test_as1_alias_create2(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 33, i256 0) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_call +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.call +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.call +define i256 @test_as6_as5_alias_call(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_call +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.call +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.call +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.call +define i256 @test_as1_alias_call(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_staticcall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.staticcall +define i256 @test_as6_as5_alias_staticcall(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_staticcall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.staticcall +define i256 @test_as1_alias_staticcall(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_callcode +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.callcode +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.callcode +define i256 @test_as6_as5_callcode(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_callcode +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.callcode +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.callcode +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.callcode +define i256 @test_as1_alias_callcode(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_delegatecall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.delegatecall +; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.delegatecall +define i256 @test_as6_as5_alias_delegatecall(ptr addrspace(5) %ptr1) { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + %ret = call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as1_alias_delegatecall +; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.delegatecall +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.delegatecall +; CHECK: NoModRef: Ptr: i256* %ptr3 <-> %ret = call i256 @llvm.evm.delegatecall +define i256 @test_as1_alias_delegatecall(ptr addrspace(1) %ptr1) { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + %ptr3 = inttoptr i256 64 to ptr addrspace(1) + store i256 3, ptr addrspace(1) %ptr3, align 32 + %ret = call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 33, ptr addrspace(1) null, i256 1) + ret i256 %ret +} + +; CHECK-LABEL: Function: test_as6_as5_alias_revert +; CHECK: NoModRef: Ptr: i256* %ptr1 <-> call void @llvm.evm.revert +; CHECK: NoModRef: Ptr: i256* %ptr2 <-> call void @llvm.evm.revert +define void @test_as6_as5_alias_revert(ptr addrspace(5) %ptr1) noreturn { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 0) + unreachable +} + +; CHECK-LABEL: Function: test_as1_alias_revert +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> call void @llvm.evm.revert +; CHECK: NoModRef: Ptr: i256* %ptr2 <-> call void @llvm.evm.revert +define void @test_as1_alias_revert(ptr addrspace(1) %ptr1) noreturn { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + call void @llvm.evm.revert(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: Function: test_as6_as5_alias_return +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> call void @llvm.evm.return +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> call void @llvm.evm.return +define void @test_as6_as5_alias_return(ptr addrspace(5) %ptr1) noreturn { + store i256 1, ptr addrspace(5) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(6) + store i256 2, ptr addrspace(6) %ptr2, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} + +; CHECK-LABEL: Function: test_as1_alias_return +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> call void @llvm.evm.return +; CHECK: NoModRef: Ptr: i256* %ptr2 <-> call void @llvm.evm.return +define void @test_as1_alias_return(ptr addrspace(1) %ptr1) noreturn { + store i256 1, ptr addrspace(1) %ptr1, align 32 + %ptr2 = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr2, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: Function: test_as1_alias_extcodecopy +; CHECK: Just Mod: Ptr: i256* null <-> call void @llvm.evm.extcodecopy +; CHECK: NoModRef: Ptr: i256* %ptr <-> call void @llvm.evm.extcodecopy +define void @test_as1_alias_extcodecopy() { + store i256 1, ptr addrspace(1) null, align 32 + %ptr = inttoptr i256 32 to ptr addrspace(1) + store i256 2, ptr addrspace(1) %ptr, align 32 + call void @llvm.evm.extcodecopy(i256 0, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + ret void +} diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll index b0245969041c..35a4a14822ce 100644 --- a/llvm/test/CodeGen/EVM/aa-memory-opt.ll +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -69,17 +69,88 @@ define i256 @test_gas() { ret i256 %ret } -define i256 @test_log0(ptr addrspace(1) %off, i256 %size) { -; CHECK-LABEL: define i256 @test_log0 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2]] { -; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 -; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 -; CHECK-NEXT: ret i256 [[RET]] +define i256 @test_log0() { +; CHECK-LABEL: define noundef i256 @test_log0 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) null, i256 32) +; CHECK-NEXT: ret i256 9 ; - store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 - call void @llvm.evm.log0(ptr addrspace(1) %off, i256 %size) - %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log0(ptr addrspace(1) null, i256 32) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log1() { +; CHECK-LABEL: define noundef i256 @test_log1 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) null, i256 32, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log1(ptr addrspace(1) null, i256 32, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log2() { +; CHECK-LABEL: define noundef i256 @test_log2 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) null, i256 32, i256 0, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log2(ptr addrspace(1) null, i256 32, i256 0, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log3() { +; CHECK-LABEL: define noundef i256 @test_log3 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log3(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 + ret i256 %ret +} + +define i256 @test_log4() { +; CHECK-LABEL: define noundef i256 @test_log4 +; CHECK-SAME: () local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 2, ptr addrspace(5) null, align 4294967296 +; CHECK-NEXT: store i256 7, ptr addrspace(1) null, align 4294967296 +; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0, i256 0) +; CHECK-NEXT: ret i256 9 +; + store i256 2, ptr addrspace(5) null, align 32 + store i256 7, ptr addrspace(1) null, align 32 + call void @llvm.evm.log4(ptr addrspace(1) null, i256 32, i256 0, i256 0, i256 0, i256 0) + %tmp = load i256, ptr addrspace(5) null, align 32 + %tmp2 = load i256, ptr addrspace(1) null, align 32 + %ret = add i256 %tmp, %tmp2 ret i256 %ret } @@ -214,7 +285,138 @@ define i256 @test_as6_large() { ret i256 %ret } +; Verify that in the following tests all load operations are preserved. + +define i256 @test_noopt_create(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_create +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.create(i256 0, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_create2(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_create2 +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 1, i256 0) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.create2(i256 0, ptr addrspace(1) null, i256 1, i256 0) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_call(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_call +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.call(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_staticcall(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_staticcall +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_callcode(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_callcode +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.callcode(i256 1, i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +define i256 @test_noopt_delegatecall(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define i256 @test_noopt_delegatecall +; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.delegatecall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + +declare i256 @llvm.evm.create(i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.call(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.callcode(i256, i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.delegatecall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) +declare i256 @llvm.evm.create2(i256, ptr addrspace(1), i256, i256) +declare i256 @llvm.evm.staticcall(i256, i256, ptr addrspace(1), i256, ptr addrspace(1), i256) declare void @llvm.memcpy.p1.p1.i256(ptr addrspace(1), ptr addrspace(1), i256, i1 immarg) declare i256 @llvm.evm.gas() declare void @llvm.evm.log0(ptr addrspace(1), i256) - +declare void @llvm.evm.log1(ptr addrspace(1), i256, i256) +declare void @llvm.evm.log2(ptr addrspace(1), i256, i256, i256) +declare void @llvm.evm.log3(ptr addrspace(1), i256, i256, i256, i256) +declare void @llvm.evm.log4(ptr addrspace(1), i256, i256, i256, i256, i256) diff --git a/llvm/test/CodeGen/EVM/cse-intrinsic.ll b/llvm/test/CodeGen/EVM/cse-intrinsic.ll index 7e0a933f9001..c2dd7efcdce8 100644 --- a/llvm/test/CodeGen/EVM/cse-intrinsic.ll +++ b/llvm/test/CodeGen/EVM/cse-intrinsic.ll @@ -225,7 +225,7 @@ define i256 @exp(i256 %rs1, i256 %rs2) nounwind { define i256 @sha3(ptr addrspace(1) %offset, i256 %size) nounwind { ; CHECK-LABEL: define i256 @sha3 -; CHECK-SAME: (ptr addrspace(1) [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFFSET:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.sha3(ptr addrspace(1) [[OFFSET]], i256 [[SIZE]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -345,7 +345,7 @@ define i256 @caller() nounwind { define i256 @balance(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @balance -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -359,7 +359,7 @@ define i256 @balance(i256 %rs1) nounwind { ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @balance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @balance_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.balance(i256 [[ADDR]]) ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.balance(i256 [[ADDR]]) @@ -379,7 +379,7 @@ define i256 @balance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %ro ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @origin_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @origin_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.origin() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.origin() @@ -399,7 +399,7 @@ define i256 @origin_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %rof ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @gasprice_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @gasprice_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.gasprice() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.gasprice() @@ -419,7 +419,7 @@ define i256 @gasprice_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %r ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @blockhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @blockhash_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[ADDR]]) ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[ADDR]]) @@ -439,7 +439,7 @@ define i256 @blockhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) % ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @blobhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @blobhash_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[ADDR]]) ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[ADDR]]) @@ -459,7 +459,7 @@ define i256 @blobhash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %r ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @coinbase_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @coinbase_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.coinbase() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.coinbase() @@ -479,7 +479,7 @@ define i256 @coinbase_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %r ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @timestamp_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @timestamp_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.timestamp() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.timestamp() @@ -499,7 +499,7 @@ define i256 @timestamp_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) % ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @number_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @number_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.number() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.number() @@ -519,7 +519,7 @@ define i256 @number_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %rof ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @difficulty_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @difficulty_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.difficulty() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.difficulty() @@ -539,7 +539,7 @@ define i256 @difficulty_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @gaslimit_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @gaslimit_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.gaslimit() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.gaslimit() @@ -559,7 +559,7 @@ define i256 @gaslimit_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %r ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @chainid_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @chainid_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.chainid() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.chainid() @@ -579,7 +579,7 @@ define i256 @chainid_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %ro ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @basefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @basefee_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.basefee() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.basefee() @@ -599,7 +599,7 @@ define i256 @basefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %ro ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @blobbasefee_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @blobbasefee_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.blobbasefee() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.blobbasefee() @@ -631,7 +631,7 @@ define i256 @calldatasize() nounwind { define i256 @calldataload(ptr addrspace(2) %rs1) nounwind { ; CHECK-LABEL: define i256 @calldataload -; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +; CHECK-SAME: (ptr addrspace(2) [[RS1:%.*]]) local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.calldataload(ptr addrspace(2) [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -657,7 +657,7 @@ define i256 @callvalue() nounwind { define i256 @codesize() nounwind { ; CHECK-LABEL: define i256 @codesize -; CHECK-SAME: () local_unnamed_addr #[[ATTR1]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR0]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.codesize() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -670,7 +670,7 @@ define i256 @codesize() nounwind { define i256 @gasprice() nounwind { ; CHECK-LABEL: define i256 @gasprice -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gasprice() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -683,7 +683,7 @@ define i256 @gasprice() nounwind { define i256 @extcodesize(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @extcodesize -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -697,7 +697,7 @@ define i256 @extcodesize(i256 %rs1) nounwind { ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @extcodesize_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @extcodesize_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[ADDR]]) ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodesize(i256 [[ADDR]]) @@ -716,7 +716,7 @@ define i256 @extcodesize_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) define i256 @extcodehash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @extcodehash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -730,7 +730,7 @@ define i256 @extcodehash(i256 %rs1) nounwind { ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @extcodehash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @extcodehash_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) @@ -747,9 +747,36 @@ define i256 @extcodehash_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) ret i256 %ret } +; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. +define i256 @extcodecopy_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { +; CHECK-LABEL: define i256 @extcodecopy_call +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { +; CHECK-NEXT: tail call void @llvm.evm.extcodecopy(i256 [[ADDR]], ptr addrspace(1) null, ptr addrspace(4) null, i256 32) +; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: tail call void @llvm.evm.extcodecopy(i256 [[ADDR]], ptr addrspace(1) null, ptr addrspace(4) null, i256 32) +; CHECK-NEXT: [[V2:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) +; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.extcodehash(i256 [[ADDR]]) +; CHECK-NEXT: [[TMP:%.*]] = add i256 [[V2]], [[V1]] +; CHECK-NEXT: [[RET:%.*]] = add i256 [[TMP]], [[V3]] +; CHECK-NEXT: tail call void @llvm.evm.extcodecopy(i256 [[ADDR]], ptr addrspace(1) null, ptr addrspace(4) null, i256 32) +; CHECK-NEXT: ret i256 [[RET]] +; + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + %v1 = call i256 @llvm.evm.extcodehash(i256 %addr) + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + %v2 = call i256 @llvm.evm.extcodehash(i256 %addr) + %unused = call i256 @llvm.evm.call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) %roff, i256 %rsize) + %v3 = call i256 @llvm.evm.extcodehash(i256 %addr) + %tmp = add i256 %v1, %v2 + %ret = add i256 %tmp, %v3 + call void @llvm.evm.extcodecopy(i256 %addr, ptr addrspace(1) null, ptr addrspace(4) null, i256 32) + ret i256 %ret +} + define i256 @returndatasize() nounwind { ; CHECK-LABEL: define i256 @returndatasize -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.returndatasize() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -762,7 +789,7 @@ define i256 @returndatasize() nounwind { define i256 @blockhash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @blockhash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blockhash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -775,7 +802,7 @@ define i256 @blockhash(i256 %rs1) nounwind { define i256 @blobhash(i256 %rs1) nounwind { ; CHECK-LABEL: define i256 @blobhash -; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: (i256 [[RS1:%.*]]) local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobhash(i256 [[RS1]]) ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -788,7 +815,7 @@ define i256 @blobhash(i256 %rs1) nounwind { define i256 @coinbase() nounwind { ; CHECK-LABEL: define i256 @coinbase -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.coinbase() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -801,7 +828,7 @@ define i256 @coinbase() nounwind { define i256 @timestamp() nounwind { ; CHECK-LABEL: define i256 @timestamp -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.timestamp() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -814,7 +841,7 @@ define i256 @timestamp() nounwind { define i256 @number() nounwind { ; CHECK-LABEL: define i256 @number -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.number() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -827,7 +854,7 @@ define i256 @number() nounwind { define i256 @difficulty() nounwind { ; CHECK-LABEL: define i256 @difficulty -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.difficulty() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -840,7 +867,7 @@ define i256 @difficulty() nounwind { define i256 @gaslimit() nounwind { ; CHECK-LABEL: define i256 @gaslimit -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.gaslimit() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -853,7 +880,7 @@ define i256 @gaslimit() nounwind { define i256 @chainid() nounwind { ; CHECK-LABEL: define i256 @chainid -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.chainid() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -866,7 +893,7 @@ define i256 @chainid() nounwind { define i256 @selfbalance() nounwind { ; CHECK-LABEL: define i256 @selfbalance -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.selfbalance() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -880,7 +907,7 @@ define i256 @selfbalance() nounwind { ; Check that the %v2 gets CSEed, but %v3 is not because of the @llvm.evm.call call. define i256 @selfbalance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) %roff, i256 %rsize) nounwind { ; CHECK-LABEL: define i256 @selfbalance_call -; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (i256 [[GAS:%.*]], i256 [[ADDR:%.*]], i256 [[VAL:%.*]], ptr addrspace(1) nocapture writeonly [[ROFF:%.*]], i256 [[RSIZE:%.*]]) local_unnamed_addr #[[ATTR6]] { ; CHECK-NEXT: [[V1:%.*]] = tail call i256 @llvm.evm.selfbalance() ; CHECK-NEXT: [[UNUSED:%.*]] = tail call i256 @llvm.evm.call(i256 [[GAS]], i256 [[ADDR]], i256 [[VAL]], ptr addrspace(1) nonnull inttoptr (i256 256 to ptr addrspace(1)), i256 32, ptr addrspace(1) [[ROFF]], i256 [[RSIZE]]) ; CHECK-NEXT: [[V3:%.*]] = tail call i256 @llvm.evm.selfbalance() @@ -899,7 +926,7 @@ define i256 @selfbalance_call(i256 %gas, i256 %addr, i256 %val, ptr addrspace(1) define i256 @basefee() nounwind { ; CHECK-LABEL: define i256 @basefee -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.basefee() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -912,7 +939,7 @@ define i256 @basefee() nounwind { define i256 @blobbasefee() nounwind { ; CHECK-LABEL: define i256 @blobbasefee -; CHECK-SAME: () local_unnamed_addr #[[ATTR4]] { +; CHECK-SAME: () local_unnamed_addr #[[ATTR5]] { ; CHECK-NEXT: [[RES1:%.*]] = tail call i256 @llvm.evm.blobbasefee() ; CHECK-NEXT: [[RET:%.*]] = shl i256 [[RES1]], 1 ; CHECK-NEXT: ret i256 [[RET]] @@ -925,7 +952,7 @@ define i256 @blobbasefee() nounwind { define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { ; CHECK-LABEL: define void @log0 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] { ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: tail call void @llvm.evm.log0(ptr addrspace(1) [[OFF]], i256 [[SIZE]]) ; CHECK-NEXT: ret void @@ -937,7 +964,7 @@ define void @log0(ptr addrspace(1) %off, i256 %size) nounwind { define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { ; CHECK-LABEL: define void @log1 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]]) local_unnamed_addr #[[ATTR7]] { ; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) ; CHECK-NEXT: tail call void @llvm.evm.log1(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]]) ; CHECK-NEXT: ret void @@ -949,7 +976,7 @@ define void @log1(ptr addrspace(1) %off, i256 %size, i256 %t1) nounwind { define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwind { ; CHECK-LABEL: define void @log2 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]]) local_unnamed_addr #[[ATTR7]] { ; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) ; CHECK-NEXT: tail call void @llvm.evm.log2(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]]) ; CHECK-NEXT: ret void @@ -961,7 +988,7 @@ define void @log2(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2) nounwin define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3) nounwind { ; CHECK-LABEL: define void @log3 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]]) local_unnamed_addr #[[ATTR7]] { ; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) ; CHECK-NEXT: tail call void @llvm.evm.log3(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]]) ; CHECK-NEXT: ret void @@ -973,7 +1000,7 @@ define void @log3(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t define void @log4(ptr addrspace(1) %off, i256 %size, i256 %t1, i256 %t2, i256 %t3, i256 %t4) nounwind { ; CHECK-LABEL: define void @log4 -; CHECK-SAME: (ptr addrspace(1) [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-SAME: (ptr addrspace(1) nocapture readonly [[OFF:%.*]], i256 [[SIZE:%.*]], i256 [[T1:%.*]], i256 [[T2:%.*]], i256 [[T3:%.*]], i256 [[T4:%.*]]) local_unnamed_addr #[[ATTR7]] { ; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) ; CHECK-NEXT: tail call void @llvm.evm.log4(ptr addrspace(1) [[OFF]], i256 [[SIZE]], i256 [[T1]], i256 [[T2]], i256 [[T3]], i256 [[T4]]) ; CHECK-NEXT: ret void @@ -1003,6 +1030,7 @@ declare i256 @llvm.evm.codesize() declare i256 @llvm.evm.gasprice() declare i256 @llvm.evm.extcodesize(i256) declare i256 @llvm.evm.extcodehash(i256) +declare void @llvm.evm.extcodecopy(i256, ptr addrspace(1), ptr addrspace(4), i256) declare i256 @llvm.evm.blockhash(i256) declare i256 @llvm.evm.blobhash(i256) declare i256 @llvm.evm.returndatasize() From 36bb5a53fcaa71d38b5347b6e77df34ac0bcc220 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Fri, 15 Aug 2025 16:55:11 +0200 Subject: [PATCH 178/203] [EVM] Extend DSE to remove dead store at the end of a function. This fixes #866. --- llvm/lib/Transforms/Scalar/CMakeLists.txt | 1 + .../Scalar/DeadStoreElimination.cpp | 48 +++++++++++- llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll | 78 +++++++++++++++++++ llvm/test/CodeGen/EVM/evm-dse.ll | 57 ++++++++++++++ 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll create mode 100644 llvm/test/CodeGen/EVM/evm-dse.ll diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index 43e0f4a43666..83ff552f114e 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -102,5 +102,6 @@ add_llvm_component_library(LLVMScalarOpts Core InstCombine Support + TargetParser # EVM local TransformUtils ) diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index 931606c6f8fe..7cccce072187 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -63,6 +63,9 @@ #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +// EVM local begin +#include "llvm/IR/IntrinsicsEVM.h" +// EVM local begin #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" @@ -73,6 +76,9 @@ #include "llvm/Support/DebugCounter.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +// EVM local begin +#include "llvm/TargetParser/Triple.h" +// EVM local end #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/BuildLibCalls.h" #include "llvm/Transforms/Utils/Local.h" @@ -1801,6 +1807,29 @@ struct DSEState { return false; } + /// Verify whether there is a path from the block containing the store to a + /// return block, i.e., a block that does not end with 'unreachable'. + bool isOnPathToFunctionReturn(const MemoryDef *MaybeDeadDef) { + const BasicBlock *BB = MaybeDeadDef->getBlock(); + // Handle the trivial case where the store resides in a block that ends + // with 'unreachable'. + if (isa(BB->getTerminator())) + return false; + + // Handle the general case by performing a depth-first traversal on the + // inverse control flow graph (CFG), starting from a return block and + // continuing until the block containing the store is reached. + for (const BasicBlock *R : PDT.roots()) { + if (isa(R->getTerminator())) + continue; + + for (auto I = idf_begin(R), E = idf_end(R); I != E; ++I) + if (*I == BB) + return true; + } + return false; + } + /// Eliminate writes to objects that are not visible in the caller and are not /// accessed before returning from the function. bool eliminateDeadWritesAtEndOfFunction() { @@ -1828,8 +1857,23 @@ struct DSEState { // underlying objects is very uncommon. If it turns out to be important, // we can use getUnderlyingObjects here instead. const Value *UO = getUnderlyingObject(DefLoc->Ptr); - if (!isInvisibleToCallerAfterRet(UO)) - continue; + if (!isInvisibleToCallerAfterRet(UO)) { + // EVM local begin + // For EVM verify that all paths originating from the basic block + // containing the store eventually lead to blocks that terminate with + // an 'unreachable' instruction. If, in addition, the store is not + // read before the function returns (as determined by + // isWriteAtEndOfFunction), then the store can be safely eliminated. + // TODO: Evaluate this transformation across all targets + // (i.e., without limiting it to EVM) to assess whether it could be + // upstreamed. At first glance, this appears to be EVM-specific, + // since in the general case it is not guaranteed that memory side + // effects preceding an unreachable can be safely ignored. + Triple TT(DefI->getFunction()->getParent()->getTargetTriple()); + if (!TT.isEVM() || isOnPathToFunctionReturn(Def)) + continue; + // EVM local end + } if (isWriteAtEndOfFunction(Def, *DefLoc)) { // See through pointer-to-pointer bitcasts diff --git a/llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll b/llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll new file mode 100644 index 000000000000..b847f178ebb5 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-dse-free-ptr.ll @@ -0,0 +1,78 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -aa-pipeline=default -passes=dse -S < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.callvalue() +declare i256 @llvm.evm.calldatasize() +declare void @llvm.evm.return(ptr addrspace(1) readonly, i256) +declare void @llvm.evm.revert(ptr addrspace(1) readonly, i256) + +; Check that DSE eliminates the initialization of unused free memory pointer: +; store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + +define void @remove_free_ptr() noreturn { +; CHECK-LABEL: define void @remove_free_ptr( +; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[TRY:.*:]] +; CHECK-NEXT: [[CALLVALUE_I:%.*]] = tail call i256 @llvm.evm.callvalue() +; CHECK-NEXT: [[COMPARISON_RESULT_I:%.*]] = icmp eq i256 [[CALLVALUE_I]], 0 +; CHECK-NEXT: br i1 [[COMPARISON_RESULT_I]], label %"block_rt_1/0.i", label %"block_rt_2/0.i" +; CHECK: "block_rt_1/0.i": +; CHECK-NEXT: [[CALLDATASIZE_I:%.*]] = tail call i256 @llvm.evm.calldatasize() +; CHECK-NEXT: [[COMPARISON_RESULT7_I:%.*]] = icmp ult i256 [[CALLDATASIZE_I]], 4 +; CHECK-NEXT: br i1 [[COMPARISON_RESULT7_I]], label %"block_rt_2/0.i", label %[[SHIFT_RIGHT_NON_OVERFLOW_I:.*]] +; CHECK: "block_rt_2/0.i": +; CHECK-NEXT: tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) +; CHECK-NEXT: unreachable +; CHECK: "block_rt_3/0.i": +; CHECK-NEXT: store i256 42, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 +; CHECK-NEXT: tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) +; CHECK-NEXT: unreachable +; CHECK: "block_rt_4/0.i": +; CHECK-NEXT: store i256 99, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 +; CHECK-NEXT: tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) +; CHECK-NEXT: unreachable +; CHECK: [[SHIFT_RIGHT_NON_OVERFLOW_I]]: +; CHECK-NEXT: [[CALLDATA_LOAD_RESULT_I:%.*]] = load i256, ptr addrspace(2) null, align 4294967296 +; CHECK-NEXT: [[SHIFT_RIGHT_NON_OVERFLOW_RESULT_I:%.*]] = lshr i256 [[CALLDATA_LOAD_RESULT_I]], 224 +; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i256 [[SHIFT_RIGHT_NON_OVERFLOW_RESULT_I]] to i32 +; CHECK-NEXT: switch i32 [[TRUNC]], label %"block_rt_2/0.i" [ +; CHECK-NEXT: i32 1039457780, label %"block_rt_3/0.i" +; CHECK-NEXT: i32 1519042605, label %"block_rt_4/0.i" +; CHECK-NEXT: ] +; +try: + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 64 + %callvalue.i = tail call i256 @llvm.evm.callvalue() + %comparison_result.i = icmp eq i256 %callvalue.i, 0 + br i1 %comparison_result.i, label %"block_rt_1/0.i", label %"block_rt_2/0.i" + +"block_rt_1/0.i": + %calldatasize.i = tail call i256 @llvm.evm.calldatasize() + %comparison_result7.i = icmp ult i256 %calldatasize.i, 4 + br i1 %comparison_result7.i, label %"block_rt_2/0.i", label %shift_right_non_overflow.i + +"block_rt_2/0.i": + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable + +"block_rt_3/0.i": + store i256 42, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) + unreachable + +"block_rt_4/0.i": + store i256 99, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 inttoptr (i256 128 to ptr addrspace(1)), i256 32) + unreachable + +shift_right_non_overflow.i: + %calldata_load_result.i = load i256, ptr addrspace(2) null, align 4294967296 + %shift_right_non_overflow_result.i = lshr i256 %calldata_load_result.i, 224 + %trunc = trunc nuw i256 %shift_right_non_overflow_result.i to i32 + switch i32 %trunc, label %"block_rt_2/0.i" [ + i32 1039457780, label %"block_rt_3/0.i" + i32 1519042605, label %"block_rt_4/0.i" + ] +} diff --git a/llvm/test/CodeGen/EVM/evm-dse.ll b/llvm/test/CodeGen/EVM/evm-dse.ll new file mode 100644 index 000000000000..71424aa76ce8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-dse.ll @@ -0,0 +1,57 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -aa-pipeline=default -passes=dse -S < %s | FileCheck %s +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +declare i256 @llvm.evm.calldatasize() +declare void @llvm.evm.return(ptr addrspace(1) readonly, i256) +declare void @llvm.evm.revert(ptr addrspace(1) readonly, i256) +declare void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(3) noalias nocapture readonly, i256, i1 immarg) + +; This test verifies that unused heap stores can be safely eliminated before revert or return intrinsics. +; It also checks that stores to Storage or Transient Storage prior to a revert are eliminated. +define void @test(ptr addrspace(3) %src) noreturn { +; CHECK-LABEL: define void @test( +; CHECK-SAME: ptr addrspace(3) [[SRC:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALLDATASIZE:%.*]] = tail call i256 @llvm.evm.calldatasize() +; CHECK-NEXT: br label %[[IF_JOIN22:.*]] +; CHECK: [[IF_JOIN22]]: +; CHECK-NEXT: store i256 1, ptr addrspace(5) null, align 32 +; CHECK-NEXT: [[COMPARISON_RESULT_I:%.*]] = icmp ugt i256 [[CALLDATASIZE]], 4 +; CHECK-NEXT: br i1 [[COMPARISON_RESULT_I]], label %[[FUN_EXIT:.*]], label %[[IF_JOIN:.*]] +; CHECK: [[IF_JOIN]]: +; CHECK-NEXT: store i256 7, ptr addrspace(6) null, align 32 +; CHECK-NEXT: tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 32) +; CHECK-NEXT: unreachable +; CHECK: [[FUN_EXIT]]: +; CHECK-NEXT: tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) +; CHECK-NEXT: unreachable +; +entry: + ; To be removed + store i256 128, ptr addrspace(1) inttoptr (i256 64 to ptr addrspace(1)), align 32 + %calldatasize = tail call i256 @llvm.evm.calldatasize() + br label %if_join22 + +if_join22: + store i256 1, ptr addrspace(5) null, align 32 + %comparison_result.i = icmp ugt i256 %calldatasize, 4 + br i1 %comparison_result.i, label %fun_exit, label %if_join + +if_join: + store i256 7, ptr addrspace(6) null, align 32 + ; To be removed + call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), ptr addrspace(3) %src, i256 32, i1 false) + tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 32) + unreachable + +fun_exit: + ; To be removed + store i256 10, ptr addrspace(1) null, align 32 + ; To Be removed + store i256 2, ptr addrspace(5) null, align 32 + store i256 3, ptr addrspace(6) null, align 32 + tail call void @llvm.evm.revert(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 0) + unreachable +} From 723b9715d7890c2376a162476081f36c1f938a68 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Fri, 1 Aug 2025 10:28:50 +0200 Subject: [PATCH 179/203] [EVM] Add pre-commit test for Fold signextend(b, signextend(b, x)) -> signextend(b, x) Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/fold-signextend.ll | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/fold-signextend.ll diff --git a/llvm/test/CodeGen/EVM/fold-signextend.ll b/llvm/test/CodeGen/EVM/fold-signextend.ll new file mode 100644 index 000000000000..054151bd6f47 --- /dev/null +++ b/llvm/test/CodeGen/EVM/fold-signextend.ll @@ -0,0 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=instcombine -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +define i256 @test_const(i256 %x) { +; CHECK-LABEL: define i256 @test_const( +; CHECK-SAME: i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[X]]) +; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[SIGNEXT1]]) +; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 15, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 15, i256 %signext1) + ret i256 %signext2 +} + +define i256 @test_const_ne(i256 %x) { +; CHECK-LABEL: define i256 @test_const_ne( +; CHECK-SAME: i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[X]]) +; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 10, i256 [[SIGNEXT1]]) +; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 15, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 10, i256 %signext1) + ret i256 %signext2 +} + +define i256 @test_var(i256 %b, i256 %x) { +; CHECK-LABEL: define i256 @test_var( +; CHECK-SAME: i256 [[B:%.*]], i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 [[B]], i256 [[X]]) +; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 [[B]], i256 [[SIGNEXT1]]) +; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 %b, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 %b, i256 %signext1) + ret i256 %signext2 +} + +define i256 @test_var_ne(i256 %b1, i256 %b2, i256 %x) { +; CHECK-LABEL: define i256 @test_var_ne( +; CHECK-SAME: i256 [[B1:%.*]], i256 [[B2:%.*]], i256 [[X:%.*]]) { +; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 [[B1]], i256 [[X]]) +; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 [[B2]], i256 [[SIGNEXT1]]) +; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; + %signext1 = call i256 @llvm.evm.signextend(i256 %b1, i256 %x) + %signext2 = call i256 @llvm.evm.signextend(i256 %b2, i256 %signext1) + ret i256 %signext2 +} + +declare i256 @llvm.evm.signextend(i256, i256) From 909bc486587de060846a47888ea3c4565273a984 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 31 Jul 2025 14:43:03 +0200 Subject: [PATCH 180/203] [EVM] Fold signextend(b, signextend(b, x)) -> signextend(b, x) Implement instCombineIntrinsic in TTI and do the folding. Doing this early in the pipeline, we give LLVM opportunity to do more optimizations. Signed-off-by: Vladimir Radosavljevic --- .../lib/Target/EVM/EVMTargetTransformInfo.cpp | 23 +++++++++++++++++++ llvm/lib/Target/EVM/EVMTargetTransformInfo.h | 3 +++ llvm/test/CodeGen/EVM/fold-signextend.ll | 6 ++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp b/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp index 0890e6816eb4..ad7c259757f8 100644 --- a/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.cpp @@ -12,10 +12,33 @@ //===----------------------------------------------------------------------===// #include "EVMTargetTransformInfo.h" +#include "llvm/IR/IntrinsicsEVM.h" +#include "llvm/Transforms/InstCombine/InstCombiner.h" using namespace llvm; +using namespace llvm::PatternMatch; #define DEBUG_TYPE "evmtti" +static std::optional instCombineSignExtend(InstCombiner &IC, + IntrinsicInst &II) { + // Fold signextend(b, signextend(b, x)) -> signextend(b, x) + Value *B = nullptr, *X = nullptr; + if (match(&II, m_Intrinsic( + m_Value(B), m_Intrinsic( + m_Deferred(B), m_Value(X))))) + return IC.replaceInstUsesWith(II, II.getArgOperand(1)); + + return std::nullopt; +} + +std::optional +EVMTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const { + if (II.getIntrinsicID() == Intrinsic::evm_signextend) + return instCombineSignExtend(IC, II); + + return std::nullopt; +} + unsigned EVMTTIImpl::getAssumedAddrSpace(const Value *V) const { const auto *LD = dyn_cast(V); if (!LD) diff --git a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h index 8e2147e9a8e6..82caa748ed69 100644 --- a/llvm/lib/Target/EVM/EVMTargetTransformInfo.h +++ b/llvm/lib/Target/EVM/EVMTargetTransformInfo.h @@ -42,6 +42,9 @@ class EVMTTIImpl final : public BasicTTIImplBase { : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), TLI(ST->getTargetLowering()) {} + std::optional instCombineIntrinsic(InstCombiner &IC, + IntrinsicInst &II) const; + bool allowsMisalignedMemoryAccesses(LLVMContext &Context, unsigned BitWidth, unsigned AddressSpace = 0, Align Alignment = Align(1), diff --git a/llvm/test/CodeGen/EVM/fold-signextend.ll b/llvm/test/CodeGen/EVM/fold-signextend.ll index 054151bd6f47..559d46381321 100644 --- a/llvm/test/CodeGen/EVM/fold-signextend.ll +++ b/llvm/test/CodeGen/EVM/fold-signextend.ll @@ -8,8 +8,7 @@ define i256 @test_const(i256 %x) { ; CHECK-LABEL: define i256 @test_const( ; CHECK-SAME: i256 [[X:%.*]]) { ; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[X]]) -; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 15, i256 [[SIGNEXT1]]) -; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; CHECK-NEXT: ret i256 [[SIGNEXT1]] ; %signext1 = call i256 @llvm.evm.signextend(i256 15, i256 %x) %signext2 = call i256 @llvm.evm.signextend(i256 15, i256 %signext1) @@ -32,8 +31,7 @@ define i256 @test_var(i256 %b, i256 %x) { ; CHECK-LABEL: define i256 @test_var( ; CHECK-SAME: i256 [[B:%.*]], i256 [[X:%.*]]) { ; CHECK-NEXT: [[SIGNEXT1:%.*]] = call i256 @llvm.evm.signextend(i256 [[B]], i256 [[X]]) -; CHECK-NEXT: [[SIGNEXT2:%.*]] = call i256 @llvm.evm.signextend(i256 [[B]], i256 [[SIGNEXT1]]) -; CHECK-NEXT: ret i256 [[SIGNEXT2]] +; CHECK-NEXT: ret i256 [[SIGNEXT1]] ; %signext1 = call i256 @llvm.evm.signextend(i256 %b, i256 %x) %signext2 = call i256 @llvm.evm.signextend(i256 %b, i256 %signext1) From f606ee141b705a8d49e723a5d39a4450a85be429 Mon Sep 17 00:00:00 2001 From: Chengjun Date: Wed, 7 May 2025 15:25:48 -0700 Subject: [PATCH 181/203] [AA] Move Target Specific AA before BasicAA (#125965) In this change, NVPTX AA is moved before Basic AA to potentially improve compile time. Additionally, it introduces a flag in the `ExternalAAWrapper` that allows other backends to run their target-specific AA passes before Basic AA, if desired. The change works for both New Pass Manager and Legacy Pass Manager. Original implementation by Princeton Ferro --- llvm/include/llvm/Analysis/AliasAnalysis.h | 12 ++++++ llvm/include/llvm/Target/TargetMachine.h | 5 +++ llvm/lib/Analysis/AliasAnalysis.cpp | 37 +++++++++++++++---- llvm/lib/Passes/PassBuilderPipelines.cpp | 4 ++ llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h | 7 ++++ llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp | 7 +--- llvm/lib/Target/NVPTX/NVPTXTargetMachine.h | 2 +- .../CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll | 17 +++++++++ 8 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h index 1b5a6ee24b86..c009e0dd4180 100644 --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -998,6 +998,18 @@ struct ExternalAAWrapperPass : ImmutablePass { explicit ExternalAAWrapperPass(CallbackT CB); + /// Returns whether this external AA should run before Basic AA. + /// + /// By default, external AA passes are run after Basic AA. If this returns + /// true, the external AA will be run before Basic AA during alias analysis. + /// + /// For some targets, we prefer to run the external AA early to improve + /// compile time as it has more target-specific information. This is + /// particularly useful when the external AA can provide more precise results + /// than Basic AA so that Basic AA does not need to spend time recomputing + /// them. + virtual bool runEarly() { return false; } + void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); } diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h index b8e56c755fbd..963bbba44bb6 100644 --- a/llvm/include/llvm/Target/TargetMachine.h +++ b/llvm/include/llvm/Target/TargetMachine.h @@ -371,6 +371,11 @@ class TargetMachine { // TODO: Populate all pass names by using PassRegistry.def. virtual void registerPassBuilderCallbacks(PassBuilder &) {} + /// Allow the target to register early alias analyses (AA before BasicAA) with + /// the AAManager for use with the new pass manager. Only affects the + /// "default" AAManager. + virtual void registerEarlyDefaultAliasAnalyses(AAManager &) {} + /// Allow the target to register alias analyses with the AAManager for use /// with the new pass manager. Only affects the "default" AAManager. virtual void registerDefaultAliasAnalyses(AAManager &) {} diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp index 9cdb315b6088..edc5c33d4764 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -788,28 +788,49 @@ bool AAResultsWrapperPass::runOnFunction(Function &F) { AAR.reset( new AAResults(getAnalysis().getTLI(F))); + // Add any target-specific alias analyses that should be run early. + auto *ExtWrapperPass = getAnalysisIfAvailable(); + if (ExtWrapperPass && ExtWrapperPass->runEarly() && ExtWrapperPass->CB) { + LLVM_DEBUG(dbgs() << "AAResults register Early ExternalAA: " + << ExtWrapperPass->getPassName() << "\n"); + ExtWrapperPass->CB(*this, F, *AAR); + } + // BasicAA is always available for function analyses. Also, we add it first // so that it can trump TBAA results when it proves MustAlias. // FIXME: TBAA should have an explicit mode to support this and then we // should reconsider the ordering here. - if (!DisableBasicAA) + if (!DisableBasicAA) { + LLVM_DEBUG(dbgs() << "AAResults register BasicAA\n"); AAR->addAAResult(getAnalysis().getResult()); + } // Populate the results with the currently available AAs. - if (auto *WrapperPass = getAnalysisIfAvailable()) + if (auto *WrapperPass = + getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register ScopedNoAliasAA\n"); AAR->addAAResult(WrapperPass->getResult()); - if (auto *WrapperPass = getAnalysisIfAvailable()) + } + if (auto *WrapperPass = getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register TypeBasedAA\n"); AAR->addAAResult(WrapperPass->getResult()); - if (auto *WrapperPass = getAnalysisIfAvailable()) + } + if (auto *WrapperPass = getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register GlobalsAA\n"); AAR->addAAResult(WrapperPass->getResult()); - if (auto *WrapperPass = getAnalysisIfAvailable()) + } + if (auto *WrapperPass = getAnalysisIfAvailable()) { + LLVM_DEBUG(dbgs() << "AAResults register SCEVAA\n"); AAR->addAAResult(WrapperPass->getResult()); + } // If available, run an external AA providing callback over the results as // well. - if (auto *WrapperPass = getAnalysisIfAvailable()) - if (WrapperPass->CB) - WrapperPass->CB(*this, F, *AAR); + if (ExtWrapperPass && !ExtWrapperPass->runEarly() && ExtWrapperPass->CB) { + LLVM_DEBUG(dbgs() << "AAResults register Late ExternalAA: " + << ExtWrapperPass->getPassName() << "\n"); + ExtWrapperPass->CB(*this, F, *AAR); + } // Analyses don't mutate the IR, so return false. return false; diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index d3074475f199..c0745590f1b0 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -2181,6 +2181,10 @@ AAManager PassBuilder::buildDefaultAAPipeline() { // The order in which these are registered determines their priority when // being queried. + // Add any target-specific alias analyses that should be run early. + if (TM) + TM->registerEarlyDefaultAliasAnalyses(AA); + // First we register the basic alias analysis that provides the majority of // per-function local AA logic. This is a stateless, on-demand local set of // AA techniques. diff --git a/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h b/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h index 2d204979eb6c..eec0c537448d 100644 --- a/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h +++ b/llvm/lib/Target/NVPTX/NVPTXAliasAnalysis.h @@ -79,16 +79,23 @@ class NVPTXAAWrapperPass : public ImmutablePass { // Wrapper around ExternalAAWrapperPass so that the default // constructor gets the callback. +// Note that NVPTXAA will run before BasicAA for compile time considerations. class NVPTXExternalAAWrapper : public ExternalAAWrapperPass { public: static char ID; + bool runEarly() override { return true; } + NVPTXExternalAAWrapper() : ExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { if (auto *WrapperPass = P.getAnalysisIfAvailable()) AAR.addAAResult(WrapperPass->getResult()); }) {} + + StringRef getPassName() const override { + return "NVPTX Address space based Alias Analysis Wrapper"; + } }; ImmutablePass *createNVPTXAAWrapperPass(); diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp index 097e29527eed..dd3e173694a7 100644 --- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp +++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp @@ -221,7 +221,7 @@ MachineFunctionInfo *NVPTXTargetMachine::createMachineFunctionInfo( F, STI); } -void NVPTXTargetMachine::registerDefaultAliasAnalyses(AAManager &AAM) { +void NVPTXTargetMachine::registerEarlyDefaultAliasAnalyses(AAManager &AAM) { AAM.registerFunctionAnalysis(); } @@ -317,10 +317,7 @@ void NVPTXPassConfig::addIRPasses() { disablePass(&ShrinkWrapID); addPass(createNVPTXAAWrapperPass()); - addPass(createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { - if (auto *WrapperPass = P.getAnalysisIfAvailable()) - AAR.addAAResult(WrapperPass->getResult()); - })); + addPass(createNVPTXExternalAAWrapperPass()); // NVVMReflectPass is added in addEarlyAsPossiblePasses, so hopefully running // it here does nothing. But since we need it for correctness when lowering diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h index 2b88da67a50f..bb65a14a8d79 100644 --- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h +++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h @@ -64,7 +64,7 @@ class NVPTXTargetMachine : public LLVMTargetMachine { createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F, const TargetSubtargetInfo *STI) const override; - void registerDefaultAliasAnalyses(AAManager &AAM) override; + void registerEarlyDefaultAliasAnalyses(AAManager &AAM) override; void registerPassBuilderCallbacks(PassBuilder &PB) override; diff --git a/llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll b/llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll new file mode 100644 index 000000000000..0d16b344e9f6 --- /dev/null +++ b/llvm/test/CodeGen/NVPTX/NVPTXAA_before_BasicAA.ll @@ -0,0 +1,17 @@ +; REQUIRES: asserts +; RUN: opt -aa-pipeline=default -passes='require' -debug-pass-manager -disable-output -S < %s 2>&1 | FileCheck %s +; RUN: llc --debug-only='aa' -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=LEGACY + +; In default AA pipeline, NVPTXAA should run before BasicAA to reduce compile time for NVPTX backend +target triple = "nvptx64-nvidia-cuda" + +; CHECK: Running analysis: NVPTXAA on foo +; CHECK-NEXT: Running analysis: BasicAA on foo + +; LEGACY: AAResults register Early ExternalAA: NVPTX Address space based Alias Analysis Wrapper +; LEGACY-NEXT: AAResults register BasicAA +define void @foo(){ +entry: + ret void +} + From 42b4430c9f102b8cd8e52e99e4ac3c672b185e0b Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Wed, 30 Jul 2025 00:38:02 +0200 Subject: [PATCH 182/203] [EVM] Place EVMAA at the start of the AAs chain --- llvm/lib/Target/EVM/EVMAliasAnalysis.h | 6 ++++++ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 8 ++------ llvm/lib/Target/EVM/EVMTargetMachine.h | 3 ++- llvm/test/CodeGen/EVM/O3-pipeline.ll | 2 +- llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll | 17 +++++++++++++++++ 5 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.h b/llvm/lib/Target/EVM/EVMAliasAnalysis.h index a6f38db39c95..0f477f2ef0ad 100644 --- a/llvm/lib/Target/EVM/EVMAliasAnalysis.h +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.h @@ -71,11 +71,17 @@ class EVMExternalAAWrapper : public ExternalAAWrapperPass { public: static char ID; + bool runEarly() override { return true; } + EVMExternalAAWrapper() : ExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { if (auto *WrapperPass = P.getAnalysisIfAvailable()) AAR.addAAResult(WrapperPass->getResult()); }) {} + + StringRef getPassName() const override { + return "EVM Address space based Alias Analysis Wrapper"; + } }; } // end namespace llvm diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 560bb39679a2..f1cb29550769 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -125,7 +125,7 @@ bool EVMTargetMachine::parseMachineFunctionInfo( return false; } -void EVMTargetMachine::registerDefaultAliasAnalyses(AAManager &AAM) { +void EVMTargetMachine::registerEarlyDefaultAliasAnalyses(AAManager &AAM) { AAM.registerFunctionAnalysis(); } @@ -221,11 +221,7 @@ void EVMPassConfig::addIRPasses() { addPass(createEVMLowerIntrinsicsPass()); if (TM->getOptLevel() != CodeGenOptLevel::None) { addPass(createEVMAAWrapperPass()); - addPass( - createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) { - if (auto *WrapperPass = P.getAnalysisIfAvailable()) - AAR.addAAResult(WrapperPass->getResult()); - })); + addPass(createEVMExternalAAWrapperPass()); } TargetPassConfig::addIRPasses(); } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.h b/llvm/lib/Target/EVM/EVMTargetMachine.h index 390398449afe..f605f563f046 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.h +++ b/llvm/lib/Target/EVM/EVMTargetMachine.h @@ -61,7 +61,8 @@ class EVMTargetMachine final : public LLVMTargetMachine { bool usesPhysRegsForValues() const override { return false; } void registerPassBuilderCallbacks(PassBuilder &PB) override; - void registerDefaultAliasAnalyses(AAManager &AAM) override; + + void registerEarlyDefaultAliasAnalyses(AAManager &AAM) override; }; // EVMTargetMachine. } // end namespace llvm diff --git a/llvm/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll index caad4552138b..c6f2605162ba 100644 --- a/llvm/test/CodeGen/EVM/O3-pipeline.ll +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -10,7 +10,7 @@ target triple = "evm" ; CHECK-NEXT: Machine Module Information ; CHECK-NEXT: Target Transform Information ; CHECK-NEXT: EVM Address space based Alias Analysis -; CHECK-NEXT: External Alias Analysis +; CHECK-NEXT: EVM Address space based Alias Analysis Wrapper ; CHECK-NEXT: Type-Based Alias Analysis ; CHECK-NEXT: Scoped NoAlias Alias Analysis ; CHECK-NEXT: Assumption Cache Tracker diff --git a/llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll b/llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll new file mode 100644 index 000000000000..a7ad2453b059 --- /dev/null +++ b/llvm/test/CodeGen/EVM/evmaa-before-basicaa.ll @@ -0,0 +1,17 @@ +; REQUIRES: asserts +; RUN: opt -aa-pipeline=default -passes='require' -debug-pass-manager -disable-output -S < %s 2>&1 | FileCheck %s +; RUN: llc --debug-only='aa' -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=LEGACY + +; In default AA pipeline, EVMAA should run before BasicAA to reduce compile time for EVM backend +target triple = "evm" + +; CHECK: Running analysis: EVMAA on foo +; CHECK-NEXT: Running analysis: BasicAA on foo + +; LEGACY: AAResults register Early ExternalAA: EVM Address space based Alias Analysis Wrapper +; LEGACY-NEXT: AAResults register BasicAA +define void @foo(){ +entry: + ret void +} + From 816ea150c3bda81f4826891c4d08a2edb72d93d1 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 28 Aug 2025 16:17:55 +0200 Subject: [PATCH 183/203] [EVM] Add an option to pass metadata size --- llvm/lib/Target/EVM/EVMConstantUnfolding.cpp | 8 +++++++- .../CodeGen/EVM/constant-unfolding-Oz-fallback.ll | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp index 97ad99e8515c..2e01a82760f3 100644 --- a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp +++ b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp @@ -71,6 +71,10 @@ static cl::opt CodeSizeLimit("evm-bytecode-sizelimit", cl::Hidden, cl::init(24576), cl::desc("EVM contract bytecode size limit")); +static cl::opt MetadataSize("evm-metadata-size", cl::Hidden, + cl::init(0), + cl::desc("EVM metadata size")); + namespace { using InstrsPerLoopDepthTy = SmallVector>; @@ -626,7 +630,9 @@ static bool runImpl(Module &M, MachineModuleInfo &MMI) { bool Changed = false; ConstantUnfolder Unfolder(&M.getContext()); - const unsigned ModuleCodeSize = EVM::calculateModuleCodeSize(M, MMI); + // Metadata size is included into the bytecode size. + const unsigned ModuleCodeSize = + EVM::calculateModuleCodeSize(M, MMI) + MetadataSize; // Collect PUSH instructions to process. DenseMap> diff --git a/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll b/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll index e71dd8822f8f..ff010cf64e52 100644 --- a/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll +++ b/llvm/test/CodeGen/EVM/constant-unfolding-Oz-fallback.ll @@ -1,28 +1,28 @@ -; RUN: llc -O3 --debug-only=evm-constant-unfolding -evm-bytecode-sizelimit=80 < %s 2>&1 | FileCheck %s +; RUN: llc -O3 --debug-only=evm-constant-unfolding -evm-metadata-size=16 -evm-bytecode-sizelimit=96 < %s 2>&1 | FileCheck %s ; REQUIRES: asserts target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" ; CHECK: *** Running constant unfolding in the default mode *** -; CHECK-NEXT: *** Initial module size: 136 *** +; CHECK-NEXT: *** Initial module size: 152 *** ; CHECK: Checking PUSH32_S i256 35408467139433450592217433187231851964531694900788300625387963629091585785856 ; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216134069776894924259991723442175 ; CHECK: Skipping identity transformation ; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216132831836855638879716824317951 ; CHECK: Skipping identity transformation -; CHECK: *** Current module size is 111, which still exceeds the limit, falling back to size-minimization mode *** +; CHECK: *** Current module size is 127, which still exceeds the limit, falling back to size-minimization mode *** ; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 0 *** -; CHECK: *** Current module size is 111 *** +; CHECK: *** Current module size is 127 *** ; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 1 *** ; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216134069776894924259991723442175 -; CHECK: *** Current module size is 103 *** +; CHECK: *** Current module size is 119 *** ; CHECK-NEXT: *** Running constant unfolding in size-minimization mode at loop depth 2 *** ; CHECK: Checking PUSH27_S i256 52656145834278593348959013841835216132831836855638879716824317951 -; CHECK: *** Current module size is 95 *** +; CHECK: *** Current module size is 111 *** define i256 @test(i256 %p) { From 7d282dbfcea927258d2580f64cf9f96f8ed9fa8f Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Tue, 26 Aug 2025 19:06:01 +0200 Subject: [PATCH 184/203] [EVM] Raise the maximum unfold limit from 3 instructions to 4. --- llvm/lib/Target/EVM/EVMConstantUnfolding.cpp | 2 +- llvm/test/CodeGen/EVM/constant-unfolding.ll | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp index 2e01a82760f3..786b1a426bfe 100644 --- a/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp +++ b/llvm/lib/Target/EVM/EVMConstantUnfolding.cpp @@ -58,7 +58,7 @@ static cl::opt "optimizing for speed")); static cl::opt InstrNumLimitUnfoldInto( - "evm-const-unfolding-inst-num-limit", cl::Hidden, cl::init(3), + "evm-const-unfolding-inst-num-limit", cl::Hidden, cl::init(4), cl::desc("Maximum number of instructions an original" "instruction can be unfolded into")); diff --git a/llvm/test/CodeGen/EVM/constant-unfolding.ll b/llvm/test/CodeGen/EVM/constant-unfolding.ll index 4c014ffc92e1..51f6030dcdc6 100644 --- a/llvm/test/CodeGen/EVM/constant-unfolding.ll +++ b/llvm/test/CodeGen/EVM/constant-unfolding.ll @@ -101,7 +101,10 @@ define void @test7() #1 { ; CHECK-LABEL: test7: ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT +; CHECK-NEXT: PUSH1 0x60 +; CHECK-NEXT: SHR ; CHECK-NEXT: PUSH0 ; CHECK-NEXT: RETURN entry: From d8b533c062bf73255d30a6be5fd44f96d096c8e3 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Sun, 31 Aug 2025 15:49:20 +0200 Subject: [PATCH 185/203] [EVM] Add llc O0 pipeline test --- llvm/test/CodeGen/EVM/O0-pipeline.ll | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/O0-pipeline.ll diff --git a/llvm/test/CodeGen/EVM/O0-pipeline.ll b/llvm/test/CodeGen/EVM/O0-pipeline.ll new file mode 100644 index 000000000000..02d77d8eeabf --- /dev/null +++ b/llvm/test/CodeGen/EVM/O0-pipeline.ll @@ -0,0 +1,86 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -O0 -debug-pass=Structure < %s -o /dev/null 2>&1 | FileCheck %s +target triple = "evm" + +; REQUIRES: asserts + +; CHECK-LABEL: Pass Arguments: +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: Target Pass Configuration +; CHECK-NEXT: Machine Module Information +; CHECK-NEXT: Target Transform Information +; CHECK-NEXT: Create Garbage Collector Module Metadata +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Profile summary info +; CHECK-NEXT: Machine Branch Probability Analysis +; CHECK-NEXT: ModulePass Manager +; CHECK-NEXT: Pre-ISel Intrinsic Lowering +; CHECK-NEXT: EVM Lower Intrinsics +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Lower Garbage Collection Instructions +; CHECK-NEXT: Shadow Stack GC Lowering +; CHECK-NEXT: Lower constant intrinsics +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: Expand vector predication intrinsics +; CHECK-NEXT: Instrument function entry/exit with calls to e.g. mcount() (post inlining) +; CHECK-NEXT: Scalarize Masked Memory Intrinsics +; CHECK-NEXT: Expand reduction intrinsics +; CHECK-NEXT: Final transformations before code generation +; CHECK-NEXT: Lower invoke and unwind, for unwindless code generators +; CHECK-NEXT: Remove unreachable blocks from the CFG +; CHECK-NEXT: CallGraph Construction +; CHECK-NEXT: EVM mark recursive functions +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Prepare callbr +; CHECK-NEXT: Safe Stack instrumentation pass +; CHECK-NEXT: Insert stack protectors +; CHECK-NEXT: Module Verifier +; CHECK-NEXT: Assignment Tracking Analysis +; CHECK-NEXT: Unnamed pass: implement Pass::getPassName() +; CHECK-NEXT: EVM Argument Move +; CHECK-NEXT: Finalize ISel and expand pseudo-instructions +; CHECK-NEXT: Local Stack Slot Allocation +; CHECK-NEXT: Eliminate PHI nodes for register allocation +; CHECK-NEXT: Two-Address instruction pass +; CHECK-NEXT: Remove Redundant DEBUG_VALUE analysis +; CHECK-NEXT: Fixup Statepoint Caller Saved +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Prologue/Epilogue Insertion & Frame Finalization +; CHECK-NEXT: Post-RA pseudo instruction expansion pass +; CHECK-NEXT: Insert fentry calls +; CHECK-NEXT: Insert XRay ops +; CHECK-NEXT: EVM split critical edges +; CHECK-NEXT: MachineDominator Tree Construction +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: EVM Optimize Live Intervals +; CHECK-NEXT: EVM Single use expressions +; CHECK-NEXT: Slot index numbering +; CHECK-NEXT: Live Interval Analysis +; CHECK-NEXT: Machine Natural Loop Construction +; CHECK-NEXT: Virtual Register Map +; CHECK-NEXT: Live Stack Slot Analysis +; CHECK-NEXT: Machine Block Frequency Analysis +; CHECK-NEXT: EVM backward propagation stackification +; CHECK-NEXT: Stack Slot Coloring +; CHECK-NEXT: EVM finalize stack frames +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Machine Sanitizer Binary Metadata +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: Stack Frame Layout Analysis +; CHECK-NEXT: EVM Lower jump_unless +; CHECK-NEXT: EVM constant unfolding +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Lazy Machine Block Frequency Analysis +; CHECK-NEXT: Machine Optimization Remark Emitter +; CHECK-NEXT: EVM Assembly +; CHECK-NEXT: Free MachineFunction + +define void @f() { + ret void +} +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; CHECK: {{.*}} From c6185d571347a2e7c5b56e493e845fe88961011c Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 30 Jul 2025 16:44:04 +0200 Subject: [PATCH 186/203] Revert "[EVM] Improve code generation for conditional jumps" This reverts commit 1df35863e8215272c423bf1e197896a423e0eadb. --- llvm/lib/Target/EVM/CMakeLists.txt | 7 +- llvm/lib/Target/EVM/EVMInstrInfo.td | 11 +- llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp | 79 +++---------- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 6 +- llvm/test/CodeGen/EVM/br.ll | 7 +- .../EVM/branch-folder-after-stackification.ll | 4 +- llvm/test/CodeGen/EVM/brcond.ll | 13 ++- llvm/test/CodeGen/EVM/lower-jump-unless.mir | 108 ------------------ llvm/test/CodeGen/EVM/select.ll | 2 + 9 files changed, 40 insertions(+), 197 deletions(-) delete mode 100644 llvm/test/CodeGen/EVM/lower-jump-unless.mir diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index a9cdfd01579e..3d009586a01d 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -56,18 +56,17 @@ add_llvm_target(EVMCodeGen EVMMachineFunctionInfo.cpp EVMMarkRecursiveFunctions.cpp EVMMCInstLower.cpp - EVMMachineFunctionInfo.cpp EVMOptimizeLiveIntervals.cpp EVMRegColoring.cpp EVMRegisterInfo.cpp EVMSHA3ConstFolding.cpp EVMSingleUseExpression.cpp EVMSplitCriticalEdges.cpp - EVMStackModel.cpp - EVMStackShuffler.cpp EVMStackSolver.cpp + EVMStackModel.cpp EVMStackify.cpp EVMStackifyCodeEmitter.cpp + EVMStackShuffler.cpp EVMSubtarget.cpp EVMTargetMachine.cpp EVMTargetTransformInfo.cpp @@ -90,6 +89,8 @@ add_llvm_target(EVMCodeGen Target TargetParser TransformUtils + EVMDesc + EVMInfo ADD_TO_COMPONENT EVM diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index 45727d4b7e2f..ebafa839ce02 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -430,17 +430,10 @@ let isBarrier = 1 in { } // isBarrier = 1 } // isBranch = 1, isTerminator = 1 -def : Pat<(brcond (setcc GPR:$src, 0, SETNE), bb:$dst), - (JUMPI bb:$dst, GPR:$src)>; -def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETEQ), bb:$dst), - (JUMPI bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; -def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), - (JUMPI bb:$dst, (SUB GPR:$rs1, GPR:$rs0))>; - def : Pat<(brcond (setcc GPR:$src, 0, SETEQ), bb:$dst), (JUMP_UNLESS bb:$dst, GPR:$src)>; -def : Pat<(brcond (setcc GPR:$src, -1, SETGT), bb:$dst), - (JUMP_UNLESS bb:$dst, (LT GPR:$src, (CONST_I256 0)))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), + (JUMP_UNLESS bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETGE), bb:$dst), (JUMP_UNLESS bb:$dst, (LT GPR:$rs0, GPR:$rs1))>; def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETLE), bb:$dst), diff --git a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp index 938c095db557..2e85a5157e70 100644 --- a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp +++ b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp @@ -14,20 +14,14 @@ #include "EVMMachineFunctionInfo.h" #include "EVMSubtarget.h" #include "MCTargetDesc/EVMMCTargetDesc.h" -#include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/Support/CodeGen.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" using namespace llvm; #define DEBUG_TYPE "evm-lower-jump-unless" #define EVM_LOWER_JUMP_UNLESS_NAME "EVM Lower jump_unless" -STATISTIC(NumPseudoJumpUnlessFolded, "Number of PseudoJUMP_UNLESS folded"); - namespace { class EVMLowerJumpUnless final : public MachineFunctionPass { public: @@ -72,51 +66,14 @@ static void lowerJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, .addReg(NewReg); } -/// Fold ` ; PseudoJUMP_UNLESS` into `PseudoJUMPI`. -/// -/// Supported `PrevMI` patterns and changes: -/// • `ISZERO_S` -> delete `ISZERO_S` -/// • `EQ_S` -> change to `SUB_S` -/// • `SUB_S` -> change to `EQ_S` -/// -/// Returns `true` if any fold was performed. -static bool tryFoldJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII) { - auto I = MachineBasicBlock::iterator(&MI); - auto *PrevMI = I == MI.getParent()->begin() ? nullptr : &*std::prev(I); - bool CanFold = PrevMI && (PrevMI->getOpcode() == EVM::ISZERO_S || - PrevMI->getOpcode() == EVM::EQ_S || - PrevMI->getOpcode() == EVM::SUB_S); - - if (!CanFold) - return false; - - ++NumPseudoJumpUnlessFolded; - - if (PrevMI->getOpcode() == EVM::ISZERO_S) - PrevMI->eraseFromParent(); - else if (PrevMI->getOpcode() == EVM::EQ_S) - PrevMI->setDesc(TII->get(EVM::SUB_S)); - else if (PrevMI->getOpcode() == EVM::SUB_S) - PrevMI->setDesc(TII->get(EVM::EQ_S)); - return true; -} - -/// Lower a `PseudoJUMP_UNLESS` to condition-setting + `PseudoJUMPI`. -/// -/// If `FoldJumps` is enabled and the local pattern allows it, an -/// optimisation in `tryFoldJumpUnless` removes the explicit `ISZERO_S`. -/// Otherwise the pseudo-op expands to: -/// ISZERO_S -/// PseudoJUMPI +// Lower pseudo jump_unless into iszero and jumpi instructions. This pseudo +// instruction can only be present in stackified functions. static void lowerPseudoJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, - const bool IsStackified, - const bool FoldJumps) { + const bool IsStackified) { assert(IsStackified && "Found pseudo jump_unless in non-stackified function"); assert(MI.getNumExplicitOperands() == 1 && "Unexpected number of operands in pseudo jump_unless"); - - if (!FoldJumps || !tryFoldJumpUnless(MI, TII)) - BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO_S)); + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO_S)); BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::PseudoJUMPI)) .add(MI.getOperand(0)); } @@ -127,7 +84,6 @@ bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { << "********** Function: " << MF.getName() << '\n'; }); - CodeGenOptLevel OptLevel = MF.getTarget().getOptLevel(); MachineRegisterInfo &MRI = MF.getRegInfo(); const auto *TII = MF.getSubtarget().getInstrInfo(); const bool IsStackified = @@ -135,24 +91,17 @@ bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { bool Changed = false; for (MachineBasicBlock &MBB : MF) { - auto TermIt = MBB.getFirstInstrTerminator(); - if (TermIt == MBB.end()) - continue; - - switch (TermIt->getOpcode()) { - case EVM::PseudoJUMP_UNLESS: - lowerPseudoJumpUnless(*TermIt, TII, IsStackified, - OptLevel != CodeGenOptLevel::None); - break; - case EVM::JUMP_UNLESS: - lowerJumpUnless(*TermIt, TII, IsStackified, MRI); - break; - default: - continue; + for (auto &MI : make_early_inc_range(MBB)) { + if (MI.getOpcode() == EVM::PseudoJUMP_UNLESS) + lowerPseudoJumpUnless(MI, TII, IsStackified); + else if (MI.getOpcode() == EVM::JUMP_UNLESS) + lowerJumpUnless(MI, TII, IsStackified, MRI); + else + continue; + + MI.eraseFromParent(); + Changed = true; } - - TermIt->eraseFromParent(); - Changed = true; } return Changed; } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index f1cb29550769..2c1864130f51 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -48,14 +48,10 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { // Register the target. const RegisterTargetMachine X(getTheEVMTarget()); auto &PR = *PassRegistry::getPassRegistry(); - initializeEVMAAWrapperPassPass(PR); - initializeEVMAllocaHoistingPass(PR); - initializeEVMBPStackificationPass(PR); initializeEVMCodegenPreparePass(PR); - initializeEVMExternalAAWrapperPass(PR); + initializeEVMAllocaHoistingPass(PR); initializeEVMLinkRuntimePass(PR); initializeEVMLowerIntrinsicsPass(PR); - initializeEVMLowerJumpUnlessPass(PR); initializeEVMOptimizeLiveIntervalsPass(PR); initializeEVMRegColoringPass(PR); initializeEVMSingleUseExpressionPass(PR); diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index 49170eb40263..e51d49e3da4d 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -8,9 +8,10 @@ define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: diamond: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DUP1 -; CHECK-NEXT: DUP3 -; CHECK-NEXT: SUB +; CHECK-NEXT: DUP2 +; CHECK-NEXT: DUP2 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true_bb diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll index 1d8d470c3ac7..7658cdde0aa8 100644 --- a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -9,8 +9,10 @@ define i256 @test(i256 %arg) { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT ; CHECK-NEXT: DUP2 -; CHECK-NEXT: SLT +; CHECK-NEXT: SGT +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: diff --git a/llvm/test/CodeGen/EVM/brcond.ll b/llvm/test/CodeGen/EVM/brcond.ll index 193d2be5b7e2..5c19962a92df 100644 --- a/llvm/test/CodeGen/EVM/brcond.ll +++ b/llvm/test/CodeGen/EVM/brcond.ll @@ -12,7 +12,8 @@ define void @br_eq(i256 %a, i256 %b) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: SUB +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -226,6 +227,9 @@ define void @br_eq_0(i256 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB10_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -325,6 +329,9 @@ define void @br_ule_0(i256 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP +; CHECK-NEXT: PUSH0 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB15_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -385,10 +392,10 @@ define void @br_slt_0(i256 %a) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 +; CHECK-NEXT: NOT ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: SLT -; CHECK-NEXT: ISZERO +; CHECK-NEXT: SGT ; CHECK-NEXT: PUSH4 @.BB18_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true diff --git a/llvm/test/CodeGen/EVM/lower-jump-unless.mir b/llvm/test/CodeGen/EVM/lower-jump-unless.mir deleted file mode 100644 index 015fdebf103c..000000000000 --- a/llvm/test/CodeGen/EVM/lower-jump-unless.mir +++ /dev/null @@ -1,108 +0,0 @@ -# RUN: llc -x mir --run-pass=evm-lower-jump-unless < %s | FileCheck %s -# RUN: llc -x mir --run-pass=evm-lower-jump-unless -O0 < %s | FileCheck %s --check-prefix=CHECK-O0 - ---- | - target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" - target triple = "evm" - - define void @br_eq(i256 %a, i256 %b) { - %cond = icmp eq i256 %a, %b - br i1 %cond, label %true, label %false - - true: ; preds = %0 - unreachable - - false: ; preds = %0 - unreachable - } - - define void @br_ugt(i256 %a, i256 %b) { - %cond = icmp ugt i256 %a, %b - br i1 %cond, label %true, label %false - - true: ; preds = %0 - unreachable - - false: ; preds = %0 - unreachable - } - -... ---- -name: br_eq -alignment: 1 -machineFunctionInfo: - isStackified: true - numberOfParameters: 2 - hasPushDeployAddress: false -body: | - ; CHECK-LABEL: name: br_eq - ; CHECK: SWAP2_S - ; CHECK-NEXT: POP_S - ; CHECK-NEXT: SUB_S - ; CHECK-NEXT: PseudoJUMPI %bb.2 - ; - ; CHECK-O0-LABEL: name: br_eq - ; CHECK-O0: SWAP2_S - ; CHECK-O0-NEXT: POP_S - ; CHECK-O0-NEXT: EQ_S - ; CHECK-O0-NEXT: ISZERO_S - ; CHECK-O0-NEXT: PseudoJUMPI %bb.2 - bb.0 (%ir-block.0): - successors: %bb.1(0x40000000), %bb.2(0x40000000) - liveins: $arguments, $value_stack - - SWAP2_S - POP_S - EQ_S - PseudoJUMP_UNLESS %bb.2 - - bb.1.true: - successors: - liveins: $value_stack - - bb.2.false: - liveins: $value_stack - -... ---- -name: br_ugt -alignment: 1 -machineFunctionInfo: - isStackified: true - numberOfParameters: 2 - hasPushDeployAddress: false -body: | - ; CHECK-LABEL: name: br_ugt - ; CHECK: SWAP1_S - ; CHECK-NEXT: SWAP2_S - ; CHECK-NEXT: POP_S - ; CHECK-NEXT: UGT_S - ; CHECK-NEXT: ISZERO_S - ; CHECK-NEXT: PseudoJUMPI %bb.2 - ; - ; CHECK-O0-LABEL: name: br_ugt - ; CHECK-O0: SWAP1_S - ; CHECK-O0-NEXT: SWAP2_S - ; CHECK-O0-NEXT: POP_S - ; CHECK-O0-NEXT: UGT_S - ; CHECK-O0-NEXT: ISZERO_S - ; CHECK-O0-NEXT: PseudoJUMPI %bb.2 - bb.0 (%ir-block.0): - successors: %bb.1(0x40000000), %bb.2(0x40000000) - liveins: $arguments, $value_stack - - SWAP1_S - SWAP2_S - POP_S - UGT_S - PseudoJUMP_UNLESS %bb.2 - - bb.1.true: - successors: - liveins: $value_stack - - bb.2.false: - liveins: $value_stack - -... diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll index 95fd2bf114f5..caec55c8ec4c 100644 --- a/llvm/test/CodeGen/EVM/select.ll +++ b/llvm/test/CodeGen/EVM/select.ll @@ -12,6 +12,8 @@ define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: From 383af5c1846fd7c2a973a9d3ef7540f42fe2ba12 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Sun, 31 Aug 2025 15:30:00 +0200 Subject: [PATCH 187/203] [EVM] NFC: Remove dead code from EVMLowerJumpUnless --- llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp | 23 ++-------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp index 2e85a5157e70..a553529c7c1f 100644 --- a/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp +++ b/llvm/lib/Target/EVM/EVMLowerJumpUnless.cpp @@ -51,21 +51,6 @@ FunctionPass *llvm::createEVMLowerJumpUnless() { return new EVMLowerJumpUnless(); } -// Lower jump_unless into iszero and jumpi instructions. This instruction -// can only be present in non-stackified functions. -static void lowerJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, - const bool IsStackified, MachineRegisterInfo &MRI) { - assert(!IsStackified && "Found jump_unless in stackified function"); - assert(MI.getNumExplicitOperands() == 2 && - "Unexpected number of operands in jump_unless"); - auto NewReg = MRI.createVirtualRegister(&EVM::GPRRegClass); - BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::ISZERO), NewReg) - .add(MI.getOperand(1)); - BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(EVM::JUMPI)) - .add(MI.getOperand(0)) - .addReg(NewReg); -} - // Lower pseudo jump_unless into iszero and jumpi instructions. This pseudo // instruction can only be present in stackified functions. static void lowerPseudoJumpUnless(MachineInstr &MI, const EVMInstrInfo *TII, @@ -84,7 +69,6 @@ bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { << "********** Function: " << MF.getName() << '\n'; }); - MachineRegisterInfo &MRI = MF.getRegInfo(); const auto *TII = MF.getSubtarget().getInstrInfo(); const bool IsStackified = MF.getInfo()->getIsStackified(); @@ -92,13 +76,10 @@ bool EVMLowerJumpUnless::runOnMachineFunction(MachineFunction &MF) { bool Changed = false; for (MachineBasicBlock &MBB : MF) { for (auto &MI : make_early_inc_range(MBB)) { - if (MI.getOpcode() == EVM::PseudoJUMP_UNLESS) - lowerPseudoJumpUnless(MI, TII, IsStackified); - else if (MI.getOpcode() == EVM::JUMP_UNLESS) - lowerJumpUnless(MI, TII, IsStackified, MRI); - else + if (MI.getOpcode() != EVM::PseudoJUMP_UNLESS) continue; + lowerPseudoJumpUnless(MI, TII, IsStackified); MI.eraseFromParent(); Changed = true; } From 491840d620f6497b471e4053e97b0428ab1527c3 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 30 Jul 2025 16:54:39 +0200 Subject: [PATCH 188/203] [EVM] Improve code generation for conditional jumps The patch introduces a few SelectionDAG patterns to improve codegen for the `br (brcond (setcc))` case. Additionally, due to the late expansion of JUMP_UNLESS, the patch introduces a peephole pass that optimizes JUMPI predicates: * `ISZERO ISZERO` is folded to nothing * `EQ ISZERO` is folded to `SUB` * `SUB ISZERO` is folded to `EQ` --- llvm/lib/Target/EVM/CMakeLists.txt | 1 + llvm/lib/Target/EVM/EVM.h | 2 + llvm/lib/Target/EVM/EVMInstrInfo.td | 11 +- llvm/lib/Target/EVM/EVMPeephole.cpp | 101 ++++++++++++++++++ llvm/lib/Target/EVM/EVMTargetMachine.cpp | 3 + llvm/test/CodeGen/EVM/O3-pipeline.ll | 1 + llvm/test/CodeGen/EVM/br.ll | 7 +- .../EVM/branch-folder-after-stackification.ll | 4 +- llvm/test/CodeGen/EVM/brcond.ll | 13 +-- .../CodeGen/EVM/evm-peephole-negative.mir | 85 +++++++++++++++ llvm/test/CodeGen/EVM/select.ll | 2 - 11 files changed, 209 insertions(+), 21 deletions(-) create mode 100644 llvm/lib/Target/EVM/EVMPeephole.cpp create mode 100644 llvm/test/CodeGen/EVM/evm-peephole-negative.mir diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 3d009586a01d..09181d1558f9 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -57,6 +57,7 @@ add_llvm_target(EVMCodeGen EVMMarkRecursiveFunctions.cpp EVMMCInstLower.cpp EVMOptimizeLiveIntervals.cpp + EVMPeephole.cpp EVMRegColoring.cpp EVMRegisterInfo.cpp EVMSHA3ConstFolding.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 3589454f1011..34b0f9de974e 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -68,6 +68,7 @@ FunctionPass *createEVMSplitCriticalEdges(); FunctionPass *createEVMStackify(); FunctionPass *createEVMBPStackification(); FunctionPass *createEVMLowerJumpUnless(); +FunctionPass *createEVMPeepholePass(); ModulePass *createEVMFinalizeStackFrames(); ModulePass *createEVMMarkRecursiveFunctionsPass(); ModulePass *createEVMConstantUnfolding(); @@ -91,6 +92,7 @@ void initializeEVMLowerJumpUnlessPass(PassRegistry &); void initializeEVMFinalizeStackFramesPass(PassRegistry &); void initializeEVMMarkRecursiveFunctionsPass(PassRegistry &); void initializeEVMConstantUnfoldingPass(PassRegistry &); +void initializeEVMPeepholePass(PassRegistry &); struct EVMLinkRuntimePass : PassInfoMixin { EVMLinkRuntimePass() = default; diff --git a/llvm/lib/Target/EVM/EVMInstrInfo.td b/llvm/lib/Target/EVM/EVMInstrInfo.td index ebafa839ce02..45727d4b7e2f 100644 --- a/llvm/lib/Target/EVM/EVMInstrInfo.td +++ b/llvm/lib/Target/EVM/EVMInstrInfo.td @@ -430,10 +430,17 @@ let isBarrier = 1 in { } // isBarrier = 1 } // isBranch = 1, isTerminator = 1 +def : Pat<(brcond (setcc GPR:$src, 0, SETNE), bb:$dst), + (JUMPI bb:$dst, GPR:$src)>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETEQ), bb:$dst), + (JUMPI bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), + (JUMPI bb:$dst, (SUB GPR:$rs1, GPR:$rs0))>; + def : Pat<(brcond (setcc GPR:$src, 0, SETEQ), bb:$dst), (JUMP_UNLESS bb:$dst, GPR:$src)>; -def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETNE), bb:$dst), - (JUMP_UNLESS bb:$dst, (EQ GPR:$rs0, GPR:$rs1))>; +def : Pat<(brcond (setcc GPR:$src, -1, SETGT), bb:$dst), + (JUMP_UNLESS bb:$dst, (LT GPR:$src, (CONST_I256 0)))>; def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETGE), bb:$dst), (JUMP_UNLESS bb:$dst, (LT GPR:$rs0, GPR:$rs1))>; def : Pat<(brcond (setcc GPR:$rs0, GPR:$rs1, SETLE), bb:$dst), diff --git a/llvm/lib/Target/EVM/EVMPeephole.cpp b/llvm/lib/Target/EVM/EVMPeephole.cpp new file mode 100644 index 000000000000..08fc34986e1a --- /dev/null +++ b/llvm/lib/Target/EVM/EVMPeephole.cpp @@ -0,0 +1,101 @@ + +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Pre-emission peephole optimizations. +// +//===----------------------------------------------------------------------===// + +#include "EVM.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" + +#define DEBUG_TYPE "evm-peephole" +#define EVM_PEEPHOLE "EVM Peephole" + +using namespace llvm; + +namespace { +/// Perform foldings on stack-form MIR before emission. +class EVMPeephole final : public MachineFunctionPass { +public: + static char ID; + EVMPeephole() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return EVM_PEEPHOLE; } + bool runOnMachineFunction(MachineFunction &MF) override; + bool optimizeConditionaJumps(MachineBasicBlock &MBB) const; +}; +} // namespace + +bool EVMPeephole::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + Changed |= optimizeConditionaJumps(MBB); + } + return Changed; +} + +static bool isNegatadAndJumpedOn(const MachineBasicBlock &MBB, + MachineBasicBlock::const_iterator I) { + if (I == MBB.end() || I->getOpcode() != EVM::ISZERO_S) + return false; + ++I; + if (I == MBB.end()) + return false; + if (I->getOpcode() == EVM::PseudoJUMPI) + return true; + if (I->getOpcode() != EVM::PUSH4_S) + return false; + ++I; + return I != MBB.end() && I->getOpcode() == EVM::JUMPI; +} + +bool EVMPeephole::optimizeConditionaJumps(MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator I = MBB.begin(); + const TargetInstrInfo *TII = MBB.getParent()->getSubtarget().getInstrInfo(); + + while (I != MBB.end()) { + // Fold ISZERO ISZERO to nothing, only if it's a predicate to JUMPI. + if (I->getOpcode() == EVM::ISZERO_S && + isNegatadAndJumpedOn(MBB, std::next(I))) { + std::next(I)->eraseFromParent(); + I->eraseFromParent(); + return true; + } + + // Fold EQ ISZERO to SUB, only if it's a predicate to JUMPI. + if (I->getOpcode() == EVM::EQ_S && + isNegatadAndJumpedOn(MBB, std::next(I))) { + I->setDesc(TII->get(EVM::SUB_S)); + std::next(I)->eraseFromParent(); + return true; + } + + // Fold SUB ISZERO to EQ, only if it's a predicate to JUMPI. + if (I->getOpcode() == EVM::SUB_S && + isNegatadAndJumpedOn(MBB, std::next(I))) { + I->setDesc(TII->get(EVM::EQ_S)); + std::next(I)->eraseFromParent(); + return true; + } + + ++I; + } + return false; +} + +char EVMPeephole::ID = 0; + +INITIALIZE_PASS(EVMPeephole, DEBUG_TYPE, EVM_PEEPHOLE, false, false) + +FunctionPass *llvm::createEVMPeepholePass() { return new EVMPeephole(); } diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 2c1864130f51..012e07dfcb6b 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -53,6 +53,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMLinkRuntimePass(PR); initializeEVMLowerIntrinsicsPass(PR); initializeEVMOptimizeLiveIntervalsPass(PR); + initializeEVMPeepholePass(PR); initializeEVMRegColoringPass(PR); initializeEVMSingleUseExpressionPass(PR); initializeEVMSplitCriticalEdgesPass(PR); @@ -295,6 +296,8 @@ void EVMPassConfig::addPreEmitPass() { void EVMPassConfig::addPreEmitPass2() { addPass(createEVMLowerJumpUnless()); addPass(createEVMConstantUnfolding()); + if (getOptLevel() != CodeGenOptLevel::None) + addPass(createEVMPeepholePass()); } TargetPassConfig *EVMTargetMachine::createPassConfig(PassManagerBase &PM) { diff --git a/llvm/test/CodeGen/EVM/O3-pipeline.ll b/llvm/test/CodeGen/EVM/O3-pipeline.ll index c6f2605162ba..5185960da01a 100644 --- a/llvm/test/CodeGen/EVM/O3-pipeline.ll +++ b/llvm/test/CodeGen/EVM/O3-pipeline.ll @@ -147,6 +147,7 @@ target triple = "evm" ; CHECK-NEXT: EVM Lower jump_unless ; CHECK-NEXT: EVM constant unfolding ; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: EVM Peephole ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: EVM Assembly diff --git a/llvm/test/CodeGen/EVM/br.ll b/llvm/test/CodeGen/EVM/br.ll index e51d49e3da4d..49170eb40263 100644 --- a/llvm/test/CodeGen/EVM/br.ll +++ b/llvm/test/CodeGen/EVM/br.ll @@ -8,10 +8,9 @@ define i256 @diamond(i256 %rs1, i256 %rs2) nounwind { ; CHECK-LABEL: diamond: ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST -; CHECK-NEXT: DUP2 -; CHECK-NEXT: DUP2 -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO +; CHECK-NEXT: DUP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SUB ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true_bb diff --git a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll index 7658cdde0aa8..1d8d470c3ac7 100644 --- a/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll +++ b/llvm/test/CodeGen/EVM/branch-folder-after-stackification.ll @@ -9,10 +9,8 @@ define i256 @test(i256 %arg) { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT ; CHECK-NEXT: DUP2 -; CHECK-NEXT: SGT -; CHECK-NEXT: ISZERO +; CHECK-NEXT: SLT ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: diff --git a/llvm/test/CodeGen/EVM/brcond.ll b/llvm/test/CodeGen/EVM/brcond.ll index 5c19962a92df..193d2be5b7e2 100644 --- a/llvm/test/CodeGen/EVM/brcond.ll +++ b/llvm/test/CodeGen/EVM/brcond.ll @@ -12,8 +12,7 @@ define void @br_eq(i256 %a, i256 %b) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO +; CHECK-NEXT: SUB ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -227,9 +226,6 @@ define void @br_eq_0(i256 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB10_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -329,9 +325,6 @@ define void @br_ule_0(i256 %a) { ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: POP -; CHECK-NEXT: PUSH0 -; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB15_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true @@ -392,10 +385,10 @@ define void @br_slt_0(i256 %a) { ; CHECK: ; %bb.0: ; CHECK-NEXT: JUMPDEST ; CHECK-NEXT: PUSH0 -; CHECK-NEXT: NOT ; CHECK-NEXT: SWAP2 ; CHECK-NEXT: POP -; CHECK-NEXT: SGT +; CHECK-NEXT: SLT +; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB18_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: ; %true diff --git a/llvm/test/CodeGen/EVM/evm-peephole-negative.mir b/llvm/test/CodeGen/EVM/evm-peephole-negative.mir new file mode 100644 index 000000000000..20243b0166ac --- /dev/null +++ b/llvm/test/CodeGen/EVM/evm-peephole-negative.mir @@ -0,0 +1,85 @@ +# RUN: llc -x mir -mtriple=evm-unknown-unknown -run-pass=evm-peephole < %s | FileCheck %s + +--- | + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define void @iszero_fallthrough() { + ret void + } + + define void @iszero_iszero_fallthrough() { + ret void + } + + define void @iszero_iszero_or_fallthrough() { + ret void + } +... +--- +name: iszero_fallthrough +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +body: | + bb.0: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + ISZERO_S + + bb.1: + successors: + liveins: $value_stack + +... +--- +name: iszero_iszero_fallthrough +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +body: | + bb.0: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + ISZERO_S + ISZERO_S + + bb.1: + successors: + liveins: $value_stack + +... +--- +name: iszero_iszero_or_fallthrough +liveins: + - { reg: '$arguments', virtual-reg: '' } + - { reg: '$value_stack', virtual-reg: '' } +body: | + bb.0: + successors: %bb.1(0x80000000) + liveins: $arguments, $value_stack + + ISZERO_S + ISZERO_S + OR_S + + bb.1: + successors: + liveins: $value_stack + +... + +# CHECK-LABEL: name: iszero_fallthrough +# CHECK: ISZERO_S +# +# CHECK-LABEL: name: iszero_iszero_fallthrough +# CHECK: ISZERO_S +# CHECK-NEXT: ISZERO_S +# +# CHECK-LABEL: name: iszero_iszero_or_fallthrough +# CHECK: ISZERO_S +# CHECK-NEXT: ISZERO_S +# CHECK-NEXT: OR_S + diff --git a/llvm/test/CodeGen/EVM/select.ll b/llvm/test/CodeGen/EVM/select.ll index caec55c8ec4c..95fd2bf114f5 100644 --- a/llvm/test/CodeGen/EVM/select.ll +++ b/llvm/test/CodeGen/EVM/select.ll @@ -12,8 +12,6 @@ define i256 @select(i256 %v1, i256 %v2, i256 %v3, i256 %v4) { ; CHECK-NEXT: SWAP1 ; CHECK-NEXT: SWAP3 ; CHECK-NEXT: EQ -; CHECK-NEXT: ISZERO -; CHECK-NEXT: ISZERO ; CHECK-NEXT: PUSH4 @.BB0_2 ; CHECK-NEXT: JUMPI ; CHECK-NEXT: ; %bb.1: From fa7c244e64501845fbb8415b6dcdde0e9d5f8346 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Wed, 30 Jul 2025 18:29:35 +0200 Subject: [PATCH 189/203] [EVM] Support OR in conditional jump optimization * `ISZERO ISZERO OR* PseudoJUMPI` -> `OR* PseudoJUMPI` * `EQ ISZERO OR* PseudoJUMPI` -> `SUB OR* PseudoJUMPI` * `SUB ISZERO OR* PseudoJUMPI` -> `EQ OR* PseudoJUMPI` --- llvm/lib/Target/EVM/EVMPeephole.cpp | 23 +++++++++++------------ llvm/test/CodeGen/EVM/brcond.ll | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMPeephole.cpp b/llvm/lib/Target/EVM/EVMPeephole.cpp index 08fc34986e1a..93cc7a4a80d7 100644 --- a/llvm/lib/Target/EVM/EVMPeephole.cpp +++ b/llvm/lib/Target/EVM/EVMPeephole.cpp @@ -45,19 +45,18 @@ bool EVMPeephole::runOnMachineFunction(MachineFunction &MF) { return Changed; } -static bool isNegatadAndJumpedOn(const MachineBasicBlock &MBB, +static bool isNegatedAndJumpedOn(const MachineBasicBlock &MBB, MachineBasicBlock::const_iterator I) { if (I == MBB.end() || I->getOpcode() != EVM::ISZERO_S) return false; ++I; - if (I == MBB.end()) - return false; - if (I->getOpcode() == EVM::PseudoJUMPI) - return true; - if (I->getOpcode() != EVM::PUSH4_S) - return false; - ++I; - return I != MBB.end() && I->getOpcode() == EVM::JUMPI; + // When a conditional jump’s predicate is a (possibly nested) bitwise `or`, + // both operands are eligible for folding. Currently we only fold the operand + // computed last. + // TODO: #887 Apply folding to all operands. + while (I != MBB.end() && I->getOpcode() == EVM::OR_S) + ++I; + return I != MBB.end() && I->getOpcode() == EVM::PseudoJUMPI; } bool EVMPeephole::optimizeConditionaJumps(MachineBasicBlock &MBB) const { @@ -67,7 +66,7 @@ bool EVMPeephole::optimizeConditionaJumps(MachineBasicBlock &MBB) const { while (I != MBB.end()) { // Fold ISZERO ISZERO to nothing, only if it's a predicate to JUMPI. if (I->getOpcode() == EVM::ISZERO_S && - isNegatadAndJumpedOn(MBB, std::next(I))) { + isNegatedAndJumpedOn(MBB, std::next(I))) { std::next(I)->eraseFromParent(); I->eraseFromParent(); return true; @@ -75,7 +74,7 @@ bool EVMPeephole::optimizeConditionaJumps(MachineBasicBlock &MBB) const { // Fold EQ ISZERO to SUB, only if it's a predicate to JUMPI. if (I->getOpcode() == EVM::EQ_S && - isNegatadAndJumpedOn(MBB, std::next(I))) { + isNegatedAndJumpedOn(MBB, std::next(I))) { I->setDesc(TII->get(EVM::SUB_S)); std::next(I)->eraseFromParent(); return true; @@ -83,7 +82,7 @@ bool EVMPeephole::optimizeConditionaJumps(MachineBasicBlock &MBB) const { // Fold SUB ISZERO to EQ, only if it's a predicate to JUMPI. if (I->getOpcode() == EVM::SUB_S && - isNegatadAndJumpedOn(MBB, std::next(I))) { + isNegatedAndJumpedOn(MBB, std::next(I))) { I->setDesc(TII->get(EVM::EQ_S)); std::next(I)->eraseFromParent(); return true; diff --git a/llvm/test/CodeGen/EVM/brcond.ll b/llvm/test/CodeGen/EVM/brcond.ll index 193d2be5b7e2..305942f28712 100644 --- a/llvm/test/CodeGen/EVM/brcond.ll +++ b/llvm/test/CodeGen/EVM/brcond.ll @@ -423,3 +423,31 @@ false: unreachable } +define void @br_or(i256 %a, i256 %b, i256 %c) { +; CHECK-LABEL: br_or: +; CHECK: ; %bb.0: +; CHECK-NEXT: JUMPDEST +; CHECK-NEXT: SWAP3 +; CHECK-NEXT: POP +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: DUP3 +; CHECK-NEXT: SWAP1 +; CHECK-NEXT: EQ +; CHECK-NEXT: ISZERO +; CHECK-NEXT: SWAP2 +; CHECK-NEXT: SUB +; CHECK-NEXT: OR +; CHECK-NEXT: PUSH4 @.BB20_2 +; CHECK-NEXT: JUMPI +; CHECK-NEXT: ; %bb.1: ; %false +; CHECK-NEXT: .BB20_2: ; %true +; CHECK-NEXT: JUMPDEST + %cond1 = icmp ne i256 %a, %b + %cond2 = icmp ne i256 %a, %c + %cond = or i1 %cond1, %cond2 + br i1 %cond, label %true, label %false +false: + unreachable +true: + unreachable +} From 568f4c21977a9c9c973fd15dec968cd63d345fec Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 9 Sep 2025 13:33:26 +0200 Subject: [PATCH 190/203] [EVM][InlineCost] Add pre-commit tests for Set threshold to a small value when BB is unreachable-terminated Add positive and negative tests to show why in some cases is preferable to inline when BB is unreachable-terminated. Functions callee_inline is taken from real world example where inlining this function reduces one SLOAD after optimizations (we have the same load from the callers and in the callee). Signed-off-by: Vladimir Radosavljevic --- .../inline-in-bb-unreachable-terminated.ll | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll diff --git a/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll b/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll new file mode 100644 index 000000000000..bb06c57c3e2d --- /dev/null +++ b/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll @@ -0,0 +1,79 @@ +; RUN: opt -passes=inline -S < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +; CHECK-LABEL: define private void @callee_inline +define private void @callee_inline(i256 %arg) { +entry: + %itptr = inttoptr i256 %arg to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %itptr, align 1 + %and = and i256 %load, 1461501637330902918203684832716283019655932542975 + %cmp = icmp eq i256 %and, 0 + br i1 %cmp, label %bb1, label %bb2 + +bb1: + store i256 32754936060235842233766999496646880210696060726502698976450834618736336437248, ptr addrspace(1) null, align 32 + tail call void @llvm.evm.revert(ptr addrspace(1) null, i256 32) + unreachable + +bb2: + ret void +} + +; CHECK-LABEL: define void @caller_inline1 +define void @caller_inline1(i256 %arg) { +entry: +; CHECK: call void @callee_inline(i256 %arg) + call void @callee_inline(i256 %arg) + %itptr = inttoptr i256 %arg to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %itptr, align 1 + store i256 %load, ptr addrspace(1) null, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: define void @caller_inline2 +define void @caller_inline2(i256 %arg1, i256 %arg2) { +entry: +; CHECK: call void @callee_inline(i256 %arg2) + call void @callee_inline(i256 %arg2) + %itptr = inttoptr i256 %arg2 to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %itptr, align 1 + store i256 %load, ptr addrspace(1) null, align 32 + call void @llvm.evm.return(ptr addrspace(1) null, i256 32) + unreachable +} + +; CHECK-LABEL: define private void @callee_noinline +define private void @callee_noinline() { +entry: + store i256 128, ptr addrspace(5) inttoptr (i256 32 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 64 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 96 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 128 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 160 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 192 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 224 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 256 to ptr addrspace(5)), align 32 + store i256 128, ptr addrspace(5) inttoptr (i256 288 to ptr addrspace(5)), align 32 + ret void +} + +; CHECK-LABEL: define void @caller_noinline1 +define void @caller_noinline1() { +entry: +; CHECK: call void @callee_noinline() + call void @callee_noinline() + call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} + +; CHECK-LABEL: define void @caller_noinline2 +define void @caller_noinline2() { +entry: +; CHECK: call void @callee_noinline() + call void @callee_noinline() + call void @llvm.evm.return(ptr addrspace(1) null, i256 0) + unreachable +} From e06dab6870e427475e4cbe963d163c9e8412a7d9 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 29 Jul 2025 10:23:22 +0200 Subject: [PATCH 191/203] [EVM][InlineCost] Set threshold to a small value when BB is unreachable-terminated For EVM, to return from the contract we are using different instructions (e.g. return, revert) which are followed by unreachable. In the Inliner heuristic, if BB is unreachable-terminated threshold is set to 0 and these callsites are unlikely to be inlined. Instead, add small threshold and continue to calculate cost model, since in some cases we benefit from inlining. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Analysis/InlineCost.cpp | 11 +++++++++-- .../EVM/inline-in-bb-unreachable-terminated.ll | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp index 345e5a019520..cb9e700b6a85 100644 --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -1911,8 +1911,15 @@ InlineCostCallAnalyzer::getHotCallSiteThreshold(CallBase &Call, void InlineCostCallAnalyzer::updateThreshold(CallBase &Call, Function &Callee) { // If no size growth is allowed for this inlining, set Threshold to 0. if (!allowSizeGrowth(Call)) { - Threshold = 0; - return; + // EVM local change begin + if (Triple(Call.getFunction()->getParent()->getTargetTriple()).isEVM()) { + constexpr int UnreachableThreshold = 10; + Threshold = std::min(Threshold, UnreachableThreshold); + } else { + Threshold = 0; + return; + } + // EVM local change end } Function *Caller = Call.getCaller(); diff --git a/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll b/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll index bb06c57c3e2d..c038c76d37b6 100644 --- a/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll +++ b/llvm/test/CodeGen/EVM/inline-in-bb-unreachable-terminated.ll @@ -3,7 +3,7 @@ target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm" -; CHECK-LABEL: define private void @callee_inline +; CHECK-NOT: define private void @callee_inline define private void @callee_inline(i256 %arg) { entry: %itptr = inttoptr i256 %arg to ptr addrspace(5) @@ -24,7 +24,7 @@ bb2: ; CHECK-LABEL: define void @caller_inline1 define void @caller_inline1(i256 %arg) { entry: -; CHECK: call void @callee_inline(i256 %arg) +; CHECK-NOT: call void @callee_inline(i256 %arg) call void @callee_inline(i256 %arg) %itptr = inttoptr i256 %arg to ptr addrspace(5) %load = load i256, ptr addrspace(5) %itptr, align 1 @@ -36,7 +36,7 @@ entry: ; CHECK-LABEL: define void @caller_inline2 define void @caller_inline2(i256 %arg1, i256 %arg2) { entry: -; CHECK: call void @callee_inline(i256 %arg2) +; CHECK-NOT: call void @callee_inline(i256 %arg2) call void @callee_inline(i256 %arg2) %itptr = inttoptr i256 %arg2 to ptr addrspace(5) %load = load i256, ptr addrspace(5) %itptr, align 1 From da0855941cb1ad7c984cd04f66f8cdcd3b4ab54d Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Tue, 29 Oct 2024 16:16:26 +0900 Subject: [PATCH 192/203] Fix more lld warnings introduced in #111434 [-Wnontrivial-memaccess] --- lld/ELF/Arch/ARM.cpp | 2 +- lld/ELF/SymbolTable.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp index 07a7535c4a23..bbe4012930a6 100644 --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -1195,7 +1195,7 @@ template void ObjFile::importCmseSymbols() { Defined *sym = reinterpret_cast(make()); // Initialize symbol fields. - memset(sym, 0, sizeof(Symbol)); + memset(static_cast(sym), 0, sizeof(Symbol)); sym->setName(CHECK(eSyms[i].getName(stringTable), this)); sym->value = eSym.st_value; sym->size = eSym.st_size; diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 258a78ab40bb..1a8d14536e09 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -58,7 +58,7 @@ void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { // alias for sym, but that could degrade the user experience of some tools // that can print out only one symbol for each location: sym is a preferred // name than real, but they might print out real instead. - memcpy(real, sym, sizeof(SymbolUnion)); + memcpy(static_cast(real), sym, sizeof(SymbolUnion)); real->isUsedInRegularObj = false; } @@ -89,7 +89,7 @@ Symbol *SymbolTable::insert(StringRef name) { symVector.push_back(sym); // *sym was not initialized by a constructor. Initialize all Symbol fields. - memset(sym, 0, sizeof(Symbol)); + memset(static_cast(sym), 0, sizeof(Symbol)); sym->setName(name); sym->partition = 1; sym->versionId = VER_NDX_GLOBAL; From e90f87fcc5ba125ecbd6e6c073330b843d20f6c6 Mon Sep 17 00:00:00 2001 From: Henry Jiang Date: Thu, 23 Jan 2025 20:36:13 -0500 Subject: [PATCH 193/203] [llvm][NFC] Remove CV-qualified base class in PassManagerInternal.h (#124193) This resolves the `-Wignored-qualifiers` warning introduced by the new warnign in https://github.com/llvm/llvm-project/pull/121419. First caught in buildbot `ppc64le-lld-multistage-test` https://lab.llvm.org/buildbot/#/builders/168/builds/7756 --------- Co-authored-by: Henry Jiang --- llvm/include/llvm/IR/PassManagerInternal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/IR/PassManagerInternal.h b/llvm/include/llvm/IR/PassManagerInternal.h index 4ada6ee5dd68..62bede206da5 100644 --- a/llvm/include/llvm/IR/PassManagerInternal.h +++ b/llvm/include/llvm/IR/PassManagerInternal.h @@ -22,6 +22,7 @@ #include "llvm/IR/Analysis.h" #include "llvm/Support/raw_ostream.h" #include +#include #include namespace llvm { @@ -167,7 +168,7 @@ template class ResultHasInvalidateMethod { // ambiguous if there were an invalidate member in the result type. template static DisabledType NonceFunction(T U::*); struct CheckerBase { int invalidate; }; - template struct Checker : CheckerBase, T {}; + template struct Checker : CheckerBase, std::remove_cv_t {}; template static decltype(NonceFunction(&Checker::invalidate)) check(rank<1>); From a0ab0cab5a25b587fb80d3aa61286000e8758e76 Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Tue, 29 Oct 2024 14:15:43 +0900 Subject: [PATCH 194/203] Fix warnings introduced in #111434 [-Wnontrivial-memaccess] --- lld/ELF/Symbols.h | 2 +- llvm/include/llvm/Support/Endian.h | 2 +- llvm/lib/ExecutionEngine/ExecutionEngine.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index e764fe8d7363..d7bf74115bae 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -86,7 +86,7 @@ class Symbol { // The default copy constructor is deleted due to atomic flags. Define one for // places where no atomic is needed. - Symbol(const Symbol &o) { memcpy(this, &o, sizeof(o)); } + Symbol(const Symbol &o) { memcpy(static_cast(this), &o, sizeof(o)); } protected: const char *nameData; diff --git a/llvm/include/llvm/Support/Endian.h b/llvm/include/llvm/Support/Endian.h index 5831fe66a1f7..f86ea8901ae4 100644 --- a/llvm/include/llvm/Support/Endian.h +++ b/llvm/include/llvm/Support/Endian.h @@ -58,7 +58,7 @@ template [[nodiscard]] inline value_type read(const void *memory, endianness endian) { value_type ret; - memcpy(&ret, + memcpy(static_cast(&ret), LLVM_ASSUME_ALIGNED( memory, (detail::PickAlignment::value)), sizeof(value_type)); diff --git a/llvm/lib/ExecutionEngine/ExecutionEngine.cpp b/llvm/lib/ExecutionEngine/ExecutionEngine.cpp index 8297d15b1580..38413fba4eab 100644 --- a/llvm/lib/ExecutionEngine/ExecutionEngine.cpp +++ b/llvm/lib/ExecutionEngine/ExecutionEngine.cpp @@ -1056,7 +1056,7 @@ void ExecutionEngine::StoreValueToMemory(const GenericValue &Val, *((double*)Ptr) = Val.DoubleVal; break; case Type::X86_FP80TyID: - memcpy(Ptr, Val.IntVal.getRawData(), 10); + memcpy(static_cast(Ptr), Val.IntVal.getRawData(), 10); break; case Type::PointerTyID: // Ensure 64 bit target pointers are fully initialized on 32 bit hosts. From ee78cccb2bbd6ce047f83ce466ebf4abe8f0fc92 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Sat, 13 Sep 2025 23:02:42 +0200 Subject: [PATCH 195/203] [EVM] NFC: Remove old stackifier --- llvm/lib/Target/EVM/CMakeLists.txt | 2 - llvm/lib/Target/EVM/EVM.h | 4 - llvm/lib/Target/EVM/EVMRegColoring.cpp | 168 --- llvm/lib/Target/EVM/EVMStackify.cpp | 1234 ---------------------- llvm/lib/Target/EVM/EVMTargetMachine.cpp | 24 +- 5 files changed, 6 insertions(+), 1426 deletions(-) delete mode 100644 llvm/lib/Target/EVM/EVMRegColoring.cpp delete mode 100644 llvm/lib/Target/EVM/EVMStackify.cpp diff --git a/llvm/lib/Target/EVM/CMakeLists.txt b/llvm/lib/Target/EVM/CMakeLists.txt index 09181d1558f9..81036de5b922 100644 --- a/llvm/lib/Target/EVM/CMakeLists.txt +++ b/llvm/lib/Target/EVM/CMakeLists.txt @@ -58,14 +58,12 @@ add_llvm_target(EVMCodeGen EVMMCInstLower.cpp EVMOptimizeLiveIntervals.cpp EVMPeephole.cpp - EVMRegColoring.cpp EVMRegisterInfo.cpp EVMSHA3ConstFolding.cpp EVMSingleUseExpression.cpp EVMSplitCriticalEdges.cpp EVMStackSolver.cpp EVMStackModel.cpp - EVMStackify.cpp EVMStackifyCodeEmitter.cpp EVMStackShuffler.cpp EVMSubtarget.cpp diff --git a/llvm/lib/Target/EVM/EVM.h b/llvm/lib/Target/EVM/EVM.h index 34b0f9de974e..aef3f124cbe5 100644 --- a/llvm/lib/Target/EVM/EVM.h +++ b/llvm/lib/Target/EVM/EVM.h @@ -62,10 +62,8 @@ ModulePass *createEVMLinkRuntimePass(); // Late passes. FunctionPass *createEVMOptimizeLiveIntervals(); -FunctionPass *createEVMRegColoring(); FunctionPass *createEVMSingleUseExpression(); FunctionPass *createEVMSplitCriticalEdges(); -FunctionPass *createEVMStackify(); FunctionPass *createEVMBPStackification(); FunctionPass *createEVMLowerJumpUnless(); FunctionPass *createEVMPeepholePass(); @@ -80,10 +78,8 @@ void initializeEVMLowerIntrinsicsPass(PassRegistry &); void initializeEVMArgumentMovePass(PassRegistry &); void initializeEVMLinkRuntimePass(PassRegistry &); void initializeEVMOptimizeLiveIntervalsPass(PassRegistry &); -void initializeEVMRegColoringPass(PassRegistry &); void initializeEVMSingleUseExpressionPass(PassRegistry &); void initializeEVMSplitCriticalEdgesPass(PassRegistry &); -void initializeEVMStackifyPass(PassRegistry &); void initializeEVMBPStackificationPass(PassRegistry &); void initializeEVMAAWrapperPassPass(PassRegistry &); void initializeEVMExternalAAWrapperPass(PassRegistry &); diff --git a/llvm/lib/Target/EVM/EVMRegColoring.cpp b/llvm/lib/Target/EVM/EVMRegColoring.cpp deleted file mode 100644 index 59bfcf0c9213..000000000000 --- a/llvm/lib/Target/EVM/EVMRegColoring.cpp +++ /dev/null @@ -1,168 +0,0 @@ -//===---------- EVMRegColoring.cpp - Register coloring -----*- 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 implements a virtual register coloring pass. -// -// EVM doesn't have a fixed number of registers, but it is still -// desirable to minimize the total number of registers used in each function. -// -// This code is modeled after lib/CodeGen/StackSlotColoring.cpp. -// -//===----------------------------------------------------------------------===// - -#include "EVM.h" -#include "EVMMachineFunctionInfo.h" -#include "llvm/CodeGen/LiveIntervals.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -#define DEBUG_TYPE "evm-reg-coloring" - -namespace { -class EVMRegColoring final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - EVMRegColoring() : MachineFunctionPass(ID) {} - - StringRef getPassName() const override { return "EVM Register Coloring"; } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired(); - AU.addRequired(); - AU.addPreserved(); - AU.addPreservedID(MachineDominatorsID); - MachineFunctionPass::getAnalysisUsage(AU); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - -private: -}; -} // end anonymous namespace - -char EVMRegColoring::ID = 0; -INITIALIZE_PASS(EVMRegColoring, DEBUG_TYPE, "Minimize number of registers used", - false, false) - -FunctionPass *llvm::createEVMRegColoring() { return new EVMRegColoring(); } - -// Compute the total spill weight for VReg. -static float computeWeight(const MachineRegisterInfo *MRI, - const MachineBlockFrequencyInfo *MBFI, - unsigned VReg) { - float Weight = 0.0F; - for (MachineOperand &MO : MRI->reg_nodbg_operands(VReg)) - Weight += LiveIntervals::getSpillWeight(MO.isDef(), MO.isUse(), MBFI, - *MO.getParent()); - return Weight; -} - -bool EVMRegColoring::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Register Coloring **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - if (MF.exposesReturnsTwice()) - return false; - - MachineRegisterInfo *MRI = &MF.getRegInfo(); - LiveIntervals *Liveness = &getAnalysis().getLIS(); - const MachineBlockFrequencyInfo *MBFI = - &getAnalysis().getMBFI(); - EVMMachineFunctionInfo &MFI = *MF.getInfo(); - - // Gather all register intervals into a list and sort them. - unsigned NumVRegs = MRI->getNumVirtRegs(); - SmallVector SortedIntervals; - SortedIntervals.reserve(NumVRegs); - - LLVM_DEBUG(dbgs() << "Interesting register intervals:\n"); - for (unsigned I = 0; I < NumVRegs; ++I) { - Register VReg = Register::index2VirtReg(I); - if (MFI.isVRegStackified(VReg)) - continue; - // Skip unused registers, which can use $drop. - if (MRI->use_empty(VReg)) - continue; - - LiveInterval *LI = &Liveness->getInterval(VReg); - assert(LI->weight() == 0.0F); - LI->setWeight(computeWeight(MRI, MBFI, VReg)); - LLVM_DEBUG(LI->dump()); - SortedIntervals.push_back(LI); - } - LLVM_DEBUG(dbgs() << '\n'); - - // Sort them to put arguments first (since we don't want to rename live-in - // registers), by weight next, and then by position. - // TODO: Investigate more intelligent sorting heuristics. For starters, we - // should try to coalesce adjacent live intervals before non-adjacent ones. - llvm::sort(SortedIntervals, [MRI](LiveInterval *LHS, LiveInterval *RHS) { - if (MRI->isLiveIn(LHS->reg()) != MRI->isLiveIn(RHS->reg())) - return MRI->isLiveIn(LHS->reg()); - if (LHS->weight() != RHS->weight()) - return LHS->weight() > RHS->weight(); - if (LHS->empty() || RHS->empty()) - return !LHS->empty() && RHS->empty(); - return *LHS < *RHS; - }); - - LLVM_DEBUG(dbgs() << "Coloring register intervals:\n"); - SmallVector SlotMapping(SortedIntervals.size(), -1U); - SmallVector, 16> Assignments( - SortedIntervals.size()); - BitVector UsedColors(SortedIntervals.size()); - bool Changed = false; - for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { - LiveInterval *LI = SortedIntervals[I]; - Register Old = LI->reg(); - size_t Color = I; - const TargetRegisterClass *RC = MRI->getRegClass(Old); - - // Check if it's possible to reuse any of the used colors. - if (!MRI->isLiveIn(Old)) - for (unsigned C : UsedColors.set_bits()) { - if (MRI->getRegClass(SortedIntervals[C]->reg()) != RC) - continue; - for (LiveInterval *OtherLI : Assignments[C]) - if ((!OtherLI->empty() && OtherLI->overlaps(*LI)) || - Liveness->intervalIsInOneMBB(*OtherLI) != - Liveness->intervalIsInOneMBB(*LI)) - goto continue_outer; - Color = C; - break; - continue_outer:; - } - - Register New = SortedIntervals[Color]->reg(); - SlotMapping[I] = New; - Changed |= Old != New; - UsedColors.set(Color); - Assignments[Color].push_back(LI); - - LLVM_DEBUG(dbgs() << "Assigning vreg" << Register::virtReg2Index(LI->reg()) - << " to vreg" << Register::virtReg2Index(New) << "\n"); - } - if (!Changed) - return false; - - // Rewrite register operands. - for (size_t I = 0, E = SortedIntervals.size(); I < E; ++I) { - Register Old = SortedIntervals[I]->reg(); - unsigned New = SlotMapping[I]; - if (Old != New) - MRI->replaceRegWith(Old, New); - } - return true; -} diff --git a/llvm/lib/Target/EVM/EVMStackify.cpp b/llvm/lib/Target/EVM/EVMStackify.cpp deleted file mode 100644 index 7eb73c4da234..000000000000 --- a/llvm/lib/Target/EVM/EVMStackify.cpp +++ /dev/null @@ -1,1234 +0,0 @@ -//===---------- EVMStackify.cpp - Replace phys regs with virt regs --------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// The pass works right before the AsmPrinter and performs code stackification, -// so it can be executed on the Ethereum virtual stack machine. Physically, -// this means adding stack manipulation instructions (POP, PUSH, DUP or SWAP) -// to the Machine IR and removing stack operands of instructions. Logically, -// this corresponds to allocation of stack slots and mapping them on virtual -// registers. -// -// Current implementation is based on the article M. Shannon, C. Bailey -// "Global Stack Allocation – Register Allocation for Stack Machines", -// but in quite simplified form. -// -// than that required for the stackification itself. It's expected that -// previous passes perform required optimizations: -// - splitting live intervals that contain unrelated register values. -// - a code scheduling to form single-expression-use. -// - coloring of virtual registers. This is a kind of register coalescing. -// -// Main conception borrowed from the article is a logical view of the physical -// stack in which stack consists of 3 main regions. Starting from the top, -// these are: -// - The evaluation region (E-stack) -// - The local region (L-stack) -// - The transfer region (X-stack) -// -// E-stack is used for holding instruction arguments/results right before and -// after its execution. In all other cases it should be empty. -// -// The L-stack is always empty at the beginning and end of any basic block, -// but may contain values between instructions. The values in L-stack are live -// inside one basic block. -// -// The X-stack is used to store values both during basic blocks and on edges in -// the flow graph. The x-stack need only be empty at procedure exit. Unlike the -// article, in current implementation X-stack is statically formed and fixed -// for the entire function. This makes an implementation easier, but leads -// to non-optimal stack usage, which, in turn, breaks the stack limits. -// FIXME: try to implement "Global Stack Allocation" approach. -// -// The algorithm maintains programmatic model of the stack to know, at any -// given point of the code: a) height of physical stack, b) content of both -// the L and X-stacks. -// -// The algorithm runs, in outline, as follows (on a function level): -// -// 1. Determine content of the X-stack. Here we include all the virtual -// registers, whose live intervals cross a BB boundary. Its content is fixed -// during the whole function. See, allocateXStack() -// -// 2. Perform handling of each instruction. See handleInstrUses(). -// As an example let's see how -// -// %vreg7 = ADD %vreg1 kill, %vreg2 kill -// -// is handled. Suppose the stack layout before the instruction is -// -// |vreg2| - depth 1 -// L |vreg3| -// |vreg9| -// |vreg1| - depth 4 -// --------- -// |vreg13| -// X |vreg7| - depth 6 -// |vreg11| -// --------- -// |raddr| - function return address -// |farg1| - first function argument -// |farg2| -// ....... -// |fargN| -// -// In order to execute the instruction its arguments should be placed -// on top of the stack for which we can use (DUP1 - DUP16) instructions. -// In terms of the stack model we load them to E-stack. So, to load -// arguments and execute the instructions: -// -// DUP4 // load %vreg1; No need to load %vreg2, as it's already located -// // in the right place (depth 1) and it's killed at ADD. -// ADD -// -// Stack layout rigth after loading arguments on top of the stack: -// -// -// |vreg1| - depth 1 -// E |vreg2| - depth 2 // It was logically migrated from L -> E stack -// --------- -// L |vreg3| -// |vreg9| -// |vreg1| - depth 5 -// --------- -// |vreg13| -// X |vreg7| - depth 7 -// |vreg11| -// --------- -// |raddr| - function return address -// |farg1| - first function argument -// |farg2| -// ....... -// |fargN| -// -// Stack layout right after instruction execution: -// -// E |vreg7| -// --------- -// L |vreg3| -// |vreg9| -// |vreg1| - depth 4, is dead -// --------- -// |vreg13| -// X |vreg7| - depth 6 -// |vreg11| -// --------- -// |raddr| - function return address -// |farg1| - first function argument -// |farg2| -// ....... -// |fargN| -// -// The resulting register %vreg7 should be placed to its final location in -// X-stack. To do this we can use SWAP + POP pair, so the final "stackified" -// code is: -// -// DUP4 // Duplicated the 4th stack item -// ADD -// SWAP5 // Swap top item with 6th (SWAP opcodes have shifted names) -// POP // Remove top item from the stack -// -// Note: -// -// - resulted code has actually 25% of payload instructions. One the -// goals of stackification is to minimize the stack manipulation -// instructions. -// -// - %vreg1 is dead after the instruction, so it theoretically should be -// removed from the stack. Unfortunately, EVM has no efficient -// instructions fot this, so we need emulate it via a sequence of -// SWAP + DUP + POP instructions which may lead to quite inefficient -// code. As an experiment, this is implemented in handleInstrDefs(). -// A better strategy would be to wait when a dead register is near -// the stack top. -// -// - Several instructions requires special handling (FCALL, RET, so on). -// -// 3. Once all the instructions are handled perform some clean up: -// replace, remove dead instructions. -// -// -// The main drawback of this algorithm is that X-stack is fixed during the -// whole function, which, for example, means we cannot remove dead registers -// from it. On more-or-less complicated programs this may lead to an attempt -// to access too far from stack top (> 16 element), which triggers an assertion -// in the code. -// More sophisticated algorithm is required, as described in the article. -// -// TODO: CPR-1562. Current implementation is a draft stackification version -// which needs to be refactored, cleaned and documented. -// -//===----------------------------------------------------------------------===// - -#include "EVM.h" -#include "EVMMachineFunctionInfo.h" -#include "EVMSubtarget.h" -#include "MCTargetDesc/EVMMCTargetDesc.h" -#include "TargetInfo/EVMTargetInfo.h" -#include "llvm/CodeGen/LiveIntervals.h" -#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" -#include "llvm/CodeGen/MachineDominators.h" -#include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/CodeGen/Passes.h" -#include "llvm/MC/MCContext.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" - -#include -#include - -using namespace llvm; - -#define DEBUG_TYPE "evm-stackify" - -namespace { - -enum class StackType { X, L, E }; - -struct StackLocation { - StackType Type; - // Depth counted from the top of this stack type - // (not a physical depth). - unsigned Depth; -}; - -using MIIter = MachineBasicBlock::iterator; -using RegisterLocation = std::pair; - -class StackModel { - [[maybe_unused]] static constexpr unsigned StackAccessDepthLimit = 16; - const LiveIntervals &LIS; - const EVMInstrInfo *TII; - MachineFunction *MF; - - // Frame objects area which is the result of - // alloca's lowering. - unsigned NumFrameObjs = 0; - std::deque XStack; - std::deque LStack; - // E-stack is only used to properly calculate physical stack - // height, so in the code below we usually push there '0' - // register. - std::deque EStack; - SmallVector ToErase; - -public: - StackModel(MachineFunction *MF, const LiveIntervals &LIS, - const EVMInstrInfo *TII) - : LIS(LIS), TII(TII), MF(MF) {} - - // Loads instruction arguments to the stack top and stores - // results. - void handleInstruction(MachineInstr *MI); - - // Allocate space for both X stack and allocas. - void preProcess(); - - // Special handling of some instructions. - void postProcess(); - - void enterBB(MachineBasicBlock *BB) { - assert(EStack.empty() && LStack.empty()); - } - - void leaveBB(MachineBasicBlock *BB) { - // Clear L-stack. - assert(EStack.empty()); - peelPhysStack(StackType::L, LStack.size(), BB, BB->end()); - } - - void dumpState() const; - -private: - StackLocation allocateStackModelLoc(const Register &Reg); - - void allocateXStack(); - - void allocateFrameObjects(); - - std::optional getStackLocation(const Register &Reg) const; - - bool isRegisterKill(const Register &Reg, const MachineInstr *MI) const; - - void migrateToEStack(const Register &Reg, const StackLocation &Loc); - - unsigned getPhysRegDepth(const StackLocation &Loc) const; - - unsigned getPhysRegDepth(const Register &Reg) const; - - MachineInstr *loadRegToPhysStackTop(unsigned Depth, MachineBasicBlock *MBB, - MIIter Insert, const DebugLoc &DL); - - MachineInstr *loadRegToPhysStackTop(const Register &Reg, - MachineBasicBlock *MBB, MIIter Insert, - const DebugLoc &DL); - - MachineInstr *loadRegToPhysStackTop(const Register &Reg, MachineInstr *MI) { - return loadRegToPhysStackTop(Reg, MI->getParent(), MI, MI->getDebugLoc()); - } - - MachineInstr *loadRegToPhysStackTop(unsigned Depth, MachineInstr *MI) { - return loadRegToPhysStackTop(Depth, MI->getParent(), MI, MI->getDebugLoc()); - } - - MachineInstr *storeRegToPhysStack(const Register &Reg, MIIter Insert); - - MachineInstr *storeRegToPhysStackAt(unsigned Depth, MachineBasicBlock *MBB, - MIIter Insert, const DebugLoc &DL); - - SmallVector::const_iterator - migrateTopRegsToEStack(const SmallVector &RegLoc, - MachineInstr *MI); - - bool migrateTopRegToEStack(const Register &Reg, MachineInstr *MI); - - StackLocation pushRegToStackModel(StackType Type, const Register &Reg); - - void handleInstrUses(MachineInstr *MI); - - void handleInstrDefs(MachineInstr *MI); - - void handleArgument(MachineInstr *MI); - - void handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, - const Register &Reg); - - void handleCondJump(MachineInstr *MI); - - void handleJump(MachineInstr *MI); - - void handleReturn(MachineInstr *MI); - - void handleCall(MachineInstr *MI); - - void handleStackLoad(MachineInstr *MI); - - void handleStackStore(MachineInstr *MI); - - void clearFrameObjsAtInst(MachineInstr *MI); - - void clearPhysStackAtInst(StackType TargetStackType, MachineInstr *MI, - const Register &Reg); - - void peelPhysStack(StackType Type, unsigned NumItems, MachineBasicBlock *BB, - MIIter Pos); - - void stackifyInstruction(MachineInstr *MI); - - static unsigned getDUPOpcode(unsigned Depth); - - static unsigned getSWAPOpcode(unsigned Depth); - - unsigned getStackSize() const { - return NumFrameObjs + XStack.size() + LStack.size() + EStack.size(); - } - - std::deque &getStack(StackType Type) { - return (Type == StackType::X) ? XStack : LStack; - } -}; - -} // end anonymous namespace - -// FIXME: this needs some kind of hashing. -std::optional -StackModel::getStackLocation(const Register &Reg) const { - auto It = std::find(LStack.begin(), LStack.end(), Reg); - if (It != LStack.end()) { - assert(std::count(LStack.begin(), LStack.end(), Reg) == 1); - unsigned Depth = std::distance(LStack.begin(), It); - return {{StackType::L, Depth}}; - } - - It = std::find(XStack.begin(), XStack.end(), Reg); - if (It != XStack.end()) { - assert(std::count(XStack.begin(), XStack.end(), Reg) == 1); - unsigned Depth = std::distance(XStack.begin(), It); - return {{StackType::X, Depth}}; - } - - return std::nullopt; -} - -void StackModel::handleInstruction(MachineInstr *MI) { - // Handle special cases. - switch (MI->getOpcode()) { - default: - break; - case EVM::JUMP: - handleJump(MI); - return; - case EVM::JUMPI: - case EVM::JUMP_UNLESS: - handleCondJump(MI); - return; - case EVM::ARGUMENT: - handleArgument(MI); - return; - case EVM::RET: - // TODO: handle RETURN, REVERT, STOP - handleReturn(MI); - return; - } - - handleInstrUses(MI); - - assert(EStack.empty()); - - if (MI->getOpcode() == EVM::STACK_LOAD) - handleStackLoad(MI); - - handleInstrDefs(MI); -} - -void StackModel::handleInstrUses(MachineInstr *MI) { - // Handle a general case. Collect use registers. - SmallVector> RegLocs; - for (const auto &MO : MI->explicit_uses()) { - if (!MO.isReg()) - continue; - - const auto Reg = MO.getReg(); - // SP is not used anyhow. - if (Reg == EVM::SP) - continue; - - const auto &Loc = getStackLocation(Reg); - assert(Loc); - RegLocs.push_back({Reg, *Loc}); - LLVM_DEBUG(dbgs() << "Use: " << Reg << ", Depth: " << getPhysRegDepth(Reg) - << '\n'); - } - - // If MI has two args, both are on stack top and at least one of them is - // killed, try to swap them to minimize the number of used DUP instructions. - bool NeedsSwap = false; - if (RegLocs.size() == 2) { - const Register &Use1 = RegLocs[0].first; - const Register &Use2 = RegLocs[1].first; - const unsigned Depth1 = getPhysRegDepth(Use1); - const unsigned Depth2 = getPhysRegDepth(Use2); - if (Depth1 == 1 && Depth2 == 0 && isRegisterKill(Use1, MI) && - isRegisterKill(Use2, MI)) - NeedsSwap = true; - - if (Depth1 == 0 && isRegisterKill(Use1, MI) && - (Depth2 > 1 || !isRegisterKill(Use2, MI))) - NeedsSwap = true; - - if (NeedsSwap) { - std::swap(RegLocs[0], RegLocs[1]); - LLVM_DEBUG(dbgs() << "Swap top stack items\n"); - } - - // If we can swap instruction operands then no need to swap them in the - // physical stack. - if (MI->isCommutable()) - NeedsSwap = false; - } - - SmallVector::const_iterator B = RegLocs.begin(), - StartUse = - migrateTopRegsToEStack( - RegLocs, MI); - for (const auto &Loc : reverse(make_range(B, StartUse))) - loadRegToPhysStackTop(Loc.first, MI); - - if (NeedsSwap) - BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::SWAP1)); - - if (MI->getOpcode() == EVM::STACK_STORE) - handleStackStore(MI); - - // Pop from the E-stack the registers consumed by the MI. - EStack.erase(EStack.begin(), EStack.begin() + RegLocs.size()); -} - -void StackModel::handleInstrDefs(MachineInstr *MI) { - unsigned NumXLStackRegs = 0; - MachineBasicBlock *MBB = MI->getParent(); - const DebugLoc &DL = MI->getDebugLoc(); - for (const auto &MO : reverse(MI->defs())) { - assert(MO.isReg()); - - LLVM_DEBUG(dbgs() << "Def(rev): " << MO.getReg() << '\n'); - // Push instruction's result into E-stack - EStack.push_front(MO.getReg()); - if (getStackLocation(MO.getReg())) - ++NumXLStackRegs; - } - - if (EStack.empty()) - return; - - // The MI pushed on the EStack several registers. Some of them need to be - // placed onto existing X/L stack locations. For others we need to allocate - // new slots on X/L stack. - auto RevIt = std::prev(EStack.end()); - MachineInstr *MII = MI; - for (unsigned Iter = 0; Iter < NumXLStackRegs; ++Iter) { - const Register TopReg = EStack.front(); - // If there exists a slot for the register, just move it there. - if (getStackLocation(TopReg)) { - MII = storeRegToPhysStack(TopReg, MII); - continue; - } - // Try to find a register for which there is s slot starting from the bottom - // of the ESatck. - const auto Begin = EStack.begin(); - while (RevIt != Begin && !getStackLocation(*RevIt)) - --RevIt; - - // Swap just found register with the one on the stack top and move it - // to existing location. - unsigned Depth = std::distance(Begin, RevIt); - const unsigned SWAPOpc = getSWAPOpcode(Depth); - MII = BuildMI(*MBB, std::next(MIIter(MII)), DL, TII->get(SWAPOpc)); - std::swap(*Begin, *RevIt); - MII = storeRegToPhysStack(EStack.front(), MII); - } - - // Handle the registers for which we need to allocate X/L stack, performing - // migration E -> L/X stack. - for (const auto Reg : reverse(EStack)) { - assert(!getStackLocation(Reg)); - allocateStackModelLoc(Reg); - EStack.pop_back(); - } - - const unsigned NumDeadRegs = std::count_if( - LStack.begin(), LStack.end(), - [this, MI](const Register &Reg) { return isRegisterKill(Reg, MI); }); - - const auto E = LStack.rend(); - for (unsigned I = 0; I < NumDeadRegs; ++I) { - auto DeadIt = - std::find_if(LStack.rbegin(), E, [this, MI](const Register &Reg) { - return isRegisterKill(Reg, MI); - }); - for (; DeadIt < E;) { - auto LiveIt = - std::find_if(std::next(DeadIt), E, [this, MI](const Register &Reg) { - return !isRegisterKill(Reg, MI); - }); - if (LiveIt == E) - break; - - // Swap live and dead stack locations. - const unsigned LiveDepth = getPhysRegDepth(*LiveIt); - MII = loadRegToPhysStackTop(LiveDepth, MBB, std::next(MIIter(MII)), DL); - const unsigned DeadDepth = getPhysRegDepth(*DeadIt); - MII = storeRegToPhysStackAt(DeadDepth, MBB, std::next(MIIter(MII)), DL); - - LLVM_DEBUG(dbgs() << "Swapping regs: dead: " << *DeadIt << " " - << DeadDepth << ", live: " << *LiveIt << " " - << LiveDepth << '\n'); - - std::swap(*DeadIt, *LiveIt); - DeadIt = LiveIt; - } - } - - LLVM_DEBUG(dbgs() << "Dumping stack before removing dead regs\n"); - dumpState(); - - for (unsigned I = 0; I < NumDeadRegs; ++I) { - assert(isRegisterKill(LStack.front(), MI)); - LStack.pop_front(); - MII = BuildMI(*MBB, std::next(MIIter(MII)), DL, TII->get(EVM::POP)); - } -} - -bool StackModel::isRegisterKill(const Register &Reg, - const MachineInstr *MI) const { - const LiveInterval *LI = &LIS.getInterval(Reg); - const SlotIndex SI = LIS.getInstructionIndex(*MI).getRegSlot(); - bool IsEnd = LI->expiredAt(SI); - LLVM_DEBUG(dbgs() << "LI:" << *LI << ", reg: " << Reg << '\n' - << (IsEnd ? "ends at: " : "doesn't end at: ") << SI << ",\n" - << *MI << '\n'); - return IsEnd; -} - -void StackModel::migrateToEStack(const Register &Reg, - const StackLocation &Loc) { - auto &Stack = getStack(Loc.Type); - assert(Reg == Stack.front()); - Stack.pop_front(); - // Put the register at the bottom of the E-stack. - EStack.push_back(Reg); -} - -unsigned StackModel::getPhysRegDepth(const StackLocation &Loc) const { - unsigned Depth = EStack.size(); - switch (Loc.Type) { - case StackType::X: - Depth += (Loc.Depth + LStack.size()); - break; - case StackType::L: - Depth += Loc.Depth; - break; - default: - llvm_unreachable("Unexpected stack type"); - break; - } - return Depth; -} - -unsigned StackModel::getPhysRegDepth(const Register &Reg) const { - const auto &Loc = getStackLocation(Reg); - assert(Loc); - return getPhysRegDepth(*Loc); -} - -MachineInstr *StackModel::loadRegToPhysStackTop(unsigned Depth, - MachineBasicBlock *MBB, - MIIter Insert, - const DebugLoc &DL) { - EStack.emplace_front(0); - return BuildMI(*MBB, Insert, DL, TII->get(getDUPOpcode(Depth))); -} - -MachineInstr *StackModel::loadRegToPhysStackTop(const Register &Reg, - MachineBasicBlock *MBB, - MIIter Insert, - const DebugLoc &DL) { - if (!migrateTopRegToEStack(Reg, &*Insert)) - return loadRegToPhysStackTop(getPhysRegDepth(Reg), MBB, Insert, DL); - - return &*Insert; -} - -SmallVector::const_iterator -StackModel::migrateTopRegsToEStack(const SmallVector &RegLoc, - MachineInstr *MI) { - assert(EStack.empty()); - - const auto B = RegLoc.begin(), E = RegLoc.end(); - if (RegLoc.empty()) - return E; - - const auto *const StartUse = std::find_if(B, E, [this](const auto &Loc) { - return getPhysRegDepth(Loc.first) == 0; - }); - for (const auto *It = StartUse; It != E; ++It) { - const Register &Reg = It->first; - const unsigned Depth = getPhysRegDepth(Reg); - if (It->second.Type == StackType::X || - Depth != std::distance(StartUse, It) || !isRegisterKill(Reg, MI)) - return E; - } - - for (const auto *It = StartUse; It != E; ++It) - migrateToEStack(It->first, It->second); - - return StartUse; -} - -bool StackModel::migrateTopRegToEStack(const Register &Reg, MachineInstr *MI) { - const auto Uses = MI->explicit_uses(); - const unsigned NumUses = - std::count_if(Uses.begin(), Uses.end(), [Reg](const MachineOperand &MO) { - if (MO.isReg()) - return Reg == MO.getReg(); - return false; - }); - - if (NumUses > 1) - return false; - - const auto &Loc = getStackLocation(Reg); - assert(Loc); - - if (Loc->Type == StackType::X || getPhysRegDepth(Reg) != 0 || - !isRegisterKill(Reg, MI)) - return false; - - migrateToEStack(Reg, *Loc); - return true; -} - -MachineInstr *StackModel::storeRegToPhysStack(const Register &Reg, - MIIter Insert) { - // Check if the reg is already located in either X or L stack. - // If so, just return its location. - const auto LocOpt = getStackLocation(Reg); - - const StackLocation &Loc = LocOpt ? *LocOpt : allocateStackModelLoc(Reg); - unsigned Depth = getPhysRegDepth(Loc); - assert(Depth > 0 && Loc.Type != StackType::E); - - // Perform migration E -> L/X stack. - // If the reg should be put on a newly created L/X stack top location, - // just do nothing. - if (Loc.Depth == 0 && !LocOpt) { - assert(EStack.size() == 1); - EStack.pop_back(); - return &*Insert; - } - return storeRegToPhysStackAt(Depth, Insert->getParent(), std::next(Insert), - Insert->getDebugLoc()); -} - -MachineInstr *StackModel::storeRegToPhysStackAt(unsigned Depth, - MachineBasicBlock *MBB, - MIIter Insert, - const DebugLoc &DL) { - // Use the SWAP + POP instructions pair to store the register - // in the physical stack on the given depth. - const unsigned SWAPOpc = getSWAPOpcode(Depth); - Insert = BuildMI(*MBB, Insert, DL, TII->get(SWAPOpc)); - Insert = BuildMI(*MBB, std::next(Insert), DL, TII->get(EVM::POP)); - EStack.pop_front(); - return &*Insert; -} - -StackLocation StackModel::pushRegToStackModel(StackType Type, - const Register &Reg) { - StackLocation Loc{Type, 0}; - auto &Stack = getStack(Type); - assert(std::find(Stack.begin(), Stack.end(), Reg) == std::end(Stack)); - Stack.push_front(Reg); - return Loc; -} - -StackLocation StackModel::allocateStackModelLoc(const Register &Reg) { - // We need to add the register to either X or L-stack, depending - // on reg's live interval. The register goes to X stack if its live interval - // spawns across a BB boundary, otherwise put it to L-stack. - const auto *LI = &LIS.getInterval(Reg); - if (LIS.intervalIsInOneMBB(*LI)) - return pushRegToStackModel(StackType::L, Reg); - - return pushRegToStackModel(StackType::X, Reg); -} - -void StackModel::handleArgument(MachineInstr *MI) { - const auto ArgIdx = MI->getOperand(1).getImm(); - // 1 - because of the return address. - unsigned Depth = 1 + static_cast(ArgIdx) + getStackSize(); - loadRegToPhysStackTop(Depth, MI); - storeRegToPhysStack(MI->getOperand(0).getReg(), MI); - ToErase.push_back(MI); -} - -void StackModel::handleLStackAtJump(MachineBasicBlock *MBB, MachineInstr *MI, - const Register &Reg) { - unsigned PseudoJumpOpc = 0; - if (MI->getOpcode() == EVM::JUMP) - PseudoJumpOpc = EVM::PseudoJUMP; - else if (MI->getOpcode() == EVM::JUMPI) - PseudoJumpOpc = EVM::PseudoJUMPI; - else if (MI->getOpcode() == EVM::JUMP_UNLESS) - PseudoJumpOpc = EVM::PseudoJUMP_UNLESS; - else - llvm_unreachable("Unexpected jump instruction"); - - // If the condition register is in the L-stack, we need to move it to - // the bottom of the L-stack. After that we should clean clean the L-stack. - // In case of an unconditional jump, the Reg value should be - // EVM::NoRegister. - clearPhysStackAtInst(StackType::L, MI, Reg); - - // Insert pseudo jump instruciton that will be replaced with PUSH and JUMP - // instructions in AsmPrinter. - ToErase.push_back(MI); - BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(PseudoJumpOpc)) - .addMBB(MBB); -} - -void StackModel::handleCondJump(MachineInstr *MI) { - MachineBasicBlock *TargetMBB = MI->getOperand(0).getMBB(); - const Register &CondReg = MI->getOperand(1).getReg(); - loadRegToPhysStackTop(CondReg, MI); - handleLStackAtJump(TargetMBB, MI, CondReg); -} - -void StackModel::handleJump(MachineInstr *MI) { - MachineBasicBlock *TargetMBB = MI->getOperand(0).getMBB(); - handleLStackAtJump(TargetMBB, MI, EVM::NoRegister); -} - -void StackModel::handleReturn(MachineInstr *MI) { - ToErase.push_back(MI); - BuildMI(*MI->getParent(), std::next(MIIter(MI)), DebugLoc(), - TII->get(EVM::PseudoRET)); - - // Collect the use registers of the RET instruction. - SmallVector ReturnRegs; - for (const auto &MO : MI->explicit_uses()) { - assert(MO.isReg()); - ReturnRegs.push_back(MO.getReg()); - } - - auto *MFI = MF->getInfo(); - if (MFI->getNumParams() >= ReturnRegs.size()) { - // Move the return registers to the stack location where - // arguments were resided. - unsigned RegCount = 0; - for (const auto &Reg : reverse(ReturnRegs)) { - loadRegToPhysStackTop(Reg, MI); - unsigned Depth = getStackSize() + MFI->getNumParams() - RegCount; - storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); - ++RegCount; - } - - if (MFI->getNumParams() != ReturnRegs.size()) { - // Now move the return address to the final location. - loadRegToPhysStackTop(getStackSize(), MI); - storeRegToPhysStackAt(getStackSize() + MFI->getNumParams() - RegCount, - MI->getParent(), MI, MI->getDebugLoc()); - } - - // Clear the phys stack. - // Save both NumFrameObjs and X-tack model to restore after clearing the - // stack, because this BB may not be the last in the CFG layout. - std::deque CopyXStack = XStack; - unsigned CopyNumFrameObjs = NumFrameObjs; - - // First clear the L and X areas. - clearPhysStackAtInst(StackType::X, MI, EVM::NoRegister); - clearFrameObjsAtInst(MI); - // Then clear a stack part corresponding to the arguments area. - unsigned NumSlotsToPop = MFI->getNumParams() - RegCount; - while (NumSlotsToPop--) - BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::POP)); - - XStack = CopyXStack; - NumFrameObjs = CopyNumFrameObjs; - } else { - // Load return address to the top, as its old location will be overwritten. - loadRegToPhysStackTop(getStackSize(), MI); - - // Move the last 'MFI->getNumParams() + 1' return registers to the final - // locations. - // (1 - because we also overwrite the return address stack location) - for (unsigned RegCount = 0; RegCount < MFI->getNumParams() + 1; - ++RegCount) { - loadRegToPhysStackTop(ReturnRegs.pop_back_val(), MI); - unsigned Depth = getStackSize() + MFI->getNumParams() - RegCount; - storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); - } - - // Save X-tack model and restore it later, because this BB may not be the - // last in the CFG layout. - std::deque CopyXStack = XStack; - unsigned CopyNumFrameObjs = NumFrameObjs; - - // Copy all the remaining return registers to the top - // locations of the stack. - for (const auto &Reg : ReturnRegs) - loadRegToPhysStackTop(Reg, MI); - - // Now copy them and the return address to the final locations. - for (unsigned RegCount = 0; RegCount < ReturnRegs.size() + 1; ++RegCount) { - unsigned Depth = getStackSize() - RegCount - 1; - storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); - } - - // Clear the phys stack. - assert(getStackSize() >= ReturnRegs.size() + 1); - unsigned NumSlotsToPop = getStackSize() - ReturnRegs.size() - 1; - while (NumSlotsToPop--) - BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::POP)); - - LStack.clear(); - XStack = CopyXStack; - NumFrameObjs = CopyNumFrameObjs; - } -} - -void StackModel::handleStackLoad(MachineInstr *MI) { - APInt Offset = MI->getOperand(2).getCImm()->getValue(); - assert((Offset.getZExtValue() % 32) == 0); - unsigned Depth = - getStackSize() - static_cast(Offset.getZExtValue() / 32) - 1; - BuildMI(*MI->getParent(), *MI, MI->getDebugLoc(), - TII->get(getDUPOpcode(Depth))); - ToErase.push_back(MI); -} - -void StackModel::handleStackStore(MachineInstr *MI) { - APInt Offset = MI->getOperand(1).getCImm()->getValue(); - assert((Offset.getZExtValue() % 32) == 0); - unsigned Depth = - getStackSize() - static_cast(Offset.getZExtValue() / 32) - 1; - const unsigned SWAPOpc = getSWAPOpcode(Depth); - BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(SWAPOpc)); - BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(EVM::POP)); - ToErase.push_back(MI); -} - -void StackModel::handleCall(MachineInstr *MI) { - // Calling convention: caller first pushes arguments then the return address. - // Callee removes them form the stack and pushes return values. - - MachineBasicBlock &MBB = *MI->getParent(); - - // Add symbol just after the jump that will be used as the return - // address from the function. - MCSymbol *RetSym = MF->getContext().createTempSymbol("FUNC_RET", true); - - // Create pseudo jump to the callee, that will be expanded into PUSH, JUMP - // return label and JUMPDEST instructions in the AsmPrinter. - const MachineOperand *CalleeOp = MI->explicit_uses().begin(); - assert(CalleeOp->isGlobal()); - MIIter It = BuildMI(MBB, MI, MI->getDebugLoc(), TII->get(EVM::PseudoCALL)) - .addGlobalAddress(CalleeOp->getGlobal()) - .addSym(RetSym); - - // Create push of the return address. - BuildMI(MBB, It, MI->getDebugLoc(), TII->get(EVM::PUSH_LABEL)).addSym(RetSym); -} - -void StackModel::clearFrameObjsAtInst(MachineInstr *MI) { - while (NumFrameObjs--) { - BuildMI(*MI->getParent(), MI, DebugLoc(), TII->get(EVM::POP)); - } -} - -void StackModel::clearPhysStackAtInst(StackType TargetStackType, - MachineInstr *MI, const Register &Reg) { - auto &Stack = getStack(TargetStackType); - // Completely clear the phys stack till the target stack. - if (Reg == EVM::NoRegister) { - if (TargetStackType == StackType::X) - peelPhysStack(StackType::L, LStack.size(), MI->getParent(), MI); - - peelPhysStack(TargetStackType, Stack.size(), MI->getParent(), MI); - return; - } - - // If the X/L-stack is empty, that means the reg is located in the E-stack, - // that means no need to store it deeper, as it can be directly consumed by - // the MI. - if (Stack.empty()) { - assert(EStack.size() == 1); - EStack.pop_front(); - return; - } - - // Assign the Reg (in terms of the stack model) to the bottom of the - // L or X stack. - StackLocation NewLoc = {TargetStackType, - static_cast(Stack.size() - 1)}; - - // Store the Reg to the physical stack at the new location. - unsigned Depth = getPhysRegDepth(NewLoc); - storeRegToPhysStackAt(Depth, MI->getParent(), MI, MI->getDebugLoc()); - assert(EStack.empty()); - - // If the Reg is placed at the bottom of the X stack, first we need to - // completely clear L stack. - if (TargetStackType == StackType::X) - peelPhysStack(StackType::L, LStack.size(), MI->getParent(), MI); - - // Remove all stack items, but the last one from the target stack. - peelPhysStack(TargetStackType, NewLoc.Depth, MI->getParent(), MI); - - // Pop CondReg from the L-stack, as JUMPI consumes CondReg. - Stack.pop_front(); -} - -void StackModel::peelPhysStack(StackType Type, unsigned NumItems, - MachineBasicBlock *BB, MIIter Pos) { - auto &Stack = getStack(Type); - while (NumItems--) { - BuildMI(*BB, Pos, DebugLoc(), TII->get(EVM::POP)); - Stack.pop_front(); - } -} - -void StackModel::allocateXStack() { - MachineRegisterInfo &MRI = MF->getRegInfo(); - MachineBasicBlock &Entry = MF->front(); - for (unsigned I = 0; I < MRI.getNumVirtRegs(); ++I) { - const Register &Reg = Register::index2VirtReg(I); - const LiveInterval *LI = &LIS.getInterval(Reg); - if (LI->empty()) - continue; - - if (!LIS.intervalIsInOneMBB(*LI)) { - LLVM_DEBUG(dbgs() << "\tallocation X-stack for: " << *LI << '\n'); - BuildMI(Entry, Entry.begin(), DebugLoc(), TII->get(EVM::PUSH0)); - XStack.push_front(Reg); - } - } -} - -void StackModel::allocateFrameObjects() { - MachineBasicBlock &Entry = MF->front(); - const MachineFrameInfo &MFI = MF->getFrameInfo(); - unsigned FrameSize = 0; - for (unsigned I = 0, E = MFI.getObjectIndexEnd(); I != E; ++I) { - // We don't have variable sized objects that have size 0. - assert(MFI.getObjectSize(I)); - FrameSize += static_cast(MFI.getObjectSize(I)); - } - assert((FrameSize % 32) == 0); - - NumFrameObjs = FrameSize / 32; - for (unsigned I = 0; I < NumFrameObjs; ++I) { - LLVM_DEBUG(dbgs() << "\tallocating frame object\n"); - BuildMI(Entry, Entry.begin(), DebugLoc(), TII->get(EVM::PUSH0)); - } -} - -void StackModel::preProcess() { - assert(!MF->empty()); - allocateFrameObjects(); - allocateXStack(); -} - -// 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 || RegOpcode == EVM::PseudoJUMP || - RegOpcode == EVM::PseudoJUMPI || RegOpcode == EVM::PseudoJUMP_UNLESS || - RegOpcode == EVM::PseudoCALL || RegOpcode == EVM::PseudoRET) - 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) { - if (MI.getOpcode() == EVM::CONST_I256) { - // Replace with PUSH* opcode and remove register operands - const APInt Imm = MI.getOperand(1).getCImm()->getValue(); - unsigned Opc = EVM::getPUSHOpcode(Imm); - MI.setDesc(TII->get(Opc)); - MI.removeOperand(0); - if (Opc == EVM::PUSH0) - MI.removeOperand(0); - } else if (MI.getOpcode() == EVM::COPY_I256) { - // Just remove the copy instruction, as the code that - // loads the argument and stores the result performs copying. - // TODO: POP_KILL should be handled before stackification pass. - ToErase.push_back(&MI); - } else if (MI.getOpcode() == EVM::FCALL) { - handleCall(&MI); - ToErase.push_back(&MI); - } - } - } - - 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(); -} - -void StackModel::dumpState() const { -#ifndef NDEBUG - LLVM_DEBUG(dbgs() << "X: " << XStack.size() << ", L: " << LStack.size() - << ", E: " << EStack.size() << '\n'); - for (const auto &Reg : LStack) { - const auto Loc = *getStackLocation(Reg); - LLVM_DEBUG(dbgs() << "reg: " << Reg - << ", Type: " << static_cast(Loc.Type) - << ", Depth: " << getPhysRegDepth(Reg) << '\n'); - } - for (const auto &Reg : XStack) { - const auto Loc = *getStackLocation(Reg); - LLVM_DEBUG(dbgs() << "reg: " << Reg - << ", Type: " << static_cast(Loc.Type) - << ", Depth: " << getPhysRegDepth(Reg) << '\n'); - } - LLVM_DEBUG(dbgs() << '\n'); -#endif -} - -unsigned StackModel::getDUPOpcode(unsigned Depth) { - assert(Depth < StackAccessDepthLimit); - switch (Depth) { - case 0: - return EVM::DUP1; - case 1: - return EVM::DUP2; - case 2: - return EVM::DUP3; - case 3: - return EVM::DUP4; - case 4: - return EVM::DUP5; - case 5: - return EVM::DUP6; - case 6: - return EVM::DUP7; - case 7: - return EVM::DUP8; - case 8: - return EVM::DUP9; - case 9: - return EVM::DUP10; - case 10: - return EVM::DUP11; - case 11: - return EVM::DUP12; - case 12: - return EVM::DUP13; - case 13: - return EVM::DUP14; - case 14: - return EVM::DUP15; - case 15: - return EVM::DUP16; - default: - llvm_unreachable("Unexpected stack depth"); - } -} - -unsigned StackModel::getSWAPOpcode(unsigned Depth) { - assert(Depth < StackAccessDepthLimit); - switch (Depth) { - case 1: - return EVM::SWAP1; - case 2: - return EVM::SWAP2; - case 3: - return EVM::SWAP3; - case 4: - return EVM::SWAP4; - case 5: - return EVM::SWAP5; - case 6: - return EVM::SWAP6; - case 7: - return EVM::SWAP7; - case 8: - return EVM::SWAP8; - case 9: - return EVM::SWAP9; - case 10: - return EVM::SWAP10; - case 11: - return EVM::SWAP11; - case 12: - return EVM::SWAP12; - case 13: - return EVM::SWAP13; - case 14: - return EVM::SWAP14; - case 15: - return EVM::SWAP15; - case 16: - return EVM::SWAP16; - default: - llvm_unreachable("Unexpected stack depth"); - } -} - -namespace { -class EVMStackify final : public MachineFunctionPass { -public: - static char ID; // Pass identification, replacement for typeid - EVMStackify() : MachineFunctionPass(ID) {} - -private: - StringRef getPassName() const override { return "EVM stackification"; } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesCFG(); - AU.addRequired(); - AU.addRequired(); - AU.addPreserved(); - AU.addPreserved(); - MachineFunctionPass::getAnalysisUsage(AU); - } - - MachineFunctionProperties getRequiredProperties() const override { - return MachineFunctionProperties().set( - MachineFunctionProperties::Property::TracksLiveness); - } - - bool runOnMachineFunction(MachineFunction &MF) override; - - void setStackOpcodes(MachineFunction &MF); -}; -} // end anonymous namespace - -char EVMStackify::ID = 0; -INITIALIZE_PASS(EVMStackify, DEBUG_TYPE, - "Insert stack manipulation instructions to execute the code in " - "stack environment", - false, false) - -FunctionPass *llvm::createEVMStackify() { return new EVMStackify(); } - -bool EVMStackify::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG({ - dbgs() << "********** Perform EVM stackification **********\n" - << "********** Function: " << MF.getName() << '\n'; - }); - - MachineRegisterInfo &MRI = MF.getRegInfo(); - const auto *TII = MF.getSubtarget().getInstrInfo(); - auto &LIS = getAnalysis().getLIS(); - - // We don't preserve SSA form. - MRI.leaveSSA(); - - assert(MRI.tracksLiveness() && "Stackify expects liveness"); - - LLVM_DEBUG(dbgs() << "ALL register intervals:\n"); - for (unsigned I = 0; I < MRI.getNumVirtRegs(); ++I) { - const Register &VReg = Register::index2VirtReg(I); - LiveInterval *LI = &LIS.getInterval(VReg); - if (LI->empty()) - continue; - - LIS.shrinkToUses(LI); - LLVM_DEBUG(LI->dump()); - if ([[maybe_unused]] const auto *BB = LIS.intervalIsInOneMBB(*LI)) { - LLVM_DEBUG(dbgs() << "\tIs live in: (" << BB->getName() << ")\n"); - } - } - LLVM_DEBUG(dbgs() << '\n'); - - StackModel Model(&MF, LIS, TII); - Model.preProcess(); - for (MachineBasicBlock &MBB : MF) { - Model.enterBB(&MBB); - for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) { - LLVM_DEBUG(dbgs() << MI); - Model.handleInstruction(&MI); - Model.dumpState(); - } - Model.leaveBB(&MBB); - } - Model.postProcess(); - - return true; -} diff --git a/llvm/lib/Target/EVM/EVMTargetMachine.cpp b/llvm/lib/Target/EVM/EVMTargetMachine.cpp index 012e07dfcb6b..16a512d1dcd4 100644 --- a/llvm/lib/Target/EVM/EVMTargetMachine.cpp +++ b/llvm/lib/Target/EVM/EVMTargetMachine.cpp @@ -36,13 +36,9 @@ using namespace llvm; // when assembly printing. cl::opt EVMKeepRegisters("evm-keep-registers", cl::Hidden, - cl::desc("EVM: output stack registers in" - " instruction output for test purposes only."), - cl::init(false)); -cl::opt - EVMUseLocalStakify("evm-use-local-stackify", cl::Hidden, - cl::desc("EVM: use the local stackification algorithm"), - cl::init(false)); + cl::desc("EVM: output stack registers in" + " instruction output for test purposes only."), + cl::init(false)); extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { // Register the target. @@ -54,10 +50,8 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMTarget() { initializeEVMLowerIntrinsicsPass(PR); initializeEVMOptimizeLiveIntervalsPass(PR); initializeEVMPeepholePass(PR); - initializeEVMRegColoringPass(PR); initializeEVMSingleUseExpressionPass(PR); initializeEVMSplitCriticalEdgesPass(PR); - initializeEVMStackifyPass(PR); initializeEVMBPStackificationPass(PR); initializeEVMAAWrapperPassPass(PR); initializeEVMExternalAAWrapperPass(PR); @@ -275,15 +269,9 @@ void EVMPassConfig::addPreEmitPass() { addPass(createEVMSplitCriticalEdges()); addPass(createEVMOptimizeLiveIntervals()); addPass(createEVMSingleUseExpression()); - if (EVMUseLocalStakify) { - // Run the register coloring pass to reduce the total number of registers. - addPass(createEVMRegColoring()); - addPass(createEVMStackify()); - } else { - addPass(createEVMBPStackification()); - addPass(&StackSlotColoringID); - addPass(createEVMFinalizeStackFrames()); - } + addPass(createEVMBPStackification()); + addPass(&StackSlotColoringID); + addPass(createEVMFinalizeStackFrames()); // Optimize branch instructions after stackification. This is done again // here, since EVMSplitCriticalEdges may introduce new BBs that could From 0ebd4cc18571f81540fefc081d7c99c1c36d5f7e Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Mon, 15 Sep 2025 09:43:51 +0200 Subject: [PATCH 196/203] [EVM] NFC: Reformat EVMSingleUseExpression pass --- .../lib/Target/EVM/EVMSingleUseExpression.cpp | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index 3c04ae94ecff..03eefe54ce02 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -67,13 +67,13 @@ FunctionPass *llvm::createEVMSingleUseExpression() { // the expression stack. static void imposeStackOrdering(MachineInstr *MI) { // Write the opaque VALUE_STACK register. - if (!MI->definesRegister(EVM::VALUE_STACK, /*TRI*/nullptr)) + if (!MI->definesRegister(EVM::VALUE_STACK, /*TRI*/ nullptr)) MI->addOperand(MachineOperand::CreateReg(EVM::VALUE_STACK, /*isDef=*/true, /*isImp=*/true)); // Also read the opaque VALUE_STACK register. - if (!MI->readsRegister(EVM::VALUE_STACK, /*TRI*/nullptr)) + if (!MI->readsRegister(EVM::VALUE_STACK, /*TRI*/ nullptr)) MI->addOperand(MachineOperand::CreateReg(EVM::VALUE_STACK, /*isDef=*/false, /*isImp=*/true)); @@ -207,8 +207,7 @@ static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI, // Test whether Def is safe and profitable to rematerialize. static bool shouldRematerialize(const MachineInstr &Def, const MachineRegisterInfo &MRI, - LiveIntervals &LIS, - const EVMInstrInfo *TII) { + LiveIntervals &LIS, const EVMInstrInfo *TII) { // FIXME: remateralization of the CALLDATALOAD and ADD instructions // is just an ad-hoc solution to more aggressively form single use expressions // to eventually decrease runtime stack height, but this can significantly @@ -319,8 +318,8 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use, Register Reg = MO.getReg(); // If the register is dead here and at Insert, ignore it. - if (MO.isDead() && Insert->definesRegister(Reg, /*TRI*/nullptr) && - !Insert->readsRegister(Reg, /*TRI*/nullptr)) + if (MO.isDead() && Insert->definesRegister(Reg, /*TRI*/ nullptr) && + !Insert->readsRegister(Reg, /*TRI*/ nullptr)) continue; if (Register::isPhysicalRegister(Reg)) { @@ -677,25 +676,23 @@ bool EVMSingleUseExpression::runOnMachineFunction(MachineFunction &MF) { // supports intra-block moves) and it's MachineSink's job to catch all // the sinking opportunities anyway. bool SameBlock = DefI->getParent() == &MBB; - bool CanMove = SameBlock && - isSafeToMove(Def, &Use, Insert, MFI, MRI) && + bool CanMove = SameBlock && isSafeToMove(Def, &Use, Insert, MFI, MRI) && !TreeWalker.isOnStack(Reg); if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) { - Insert = - moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); + Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); } else if (shouldRematerialize(*DefI, MRI, LIS, TII)) { if (DefI->getOpcode() == EVM::CALLDATALOAD) { - MachineInstr *OffsetI = getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, - LIS); + MachineInstr *OffsetI = + getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, LIS); assert(OffsetI); Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, - Insert->getIterator(), LIS, MFI, - MRI, TII, TRI); + Insert->getIterator(), LIS, MFI, MRI, + TII, TRI); MachineOperand &OffsetMO = Insert->getOperand(1); - Insert = rematerializeCheapDef(OffsetMO.getReg(), OffsetMO, *OffsetI, MBB, - Insert->getIterator(), LIS, MFI, - MRI, TII, TRI); + Insert = rematerializeCheapDef(OffsetMO.getReg(), OffsetMO, + *OffsetI, MBB, Insert->getIterator(), + LIS, MFI, MRI, TII, TRI); } else if (DefI->getOpcode() == EVM::ADD) { MachineInstr *DefI1 = getVRegDef(DefI->getOperand(1).getReg(), DefI, MRI, LIS); @@ -729,8 +726,8 @@ bool EVMSingleUseExpression::runOnMachineFunction(MachineFunction &MF) { LIS, MFI, MRI, TII, TRI); } else { Insert = rematerializeCheapDef(Reg, Use, *DefI, MBB, - Insert->getIterator(), LIS, MFI, - MRI, TII, TRI); + Insert->getIterator(), LIS, MFI, MRI, + TII, TRI); } } else { // We failed to stackify the operand. If the problem was ordering From 43ce0ccc4b609ca74e1f532a992c4b5c7ff65a87 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Mon, 15 Sep 2025 10:20:12 +0200 Subject: [PATCH 197/203] [EVM] Don't rematerialize CALLDATALOAD when it redefines a VR The patch fixes solx #136 issue. --- .../lib/Target/EVM/EVMSingleUseExpression.cpp | 5 +- ...ngle-use-expressions-rematerialization.mir | 61 +++++++++++++++++++ .../CodeGen/EVM/solx-issue-136-bugpoint.ll | 54 ++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir create mode 100644 llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll diff --git a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp index 03eefe54ce02..1a0d180075e4 100644 --- a/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp +++ b/llvm/lib/Target/EVM/EVMSingleUseExpression.cpp @@ -213,7 +213,10 @@ static bool shouldRematerialize(const MachineInstr &Def, // to eventually decrease runtime stack height, but this can significantly // increase a code size. unsigned Opcode = Def.getOpcode(); - if (Opcode == EVM::CALLDATALOAD) { + if (Opcode == EVM::CALLDATALOAD && + // Don't rematerialize CALLDATALOAD which redefines the register it uses, + // as the use will no longer contribute to the register pressure. + Def.getOperand(0).getReg() != Def.getOperand(1).getReg()) { MachineInstr *DefI = getVRegDef(Def.getOperand(1).getReg(), &Def, MRI, LIS); if (!DefI) return false; diff --git a/llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir b/llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir new file mode 100644 index 000000000000..b9de7053c4b8 --- /dev/null +++ b/llvm/test/CodeGen/EVM/single-use-expressions-rematerialization.mir @@ -0,0 +1,61 @@ +# RUN: llc -x mir -run-pass=evm-single-use-expressions < %s | FileCheck %s + +--- | + ; ModuleID = 'rematerialize_calldataload.ll' + source_filename = "rematerialize_calldataload.ll" + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm-unknown-unknown" + + define void @dont_rematerialize_redefinition() { + ret void + } + + define void @rematerialize_calldataload() { + ret void + } + +... +--- +# CHECK-LABEL: name: dont_rematerialize_redefinition +# CHECK: CALLDATALOAD %{{[0-9]+}} +# CHECK-NOT: CALLDATALOAD %{{[0-9]+}} +name: dont_rematerialize_redefinition +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + + %0:gpr = ARGUMENT 0, implicit $arguments + %2:gpr = CONST_I256 i256 1, implicit-def dead $arguments + JUMP %bb.2, implicit-def $arguments + + bb.1: + %4:gpr = MUL %2, %0, implicit-def dead $arguments + RET %4, implicit-def dead $arguments + + bb.2: + %2:gpr = CALLDATALOAD %2, implicit-def dead $arguments + %4:gpr = ADD %2, %2, implicit-def dead $arguments + JUMP %bb.1, implicit-def $arguments + +... +--- +# CHECK-LABEL: name: rematerialize_calldataload +# CHECK: [[K1:%[0-9]+]]:gpr = CONST_I256 i256 1 +# CHECK-NEXT: [[L1:%[0-9]+]]:gpr = CALLDATALOAD [[K1]] +# CHECK-NEXT: [[K2:%[0-9]+]]:gpr = CONST_I256 i256 1 +# CHECK-NEXT: [[L2:%[0-9]+]]:gpr = CALLDATALOAD [[K2]] +# CHECK-NEXT: [[SUM:%[0-9]+]]:gpr = ADD [[L2]], [[L1]] +name: rematerialize_calldataload +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + + %0:gpr = ARGUMENT 0, implicit $arguments + %2:gpr = CONST_I256 i256 1, implicit-def dead $arguments + %3:gpr = CALLDATALOAD %2, implicit-def dead $arguments + %4:gpr = ADD %3, %3, implicit-def dead $arguments + RET %4, implicit-def dead $arguments + +... diff --git a/llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll b/llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll new file mode 100644 index 000000000000..90512a4d0579 --- /dev/null +++ b/llvm/test/CodeGen/EVM/solx-issue-136-bugpoint.ll @@ -0,0 +1,54 @@ +; RUN: llc < %s + +; EVMSingleUseExpression rematerializes CALLDATALOAD when loading from a +; constant address. +; Before the fix, this broke when CALLDATALOAD redefined its input virtual +; register. +; Reduced from issue #136; minimized with bugpoint. +; https://github.com/matter-labs/solx/issues/136 +; Triggers the coalescer to coalesce the CALLDATALOAD def and its use. +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm-unknown-unknown" + +define void @__entry() { +entry: + switch i32 poison, label %if_join [ + i32 306754293, label %switch_case_branch_1_block + i32 1826699332, label %switch_case_branch_2_block + i32 -974522369, label %switch_case_branch_3_block + i32 -171475469, label %switch_case_branch_4_block + ] + +if_join: ; preds = %entry + unreachable + +switch_case_branch_1_block: ; preds = %entry + unreachable + +switch_case_branch_2_block: ; preds = %entry + unreachable + +switch_case_branch_3_block: ; preds = %entry + %calldata_load_result.i = load i256, ptr addrspace(2) inttoptr (i256 4 to ptr addrspace(2)), align 4 + %calldata_load_result.i51 = load i256, ptr addrspace(2) inttoptr (i256 68 to ptr addrspace(2)), align 4 + br i1 poison, label %switch_join_block.i, label %checked_div_t_int256.exit.i + +switch_join_block.i: ; preds = %switch_case_branch_3_block + %comparison_result6.i.i = icmp eq i256 %calldata_load_result.i51, -1 + %comparison_result9.i.i = icmp eq i256 %calldata_load_result.i, -57896044618658097711785492504343953926634992332820282019728792003956564819968 + %and_result21.i.i = and i1 %comparison_result9.i.i, %comparison_result6.i.i + br i1 %and_result21.i.i, label %if_main12.i.i, label %checked_div_t_int256.exit.i + +if_main12.i.i: ; preds = %switch_join_block.i + unreachable + +checked_div_t_int256.exit.i: ; preds = %switch_join_block.i, %switch_case_branch_3_block + %expr_38.0.i7078 = phi i256 [ %calldata_load_result.i51, %switch_join_block.i ], [ 68, %switch_case_branch_3_block ] + %division_signed_result_non_zero.i.i = sdiv i256 %calldata_load_result.i, %expr_38.0.i7078 + %remainder_signed_result_non_zero.i.i = srem i256 %division_signed_result_non_zero.i.i, %calldata_load_result.i + store i256 %remainder_signed_result_non_zero.i.i, ptr addrspace(1) inttoptr (i256 128 to ptr addrspace(1)), align 128 + unreachable + +switch_case_branch_4_block: ; preds = %entry + unreachable +} From 6ebf013220544e9a0ef86b6196ef25dc9e1f38f3 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Sun, 14 Sep 2025 22:26:36 +0200 Subject: [PATCH 198/203] [EVM] Include read-only section size in module size calculation --- .../lib/Target/EVM/EVMCalculateModuleSize.cpp | 14 ++++ llvm/unittests/Target/EVM/BytecodeSize.cpp | 66 +++++++++++++++---- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp b/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp index 9d6b9d97c96f..b0ee0db5603e 100644 --- a/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp +++ b/llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp @@ -123,6 +123,18 @@ uint64_t llvm::EVM::calculateFunctionCodeSize(const MachineFunction &MF) { return Size; } +static uint64_t calculateReadOnlyDataSize(const Module &M) { + uint64_t Size = 0; + for (const GlobalVariable &GV : M.globals()) { + if (GV.getAddressSpace() != EVMAS::AS_CODE || !GV.hasInitializer()) + continue; + + if (const auto *CV = dyn_cast(GV.getInitializer())) + Size += CV->getRawDataValues().size(); + } + return Size; +} + uint64_t llvm::EVM::calculateModuleCodeSize(Module &M, const MachineModuleInfo &MMI) { uint64_t TotalSize = 0; @@ -133,6 +145,8 @@ uint64_t llvm::EVM::calculateModuleCodeSize(Module &M, TotalSize += llvm::EVM::calculateFunctionCodeSize(*MF); } + // Take into account the read-only data that we append to the .text section. + TotalSize += calculateReadOnlyDataSize(M); // Take into account INVALID instruction at the end of the .text section. TotalSize++; return TotalSize; diff --git a/llvm/unittests/Target/EVM/BytecodeSize.cpp b/llvm/unittests/Target/EVM/BytecodeSize.cpp index e382e7926a8a..d012ecf3a54d 100644 --- a/llvm/unittests/Target/EVM/BytecodeSize.cpp +++ b/llvm/unittests/Target/EVM/BytecodeSize.cpp @@ -32,11 +32,10 @@ std::unique_ptr createTargetMachine() { class BytecodeSizeTest : public testing::Test { protected: static const char *MIRString; + static const char *MIRStringReadOnlyData; LLVMContext Context; std::unique_ptr TM; std::unique_ptr MMI; - std::unique_ptr Parser; - std::unique_ptr M; static void SetUpTestCase() { LLVMInitializeEVMTargetInfo(); @@ -44,33 +43,40 @@ class BytecodeSizeTest : public testing::Test { LLVMInitializeEVMTargetMC(); } - void SetUp() override { - TM = createTargetMachine(); - std::unique_ptr MBuffer = - MemoryBuffer::getMemBuffer(MIRString); - Parser = createMIRParser(std::move(MBuffer), Context); + std::unique_ptr parseMIR(StringRef MIR) { + std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIR); + std::unique_ptr Parser = + createMIRParser(std::move(MBuffer), Context); if (!Parser) report_fatal_error("null MIRParser"); - M = Parser->parseIRModule(); + std::unique_ptr M = Parser->parseIRModule(); if (!M) report_fatal_error("parseIRModule failed"); M->setTargetTriple(TM->getTargetTriple().getTriple()); M->setDataLayout(TM->createDataLayout()); - MMI = std::make_unique(TM.get()); if (Parser->parseMachineFunctions(*M, *MMI)) report_fatal_error("parseMachineFunctions failed"); + + return M; + } + + void SetUp() override { + TM = createTargetMachine(); + MMI = std::make_unique(TM.get()); } }; TEST_F(BytecodeSizeTest, Test) { - uint64_t Size = EVM::calculateModuleCodeSize(*M, *MMI); - ASSERT_TRUE(Size == 131); + std::unique_ptr M(parseMIR(MIRString)); + ASSERT_TRUE(EVM::calculateModuleCodeSize(*M, *MMI) == 131); + + std::unique_ptr M2(parseMIR(MIRStringReadOnlyData)); + // 23 = 21 (global variable initializers) + JUMPDEST + INVALID + ASSERT_TRUE(EVM::calculateModuleCodeSize(*M2, *MMI) == 23); } const char *BytecodeSizeTest::MIRString = R"MIR( --- | - ; ModuleID = '/Users/pavelkopyl/projects/mlab/era-compiler-tester/tests/llvm/evm/complex/egcd.ll' - source_filename = "/Users/pavelkopyl/projects/mlab/era-compiler-tester/tests/llvm/evm/complex/egcd.ll" target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" target triple = "evm-unknown-unknown" @@ -358,4 +364,38 @@ body: | ... )MIR"; +const char *BytecodeSizeTest::MIRStringReadOnlyData = R"MIR( +--- | + target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" + target triple = "evm" + + @code_const.1 = private unnamed_addr addrspace(4) constant [7 x i8] c"global1" + @code_const.2 = private unnamed_addr addrspace(4) constant [7 x i8] c"global2" + @code_const.3 = private unnamed_addr addrspace(4) constant [7 x i8] c"global3" + + ; The following globals are never generated and should not be taken into account. + @heap_const = private unnamed_addr addrspace(1) constant [7 x i8] c"invalid" + @code_global = addrspace(4) global i256 0 + @code_global_ext = external addrspace(4) global i256 + + define void @test() noreturn { + unreachable + } + +... +--- +name: test +alignment: 1 +tracksRegLiveness: true +machineFunctionInfo: + isStackified: true + numberOfParameters: 0 + hasPushDeployAddress: false +body: | + bb.0 (%ir-block.0): + liveins: $arguments, $value_stack + +... +)MIR"; + } // anonymous namespace From d6b7b6571e5f94ac800254d9f06ce498e7962773 Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 22 Sep 2025 14:56:59 +0200 Subject: [PATCH 199/203] [CGP] Add pre-commit test for Disable optimizeMemoryInst only for EraVM Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll | 157 +++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll diff --git a/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll b/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll new file mode 100644 index 000000000000..b94ab7b71bc1 --- /dev/null +++ b/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll @@ -0,0 +1,157 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes='require,function(codegenprepare)' < %s | FileCheck %s + +target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" +target triple = "evm" + +@glob = external addrspace(1) global i256 + +define i256 @test_as1(i256 %arg) { +; CHECK-LABEL: define i256 @test_as1( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[ADD:%.*]] = add i256 [[ARG]], 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[ADD]] to ptr addrspace(1) +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %add = add i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %add to ptr addrspace(1) + %load = load i256, ptr addrspace(1) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_as5(i256 %arg) { +; CHECK-LABEL: define i256 @test_as5( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[ADD:%.*]] = add i256 [[ARG]], 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[ADD]] to ptr addrspace(5) +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(5) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %add = add i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %add to ptr addrspace(5) + %load = load i256, ptr addrspace(5) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_no_as2(i256 %arg) { +; CHECK-LABEL: define i256 @test_no_as2( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[ADD:%.*]] = add i256 [[ARG]], 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[ADD]] to ptr addrspace(2) +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(2) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %add = add i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %add to ptr addrspace(2) + %load = load i256, ptr addrspace(2) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_no_scale(i256 %arg) { +; CHECK-LABEL: define i256 @test_no_scale( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[MUL:%.*]] = mul i256 [[ARG]], 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[MUL]] to ptr addrspace(1) +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %mul = mul i256 %arg, 4 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %mul to ptr addrspace(1) + %load = load i256, ptr addrspace(1) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load, %then ], [ 0, %entry ] + ret i256 %phi +} + +define i256 @test_no_glob(i256 %arg) { +; CHECK-LABEL: define i256 @test_no_glob( +; CHECK-SAME: i256 [[ARG:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[LOAD1:%.*]] = load i256, ptr addrspace(1) @glob, align 64 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 +; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] +; CHECK: [[THEN]]: +; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[LOAD1]] to ptr addrspace(1) +; CHECK-NEXT: [[LOAD2:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 +; CHECK-NEXT: br label %[[EXIT]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: [[PHI:%.*]] = phi i256 [ [[LOAD2]], %[[THEN]] ], [ 0, %[[ENTRY]] ] +; CHECK-NEXT: ret i256 [[PHI]] +; +entry: + %load1 = load i256, ptr addrspace(1) @glob, align 64 + %cmp = icmp ugt i256 %arg, 10 + br i1 %cmp, label %then, label %exit + +then: + %inttoptr = inttoptr i256 %load1 to ptr addrspace(1) + %load2 = load i256, ptr addrspace(1) %inttoptr, align 1 + br label %exit + +exit: + %phi = phi i256 [ %load2, %then ], [ 0, %entry ] + ret i256 %phi +} From 1e506a04503a888f66b1cd81899c8ad6d09ff11c Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 16 Sep 2025 16:33:37 +0200 Subject: [PATCH 200/203] [CGP] Disable optimizeMemoryInst only for EraVM This enables optimization for EVM and implements isLegalAddressingMode, allowing r + imm addressing mode for all address spaces except CALLDATALOAD. Benchmarks indicate that enabling it for CALLDATALOAD is not beneficial. This change primarily allows address mode calculations (r + imm) to be sunk to their uses, which can reduce spills and reloads in some cases. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMISelLowering.cpp | 19 +++++++++++++++++++ llvm/lib/Target/EVM/EVMISelLowering.h | 4 ++++ llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll | 8 ++++---- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMISelLowering.cpp b/llvm/lib/Target/EVM/EVMISelLowering.cpp index ecca571fb7b0..db460f8c4025 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.cpp +++ b/llvm/lib/Target/EVM/EVMISelLowering.cpp @@ -110,6 +110,25 @@ const char *EVMTargetLowering::getTargetNodeName(unsigned Opcode) const { return nullptr; } +bool EVMTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I) const { + // We don't support global and scaled addresses. + if (AM.BaseGV || AM.Scale) + return false; + + // For CALLDATALOAD, benchmark numbers showed it is not profitable to set + // that we support r + imm. + if (AS == EVMAS::AS_CALL_DATA) + return !(AM.HasBaseReg && AM.BaseOffs); + + // Allow r + imm addressing mode for other address spaces. This can help + // to reduce register pressure by sinking add with immediate to its uses, + // minimizing the need for extra registers and reducing spills and reloads. + return true; +} + //===----------------------------------------------------------------------===// // EVM Lowering private implementation. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/EVM/EVMISelLowering.h b/llvm/lib/Target/EVM/EVMISelLowering.h index 8158652cdb8a..deafb49ef160 100644 --- a/llvm/lib/Target/EVM/EVMISelLowering.h +++ b/llvm/lib/Target/EVM/EVMISelLowering.h @@ -40,6 +40,10 @@ class EVMTargetLowering final : public TargetLowering { /// DAG node. const char *getTargetNodeName(unsigned Opcode) const override; + bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I = nullptr) const override; + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, EVT VT) const override { return MVT::i256; diff --git a/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll b/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll index b94ab7b71bc1..31017d31a722 100644 --- a/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll +++ b/llvm/test/CodeGen/EVM/cgp-sink-addrmode.ll @@ -10,11 +10,11 @@ define i256 @test_as1(i256 %arg) { ; CHECK-LABEL: define i256 @test_as1( ; CHECK-SAME: i256 [[ARG:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: -; CHECK-NEXT: [[ADD:%.*]] = add i256 [[ARG]], 4 ; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 ; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] ; CHECK: [[THEN]]: -; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[ADD]] to ptr addrspace(1) +; CHECK-NEXT: [[SUNKADDR:%.*]] = inttoptr i256 [[ARG]] to ptr addrspace(1) +; CHECK-NEXT: [[INTTOPTR:%.*]] = getelementptr i8, ptr addrspace(1) [[SUNKADDR]], i256 4 ; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) [[INTTOPTR]], align 1 ; CHECK-NEXT: br label %[[EXIT]] ; CHECK: [[EXIT]]: @@ -40,11 +40,11 @@ define i256 @test_as5(i256 %arg) { ; CHECK-LABEL: define i256 @test_as5( ; CHECK-SAME: i256 [[ARG:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: -; CHECK-NEXT: [[ADD:%.*]] = add i256 [[ARG]], 4 ; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i256 [[ARG]], 10 ; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]] ; CHECK: [[THEN]]: -; CHECK-NEXT: [[INTTOPTR:%.*]] = inttoptr i256 [[ADD]] to ptr addrspace(5) +; CHECK-NEXT: [[SUNKADDR:%.*]] = inttoptr i256 [[ARG]] to ptr addrspace(5) +; CHECK-NEXT: [[INTTOPTR:%.*]] = getelementptr i8, ptr addrspace(5) [[SUNKADDR]], i256 4 ; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(5) [[INTTOPTR]], align 1 ; CHECK-NEXT: br label %[[EXIT]] ; CHECK: [[EXIT]]: From d66a7b3b2d5698e0104b873c74f703d50ee3314c Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Mon, 22 Sep 2025 17:13:53 +0200 Subject: [PATCH 201/203] [EVM] Set that staticcall can't change storage From the description, staticcall can't change storage, so update the code to ensure no storage modifications occur during staticcall. Signed-off-by: Vladimir Radosavljevic --- llvm/lib/Target/EVM/EVMAliasAnalysis.cpp | 2 +- llvm/test/CodeGen/EVM/aa-eval.ll | 4 +-- llvm/test/CodeGen/EVM/aa-memory-opt.ll | 37 +++++++++++------------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp index 00f97ce613e9..2d6ac3e58c74 100644 --- a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp @@ -109,6 +109,7 @@ ModRefInfo EVMAAResult::getModRefInfo(const CallBase *Call, unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace(); switch (II->getIntrinsicID()) { case Intrinsic::evm_return: + case Intrinsic::evm_staticcall: if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) return ModRefInfo::Ref; break; @@ -117,7 +118,6 @@ ModRefInfo EVMAAResult::getModRefInfo(const CallBase *Call, case Intrinsic::evm_call: case Intrinsic::evm_callcode: case Intrinsic::evm_delegatecall: - case Intrinsic::evm_staticcall: if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) return ModRefInfo::ModRef; break; diff --git a/llvm/test/CodeGen/EVM/aa-eval.ll b/llvm/test/CodeGen/EVM/aa-eval.ll index c474a47c5b20..32916b01cbe2 100644 --- a/llvm/test/CodeGen/EVM/aa-eval.ll +++ b/llvm/test/CodeGen/EVM/aa-eval.ll @@ -343,8 +343,8 @@ define i256 @test_as1_alias_call(ptr addrspace(1) %ptr1) { } ; CHECK-LABEL: Function: test_as6_as5_alias_staticcall -; CHECK: Both ModRef: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.staticcall -; CHECK: Both ModRef: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: Just Ref: Ptr: i256* %ptr1 <-> %ret = call i256 @llvm.evm.staticcall +; CHECK: Just Ref: Ptr: i256* %ptr2 <-> %ret = call i256 @llvm.evm.staticcall define i256 @test_as6_as5_alias_staticcall(ptr addrspace(5) %ptr1) { store i256 1, ptr addrspace(5) %ptr1, align 32 %ptr2 = inttoptr i256 32 to ptr addrspace(6) diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll index 35a4a14822ce..26988533c7f6 100644 --- a/llvm/test/CodeGen/EVM/aa-memory-opt.ll +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -285,6 +285,23 @@ define i256 @test_as6_large() { ret i256 %ret } +define i256 @test_opt_staticcall(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { +; CHECK-LABEL: define noundef i256 @test_opt_staticcall +; CHECK-SAME: (ptr addrspace(5) nocapture writeonly [[PTR1:%.*]], ptr addrspace(6) nocapture writeonly [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { +; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 +; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) +; CHECK-NEXT: ret i256 3 +; + store i256 1, ptr addrspace(5) %ptr1, align 32 + store i256 2, ptr addrspace(6) %ptr2, align 32 + call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) + %ret1 = load i256, ptr addrspace(5) %ptr1 + %ret2 = load i256, ptr addrspace(6) %ptr2 + %ret = add i256 %ret1, %ret2 + ret i256 %ret +} + ; Verify that in the following tests all load operations are preserved. define i256 @test_noopt_create(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { @@ -347,26 +364,6 @@ define i256 @test_noopt_call(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { ret i256 %ret } -define i256 @test_noopt_staticcall(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { -; CHECK-LABEL: define i256 @test_noopt_staticcall -; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { -; CHECK-NEXT: store i256 1, ptr addrspace(5) [[PTR1]], align 32 -; CHECK-NEXT: store i256 2, ptr addrspace(6) [[PTR2]], align 32 -; CHECK-NEXT: [[TMP1:%.*]] = tail call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) -; CHECK-NEXT: [[RET1:%.*]] = load i256, ptr addrspace(5) [[PTR1]], align 32 -; CHECK-NEXT: [[RET2:%.*]] = load i256, ptr addrspace(6) [[PTR2]], align 32 -; CHECK-NEXT: [[RET:%.*]] = add i256 [[RET2]], [[RET1]] -; CHECK-NEXT: ret i256 [[RET]] -; - store i256 1, ptr addrspace(5) %ptr1, align 32 - store i256 2, ptr addrspace(6) %ptr2, align 32 - call i256 @llvm.evm.staticcall(i256 1, i256 1, ptr addrspace(1) null, i256 1, ptr addrspace(1) null, i256 1) - %ret1 = load i256, ptr addrspace(5) %ptr1 - %ret2 = load i256, ptr addrspace(6) %ptr2 - %ret = add i256 %ret1, %ret2 - ret i256 %ret -} - define i256 @test_noopt_callcode(ptr addrspace(5) %ptr1, ptr addrspace(6) %ptr2) { ; CHECK-LABEL: define i256 @test_noopt_callcode ; CHECK-SAME: (ptr addrspace(5) nocapture [[PTR1:%.*]], ptr addrspace(6) nocapture [[PTR2:%.*]]) local_unnamed_addr #[[ATTR3]] { From 61620d9fcee1069743eeeb44dd71e4c7c811a00c Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Thu, 25 Sep 2025 13:51:54 +0200 Subject: [PATCH 202/203] [EVM] Add pre-commit tests for Set that gas intrinsic doesn't access storage memory Signed-off-by: Vladimir Radosavljevic --- llvm/test/CodeGen/EVM/aa-memory-opt.ll | 44 ++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll index 26988533c7f6..43dd2cb7eb13 100644 --- a/llvm/test/CodeGen/EVM/aa-memory-opt.ll +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -53,19 +53,51 @@ define i256 @test_different_locsizes() { ret i256 %ret } -define i256 @test_gas() { -; CHECK-LABEL: define i256 @test_gas +define i256 @test_gas_as1() { +; CHECK-LABEL: define i256 @test_gas_as1 ; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[LOAD]], [[GAS]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + %gas = call i256 @llvm.evm.gas() + %load = load i256, ptr addrspace(1) inttoptr (i256 2 to ptr addrspace(1)), align 64 + %ret = add i256 %load, %gas + ret i256 %ret +} + +define i256 @test_gas_as5() { +; CHECK-LABEL: define i256 @test_gas_as5 +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() -; CHECK-NEXT: store i256 [[GAS]], ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 -; CHECK-NEXT: [[RET:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[LOAD]], [[GAS]] ; CHECK-NEXT: ret i256 [[RET]] ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 %gas = call i256 @llvm.evm.gas() - store i256 %gas, ptr addrspace(1) inttoptr (i256 1 to ptr addrspace(1)), align 64 - %ret = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + %load = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 + %ret = add i256 %load, %gas + ret i256 %ret +} + +define i256 @test_gas_as6() { +; CHECK-LABEL: define i256 @test_gas_as6 +; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { +; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 +; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() +; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 +; CHECK-NEXT: [[RET:%.*]] = add i256 [[LOAD]], [[GAS]] +; CHECK-NEXT: ret i256 [[RET]] +; + store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + %gas = call i256 @llvm.evm.gas() + %load = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 + %ret = add i256 %load, %gas ret i256 %ret } From 7a9f81abe96ddb09344ba9de0c73b963b42f9abe Mon Sep 17 00:00:00 2001 From: Vladimir Radosavljevic Date: Tue, 23 Sep 2025 12:35:32 +0200 Subject: [PATCH 203/203] [EVM] Set that gas intrinsic doesn't access storage memory This will help optimizations that use alias analysis to search past it, and to optimize further. Benchmark numbers showed that this is not profitable to do for heap address space. Signed-off-by: Vladimir Radosavljevic --- llvm/include/llvm/IR/IntrinsicsEVM.td | 4 ++++ llvm/lib/Target/EVM/EVMAliasAnalysis.cpp | 7 +++++++ llvm/test/CodeGen/EVM/aa-memory-opt.ll | 8 ++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsEVM.td b/llvm/include/llvm/IR/IntrinsicsEVM.td index 3a29f8c076b9..e70e6ea9f824 100644 --- a/llvm/include/llvm/IR/IntrinsicsEVM.td +++ b/llvm/include/llvm/IR/IntrinsicsEVM.td @@ -108,6 +108,10 @@ def int_evm_msize : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; def int_evm_pc : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; +// TODO: #904: Set IntrInaccessibleMemOnly attribute to gas intrinsic. +// Currently, it is not set because benchmark numbers showed that it is not +// profitable to do that for heap address space. Revisit this decision once +// optimization for free ptr updates is implemented. def int_evm_gas : Intrinsic<[llvm_i256_ty], [], [IntrWillReturn]>; // Getting values from the context. diff --git a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp index 2d6ac3e58c74..e2e01221e06d 100644 --- a/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp +++ b/llvm/lib/Target/EVM/EVMAliasAnalysis.cpp @@ -108,6 +108,13 @@ ModRefInfo EVMAAResult::getModRefInfo(const CallBase *Call, unsigned AS = Loc.Ptr->getType()->getPointerAddressSpace(); switch (II->getIntrinsicID()) { + case Intrinsic::evm_gas: + // TODO: #904: Ideally, we would add IntrInaccessibleMemOnly attribute to + // gas intrinsic, but benchmark numbers showed that it is not profitable + // to do that for heap address space. + if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) + return ModRefInfo::NoModRef; + return ModRefInfo::ModRef; case Intrinsic::evm_return: case Intrinsic::evm_staticcall: if (AS == EVMAS::AS_STORAGE || AS == EVMAS::AS_TSTORAGE) diff --git a/llvm/test/CodeGen/EVM/aa-memory-opt.ll b/llvm/test/CodeGen/EVM/aa-memory-opt.ll index 43dd2cb7eb13..d4b598ec2b91 100644 --- a/llvm/test/CodeGen/EVM/aa-memory-opt.ll +++ b/llvm/test/CodeGen/EVM/aa-memory-opt.ll @@ -53,6 +53,8 @@ define i256 @test_different_locsizes() { ret i256 %ret } +; TODO: #904: It is safe to remove load in this test, but it is not done since benchmark +; numbers showed that it is not profitable to do that for heap address space. define i256 @test_gas_as1() { ; CHECK-LABEL: define i256 @test_gas_as1 ; CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { @@ -74,8 +76,7 @@ define i256 @test_gas_as5() { ; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 ; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() -; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 -; CHECK-NEXT: [[RET:%.*]] = add i256 [[LOAD]], [[GAS]] +; CHECK-NEXT: [[RET:%.*]] = add i256 [[GAS]], 2 ; CHECK-NEXT: ret i256 [[RET]] ; store i256 2, ptr addrspace(5) inttoptr (i256 2 to ptr addrspace(5)), align 64 @@ -90,8 +91,7 @@ define i256 @test_gas_as6() { ; CHECK-SAME: () local_unnamed_addr #[[ATTR2]] { ; CHECK-NEXT: store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 ; CHECK-NEXT: [[GAS:%.*]] = tail call i256 @llvm.evm.gas() -; CHECK-NEXT: [[LOAD:%.*]] = load i256, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64 -; CHECK-NEXT: [[RET:%.*]] = add i256 [[LOAD]], [[GAS]] +; CHECK-NEXT: [[RET:%.*]] = add i256 [[GAS]], 2 ; CHECK-NEXT: ret i256 [[RET]] ; store i256 2, ptr addrspace(6) inttoptr (i256 2 to ptr addrspace(6)), align 64