Skip to content

[mlir][Sol] Replace int64_t sentinel for dynamic Sol_ArrayType size with std::optional<APInt> - #85

Open
PavelKopyl wants to merge 1 commit into
kpv-outline-copyfrom
kpv-huge-static-arrays
Open

[mlir][Sol] Replace int64_t sentinel for dynamic Sol_ArrayType size with std::optional<APInt>#85
PavelKopyl wants to merge 1 commit into
kpv-outline-copyfrom
kpv-huge-static-arrays

Conversation

@PavelKopyl

@PavelKopyl PavelKopyl commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

NomicFoundation/solx-solidity#142

Replace the int64_t dimension with a -1 sentinel for dynamic arrays:
int64_t can neither hold storage-array dimensions up to 2^256-1 nor
reliably distinguish them from the sentinel. Store the size as
std::optional (nullopt = dynamic) via a custom Sol_OptAPIntParam,
needed because ODS's OptionalParameter asserts on mismatched
bit widths in the generated comparator.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Sol dialect’s sol.array type to represent static array dimensions as std::optional<llvm::APInt> (nullopt = dynamic, value = 256-bit static size), removing the previous int64_t “-1 sentinel” approach that could not correctly represent Solidity’s allowed uint[(2**256)-1] bounds. It also introduces a custom ODS parameter (Sol_OptAPIntParam) with a safe comparator to avoid APInt::operator== bitwidth assertions when used under std::optional.

Changes:

  • Replace Sol_ArrayType size parameter from int64_t (with -1 sentinel) to std::optional<llvm::APInt> and add helper APIs (isDynSized(), getSize(), getSmallSize()).
  • Update parsing/printing and multiple lowering/utilities to use the new APIs (including APInt comparisons where needed).
  • Add an ODS parameter wrapper with a comparator that guards bitwidth equality before comparing APInt values.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
mlir/lib/Dialect/Sol/SolBase.cpp Updates sol.array parse/print and storage slot sizing to use optional APInt sizes.
mlir/lib/Conversion/SolToYul/TypeConverter.cpp Switches stack array lowering to use getSmallSize() for LLVM array element counts.
mlir/lib/Conversion/SolToYul/SolToYul.cpp Updates array bounds checks and memory allocation sizing to match new array-size representation.
mlir/lib/Conversion/SolToYul/EVMUtil.cpp Replaces size computations with getSmallSize()/APInt comparisons for ABI/memory helpers and array copy logic.
mlir/include/mlir/Dialect/Sol/SolBase.td Introduces Sol_OptAPIntParam and updates Sol_ArrayType parameter + helper methods.
Comments suppressed due to low confidence (1)

mlir/lib/Conversion/SolToYul/EVMUtil.cpp:3360

  • arrTy.getSmallSize() * 32 is evaluated as a signed multiplication and can overflow (undefined behavior) before being wrapped into an i256 constant. Use checked multiplication (or APInt arithmetic) before converting to an i256 constant.
      dstAddr = genMemAlloc(bExt.genI256Const(arrTy.getSmallSize() * 32), loc);
      ret = dstAddr;
      srcAddr = addr;
      size = bExt.genI256Const(arrTy.getSize());
      guards.requireFixedArraySpan(srcAddr, arrTy);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread mlir/lib/Dialect/Sol/SolBase.cpp Outdated
Comment thread mlir/include/mlir/Dialect/Sol/SolBase.td Outdated
Comment thread mlir/lib/Conversion/SolToYul/EVMUtil.cpp
Comment thread mlir/lib/Conversion/SolToYul/SolToYul.cpp

@abinavpp abinavpp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getSmallSize + getSize is confusing. how about just getSize? and those non storage contexts could use getZExtValue (which, iirc, asserts?)? optionally, we could add a type verifier that only allows >= u64.max values for storage data-locations.

thanks for working on this!

@PavelKopyl

Copy link
Copy Markdown
Contributor Author

getSmallSize + getSize is confusing. how about just getSize? and those non storage contexts could use getZExtValue (which, iirc, asserts?)? optionally, we could add a type verifier that only allows >= u64.max values for storage data-locations.

thanks for working on this!

Thanks, will change that.

