feat(mlir): enable the spill-retry protocol for the MLIR pipeline - #653
feat(mlir): enable the spill-retry protocol for the MLIR pipeline#653abinavpp wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the MLIR-based compilation pipeline to support the spill-retry protocol by keeping memoryguard symbolic through MLIR lowering and then folding it into a constant in the LLVM module before LLVM optimization runs.
Changes:
- Updates the MLIR
convert-yul-to-stdpass binding and usage to keepmemoryguardsymbolic via a newsymbolic_mem_guardparameter. - Adds a pre-optimization fold step in the EVM codegen context to rewrite
llvm.evm.memoryguardcalls into constants based onspill_area_size. - Updates Sol dialect array type construction stub to the newer API shape (optional fixed-size vs dynamic).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| solx-mlir/src/ffi.rs | Updates the MLIR pass FFI binding to accept symbolic_mem_guard. |
| solx-mlir/src/context/mod.rs | Uses the new pass constructor and documents symbolic memoryguard behavior in the pass pipeline. |
| solx-mlir/sol_attr_stubs.cpp | Updates Sol dialect ArrayType construction to use an optional size representation. |
| solx-core/src/project/contract/mod.rs | Folds symbolic memoryguard in the MLIR→LLVM path before running optimization/codegen. |
| solx-codegen-evm/src/codegen/context/mod.rs | Introduces fold_memory_guard to rewrite llvm.evm.memoryguard into constants and capture the guard value. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Coverage Summary
|
2f831d3 to
e43f9a2
Compare
|
The delta is one file, Measured with the two commits cherry-picked onto |
|
Correction to the tally above, after rebasing the MLIR side onto current Baseline and branch are now identical: 10247 passed / 241 failed / 474 invalid, same failing set - so this PR's tester delta is 0. The +4 I reported was not this PR. It came from solx-llvm So the corpus currently contains no test whose outcome depends on the retry path. What the fix buys is still real, just unobservable here: under the pre-dispatch-table MLIR that file was the demonstration (corrupted arrays -> pass), and the hand-written spill inputs behave correctly (reload reuse and the return buffer land at G+S, a non-spilling contract keeps FMP at 0x80). Cost is nil: a sequential sweep of |
14877f9 to
935e266
Compare
hedgar2017
left a comment
There was a problem hiding this comment.
LGTM, but please get merged LLVM first.
935e266 to
db4030d
Compare
cf298a4 to
c72be44
Compare
c72be44 to
eb850f7
Compare
c049410 to
83d1c18
Compare
809a38f to
92f5806
Compare
| use inkwell::values::AsValueRef; | ||
|
|
||
| let size = context.i64_type().const_int(size, false); | ||
| for flag in module.get_global_metadata("llvm.module.flags") { |
There was a problem hiding this comment.
Can we use module.get_flag instead?
3e3cf32 to
84c3d66
Compare
The MLIR pipeline keeps the memoryguard symbolic through the conversion, and the EVM backend's evm-fold-memory-guard pass folds it and publishes the spill region's base, so the retry no longer places the region under the heap.
The pipeline needs evm-fold-memory-guard, which the pinned revision predates.
Both front ends now name the region on the module rather than on the command line, so the backend has one interface and the legacy pipeline stops depending on process-global options. The MLIR front end publishes the guard itself, since only it knows where its static allocations end.
84c3d66 to
c7393c1
Compare
|
The |
| /// | ||
| /// Whether the `llvm.module.flags` triplet `flag` is named `key`. | ||
| /// | ||
| fn is_flag_named(flag: &inkwell::values::MetadataValue<'ctx>, key: &str) -> bool { |
There was a problem hiding this comment.
Single call: please inline.
| /// Whether the `llvm.module.flags` triplet `flag` is named `key`. | ||
| /// | ||
| fn is_flag_named(flag: &inkwell::values::MetadataValue<'ctx>, key: &str) -> bool { | ||
| flag.get_node_values().get(1).is_some_and(|name| { |
There was a problem hiding this comment.
Worth explaining why is this index == 1, using a named constant, and/or using a stricter / more typed accessor.
| /// Sets the spill region module flags read by the EVM backend. | ||
| /// | ||
| fn describe_stack_region(&self, size: u64) { | ||
| if self.module().get_flag("evm-memory-guard").is_none() { |
There was a problem hiding this comment.
Is this guard needed, or is the setter idempotent?
If it's needed, worth spelling the magic literal once, or even refactoring these keys by moving them to some enum as their number grows.
| /// | ||
| /// Adds the `key` module flag, or replaces its value if it is already there. | ||
| /// | ||
| fn set_module_flag(&self, key: &str, value: u64) { |
There was a problem hiding this comment.
This is more low-level so belongs in inkwell. Maybe it already contains some flag setters in the module.
| /// | ||
| /// Sets the spill region module flags read by the EVM backend. | ||
| /// | ||
| fn describe_stack_region(&self, size: u64) { |
There was a problem hiding this comment.
I think can also be inlined once set_module_flag is moved to inkwell.
NomicFoundation/solx-llvm#142