diff --git a/.github/workflows/check-llvm-version.yml b/.github/workflows/check-llvm-version.yml index ce3c5ec..5ef53e5 100644 --- a/.github/workflows/check-llvm-version.yml +++ b/.github/workflows/check-llvm-version.yml @@ -25,7 +25,7 @@ on: jobs: check-llvm-version: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout revive uses: actions/checkout@v6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aababb..8b9ae07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,14 @@ Supported `polkadot-sdk` rev: `2604.2.0` ### Fixed - Restored the top-level `revive_version` field in `--standard-json` output and the `resolc_version` field in `--combined-json` output, both of which stopped being populated in `v0.4.0`. [#557](https://github.com/paritytech/revive/pull/557) -- ICE triggered by `--disable-solc-optimizer --newyork where inlined loops could emit an unterminated basic block in the inlined entrypoint. [#561](https://github.com/paritytech/revive/pull/561) +- ICE triggered by `--disable-solc-optimizer --newyork` where inlined loops could emit an unterminated basic block in the inlined entrypoint. [#561](https://github.com/paritytech/revive/pull/561) - `--newyork`: dead-store elimination could drop a store whose memory expansion an intervening `msize()` observes, so `msize()` read a stale (unexpanded) value. [#565](https://github.com/paritytech/revive/pull/565) -- `--newyork`: dead-store elimination could drop a store that a later overlapping unaligned `mload` reads, returning zeroed memory instead of the stored bytes. -- `--newyork`: an unaligned full-word `mload`/`mstore` overlapping a word kept native (little-endian) by the heap optimization returned byte-swapped bytes. +- `--newyork`: dead-store elimination could drop a store that a later overlapping unaligned `mload` reads, returning zeroed memory instead of the stored bytes. [#563](https://github.com/paritytech/revive/pull/563) +- `--newyork`: an unaligned full-word `mload`/`mstore` overlapping a word kept native (little-endian) by the heap optimization returned byte-swapped bytes. [#562](https://github.com/paritytech/revive/pull/562) - `--newyork`: a `calldatacopy`/`codecopy`/`returndatacopy` to a dynamic destination that could overwrite the free-memory-pointer word truncated a subsequent `mload(0x40)` to `0`. [#564](https://github.com/paritytech/revive/pull/564) - `--newyork`: a full-width value stored to the free-memory-pointer slot (e.g. a function parameter `>= 2^64`) was narrowed to 64 bits, inserting a checked truncation that consumed all gas. [#567](https://github.com/paritytech/revive/pull/567) - `--newyork`: an unaligned `mstore` corrupting the free-memory-pointer word let a later store through the corrupted pointer silently succeed where EVM runs out of gas. [#566](https://github.com/paritytech/revive/pull/566) +- Fixed a bytecode divergence (yet functionally equivalent) between the resolc Linux/macOS builds and the Windows build for contracts with loops indexing array elements at constant offsets from a `uint8` loop variable (e.g. `array[i]`/`array[i + 1]`), compiled with `--newyork` at `-Oz` with the solc optimizer disabled. [#570](https://github.com/paritytech/revive/pull/570) ## v1.3.0 diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 0000000..39079d7 --- /dev/null +++ b/patches/README.md @@ -0,0 +1,25 @@ +# Third-Party Source Patches + +Patches in this directory are applied on top of the corresponding third-party source files (e.g. the LLVM submodule) at build time. No manual step should be required, the build tooling applies them automatically. + +## Layout and naming + +* One subdirectory per patched project. + * LLVM patches live in `patches/llvm/`. +* Cherry-picks of upstream commits or pull requests are named accordingly. + * Example: `llvm-pr-190587.patch` + * `190587` would be the upstream PR number + +## Format and content + +* The `.patch` files must be `git apply`-compatible unified diffs, with paths relative to the submodule root (e.g. `a/llvm/lib/...`). +* The `.patch` files are forced to LF line endings via `.gitattributes`. + +## Behavior + +* Patches are applied in lexicographic order. +* Already-applied patches are detected and skipped, so re-running the build is safe. + +## Lifecycle + +Vendor a patch when a needed fix is not yet in the pinned revision, and delete the patch file when the pinned revision is bumped to one that contains the fix. diff --git a/patches/llvm/llvm-pr-190587.patch b/patches/llvm/llvm-pr-190587.patch new file mode 100644 index 0000000..4355087 --- /dev/null +++ b/patches/llvm/llvm-pr-190587.patch @@ -0,0 +1,52 @@ +diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp +index a4c1e08430..2876353ee5 100644 +--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp ++++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp +@@ -1373,23 +1373,31 @@ static bool isAMCompletelyFolded(const TargetTransformInfo &TTI, + bool HasBaseReg, int64_t Scale, + Instruction *Fixup = nullptr); + +-static unsigned getSetupCost(const SCEV *Reg, unsigned Depth) { +- if (isa(Reg) || isa(Reg)) ++static unsigned getSetupCost(const SCEV *Reg, unsigned Depth, ++ const TargetTransformInfo &TTI) { ++ if (isa(Reg)) + return 1; ++ if (const auto *C = dyn_cast(Reg)) { ++ if (TTI.getIntImmCost(C->getAPInt(), C->getType(), ++ TargetTransformInfo::TCK_RecipThroughput) == ++ TargetTransformInfo::TCC_Free) ++ return 0; ++ return 1; ++ } + if (Depth == 0) + return 0; + if (const auto *S = dyn_cast(Reg)) +- return getSetupCost(S->getStart(), Depth - 1); ++ return getSetupCost(S->getStart(), Depth - 1, TTI); + if (auto S = dyn_cast(Reg)) +- return getSetupCost(S->getOperand(), Depth - 1); ++ return getSetupCost(S->getOperand(), Depth - 1, TTI); + if (auto S = dyn_cast(Reg)) + return std::accumulate(S->operands().begin(), S->operands().end(), 0, + [&](unsigned i, const SCEV *Reg) { +- return i + getSetupCost(Reg, Depth - 1); ++ return i + getSetupCost(Reg, Depth - 1, TTI); + }); + if (auto S = dyn_cast(Reg)) +- return getSetupCost(S->getLHS(), Depth - 1) + +- getSetupCost(S->getRHS(), Depth - 1); ++ return getSetupCost(S->getLHS(), Depth - 1, TTI) + ++ getSetupCost(S->getRHS(), Depth - 1, TTI); + return 0; + } + +@@ -1459,7 +1467,7 @@ void Cost::RateRegister(const Formula &F, const SCEV *Reg, + + // Rough heuristic; favor registers which don't require extra setup + // instructions in the preheader. +- C.SetupCost += getSetupCost(Reg, SetupCostDepthLimit); ++ C.SetupCost += getSetupCost(Reg, SetupCostDepthLimit, *TTI); + // Ensure we don't, even with the recusion limit, produce invalid costs. + C.SetupCost = std::min(C.SetupCost, 1 << 16); +