@PavelKopyl
PavelKopyl force-pushed the kpv-huge-static-arrays branch from e18eba8 to 163de4c Compare June 22, 2026 16:35
@PavelKopyl
PavelKopyl force-pushed the kpv-huge-static-arrays branch from 163de4c to f03c5b5 Compare July 15, 2026 14:20
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@PavelKopyl
PavelKopyl changed the base branch from main to kpv-outline-copy July 15, 2026 14:40
@PavelKopyl
PavelKopyl force-pushed the kpv-huge-static-arrays branch from f03c5b5 to ff84ae9 Compare July 16, 2026 18:13
@PavelKopyl
PavelKopyl requested a review from Copilot July 16, 2026 18:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread mlir/lib/Dialect/Sol/SolBase.cpp
Comment thread mlir/lib/Dialect/Sol/SolBase.cpp
@PavelKopyl
PavelKopyl force-pushed the kpv-huge-static-arrays branch from ff84ae9 to 5d03e06 Compare July 16, 2026 21:53
@PavelKopyl
PavelKopyl force-pushed the kpv-outline-copy branch 2 times, most recently from 3f7bb32 to 53fb3ef Compare August 6, 2026 16:34
Replace the int64_t dimension with a -1 sentinel for dynamic arrays:
int64_t can neither hold storage-array dimensions up to 2^256-1 nor
reliably distinguish them from the sentinel. Store the size as
std::optional<APInt> (nullopt = dynamic) via a custom Sol_OptAPIntParam,
needed because ODS's OptionalParameter<APInt> asserts on mismatched
bit widths in the generated comparator.
Update other MLIR parts according to the new type.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (4)

mlir/lib/Conversion/SolToYul/EVMUtil.cpp:3767

  • This allocation size is computed with host uint64_t arithmetic (getZExtValue() * 32) before being wrapped into an i256 constant. For very large fixed array sizes, the host multiplication can overflow and wrap, causing an incorrect (too small) allocation size to be emitted. Use APInt arithmetic for the * 32 computation to preserve the full value and let the existing runtime guards trigger appropriately.
      dstAddr = genMemAlloc(
          bExt.genI256Const(arrTy.getSize().getZExtValue() * 32), loc);
      ret = dstAddr;

mlir/lib/Conversion/SolToYul/SolToYul.cpp:2297

  • genMemAlloc is being called with an AllocSize computed via host uint64_t arithmetic (getZExtValue() * 32). With the new APInt sizes, fixed arrays can be large enough that this overflows/wraps (and also narrows to int64_t), producing an incorrect allocation size. Prefer the genMemAlloc(Value) overload and compute sizeInBytes in i256 IR, which will interact correctly with the existing runtime overflow/too-large guards.
        size = bExt.genI256Const(arrTy.getSize());
        dstAddr = evmB.genMemAlloc(arrTy.getSize().getZExtValue() * 32);
        dstDataAddr = dstAddr;

mlir/lib/Conversion/SolToYul/EVMUtil.cpp:862

  • getMallocSize converts the 256-bit array size to uint64_t and then to int64_t implicitly via static_cast<int64_t>(...). With the new APInt-backed sizes, non-storage arrays can legally be larger than INT64_MAX (up to 2^64-1), which would make this cast implementation-defined and the returned allocation size incorrect (potentially negative). Consider doing the multiplication in APInt and asserting that the byte size fits AllocSize before converting.
  if (auto arrayTy = dyn_cast<sol::ArrayType>(ty)) {
    assert(!arrayTy.isDynSized());
    return static_cast<int64_t>(arrayTy.getSize().getZExtValue()) * 32;
  }

mlir/lib/Conversion/SolToYul/EVMUtil.cpp:787

  • This bounds check computes the fixed-array span using host uint64_t arithmetic (getZExtValue() * ...). For sizes > 2^64/stride, this multiplication will wrap in C++ before creating the i256 constant, potentially making the end-address check unsound for very large (but verifier-legal) non-storage arrays. Compute the span in APInt (256-bit) to avoid host overflow.

This issue also appears on line 3765 of the same file.

    Value endAddr = b.create<yul::AddOp>(
        loc, baseAddr,
        bExt.genI256Const(arrTy.getSize().getZExtValue() *
                          evm::getCallDataHeadSize(arrTy.getEltType())));

@abinavpp abinavpp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants