Skip to content

[mono-move] Chained field access fusion - #20249

Merged
vineethk merged 1 commit into
mainfrom
vk/chained-field-fusion
Jul 28, 2026
Merged

[mono-move] Chained field access fusion#20249
vineethk merged 1 commit into
mainfrom
vk/chained-field-fusion

Conversation

@vineethk

@vineethk vineethk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Chained Field Access Fusion

Overview

This PR teaches the mono-move specializer to recognize chains of struct field borrows — patterns like &s.a.b.c followed by a read or write — and collapse each chain into a single fused instruction. Where the interpreter previously executed one micro-op per field step (each computing an address, materializing a reference, and dispatching back through the run loop), a fused chain now executes as one micro-op that folds the entire path into a single precomputed offset.

Main Changes

New fused IR instructions

The stackless IR gains a family of field chain instructions covering the useful combinations of root (a local, or an existing reference) and terminal action (borrow immutably, borrow mutably, read the value out, or write a value in). Each carries the full field path, so downstream phases see the whole access as one unit. Borrow chains deliberately preserve the immutable/mutable distinction, because a mutable borrow of a local creates a hidden-write channel that later optimization passes (coalescing, copy propagation) must be able to see.

The fusion pass

A new pass runs during destacking, before slot allocation. It scans each block for runs of single-use field borrows that feed directly into one another and ends each run at its terminal use. Key properties:

  • Single-use discipline — an intermediate reference consumed by anything other than the next link breaks the chain, so no observable reference ever disappears.
  • Gap tolerance with terminal sinking — unrelated instructions may sit between the chain and its terminal; the fused instruction is placed at the terminal's position. This is sound for struct chains because the intermediate steps are pure address computations with no observable effects.
  • Enum exclusion — enum variant-field steps are excluded from fusion, because each step carries a tag check that can abort; reordering or merging those checks would change abort behavior.
  • Linear-time operation — the pass rewrites blocks with a lazy placement plan and in-place compaction, keeping expected cost proportional to block size.

Enum variant-field access rework

Enum field access micro-ops were restructured into two tiers:

  • A uniform fast path: when a field lives at the same offset in every variant, the access needs no tag inspection at all and is emitted as a plain heap read/write at a static offset — the same shape as ordinary struct access.
  • A tag-dispatched path: otherwise, the micro-op carries a per-tag offset table; at runtime the tag selects the offset, and a missing entry aborts with a variant mismatch. This replaced an older scratch-slot-plus-dereference lowering for divergent-offset fields, eliminating the scratch mechanism entirely.

All offsets are precomputed as object-relative (the enum tag header is folded in at construction), so the interpreter does no offset arithmetic beyond a single add.

Runtime and gas

  • The interpreter gains straightforward handlers for the new micro-ops, sharing one small helper for the tag-dispatch prologue.
  • A latent undefined-behavior issue was fixed along the way: an 8-byte move micro-op assumed 8-byte alignment, but size-8 aggregates can legally sit at 4-aligned offsets; the accesses are now explicitly unaligned (free on the targeted architectures).

Testing

  • New differential tests cover chain fusion end-to-end, gap-tolerant terminal sinking, divergent enum writes (including the abort-on-wrong-variant paths), and abort-ordering behavior that justifies the enum exclusion.
  • All existing differential and unit suites pass with the fusion enabled; golden outputs were regenerated to reflect the fused instruction streams.

Note

Medium Risk
Touches interpreter memory access (unaligned u64), enum tag dispatch, and optimization soundness for mut-borrow chains; broad differential coverage mitigates regressions but VM semantics changes warrant careful review.

Overview
Adds pre-slot-allocation fusion that collapses depth-≥2 inline struct field borrow runs (single-use intermediates, terminal read/write/borrow, gap-tolerant sinking) into new *FieldChain stackless IR, then lowers them to one micro-op with a summed offset. Coalescing/copy-prop gain shared mut_local_borrow_target guards (including MutBorrowLocalFieldChain).

Enum variant fields are split into uniform HeapReadOffset / HeapWriteOffset vs tag-dispatched Enum*VariantFieldByTag ops; divergent by-value access no longer uses a scratch fat pointer. Move8 / heap 8-byte moves use unaligned loads/stores for 4-aligned size-8 slots.

Gas metering matches pre-fusion borrow/read/write costs for chains; differential tests cover struct chains, enum paths, abort order, and generics.

Reviewed by Cursor Bugbot for commit f77a71e. Bugbot is set up for automated code reviews on this repo. Configure here.

vineethk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@vineethk vineethk changed the title Implement chained field access fusion [mono-move] Chained field access fusion Jul 22, 2026
@vineethk
vineethk force-pushed the vk/chained-field-fusion branch 2 times, most recently from fceccc9 to d55336f Compare July 22, 2026 19:06
@vineethk
vineethk marked this pull request as ready for review July 22, 2026 19:07
@vineethk
vineethk force-pushed the vk/chained-field-fusion branch from d55336f to bd474d4 Compare July 23, 2026 14:09
Comment thread third_party/move/mono-move/specializer/src/stackless_exec_ir/instr_utils.rs Outdated
Comment thread third_party/move/mono-move/specializer/src/stackless_exec_ir/instr_utils.rs Outdated
Comment thread third_party/move/mono-move/runtime/src/interpreter.rs
Comment thread third_party/move/mono-move/runtime/src/interpreter.rs Outdated
Comment thread third_party/move/mono-move/runtime/src/interpreter.rs
Comment thread third_party/move/mono-move/runtime/src/interpreter.rs Outdated
Comment thread third_party/move/mono-move/runtime/src/interpreter.rs Outdated
Comment thread third_party/move/mono-move/specializer/src/gas.rs Outdated
@vineethk
vineethk force-pushed the vk/chained-field-fusion branch from bd474d4 to c746e0c Compare July 27, 2026 15:02

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c746e0c. Configure here.

Comment thread third_party/move/mono-move/specializer/src/gas.rs Outdated
@vineethk
vineethk force-pushed the vk/chained-field-fusion branch 2 times, most recently from 410f5de to e2ba9f2 Compare July 28, 2026 00:15
@vineethk
vineethk enabled auto-merge (squash) July 28, 2026 00:17
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@vineethk
vineethk force-pushed the vk/chained-field-fusion branch from e2ba9f2 to f77a71e Compare July 28, 2026 01:05

@cursor cursor Bot 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.

Aptos Security Bugbot has reviewed your changes and found no new issue.

Open in Web View Automation 

Sent by Cursor Automation: Security Review Bot

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

mono-move benchmark gate

4 regression(s) beyond ±3% noise band

2 ok · 1 improved · 0 new · 0 absent (threshold T = ±3%, criterion mean CI vs main)

Benchmark mean Δ 95% CI median (main → PR) Verdict
fib/mono +2.7% [+2.2%, +3.2%] 6.66ms → 6.77ms ok
nested_loop/mono +32.3% [+32.1%, +32.4%] 8.94ms → 11.80ms regression
merge_sort/mono +14.5% [+14.2%, +14.9%] 940.57µs → 1.07ms regression
bst/mono +1.0% [+0.4%, +1.6%] 3.27ms → 3.26ms ok
match_sum/mono -12.5% [-13.4%, -11.5%] 24.42ms → 21.00ms improved
int_arith_loop/mono_u64 +20.6% [+20.4%, +20.7%] 211.52µs → 255.35µs regression
int_arith_loop/mono_i64 +6.4% [+5.9%, +6.8%] 414.71µs → 435.74µs regression

Improvements are not failures. main rebaselines on merge, so the next PR compares against the faster code automatically.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Forge suite compat success on d409a7017a5f270d45b4da5301fc2c689fcc363c ==> f77a71ec7a7608b3665ae38399659972361d8eda

Compatibility test results for d409a7017a5f270d45b4da5301fc2c689fcc363c ==> f77a71ec7a7608b3665ae38399659972361d8eda (PR)
1. Check liveness of validators at old version: d409a7017a5f270d45b4da5301fc2c689fcc363c
compatibility::simple-validator-upgrade::liveness-check : committed: 15099.64 txn/s, latency: 2221.38 ms, (p50: 2100 ms, p70: 2600, p90: 3200 ms, p99: 5100 ms), latency samples: 504300
2. Upgrading first Validator to new version: f77a71ec7a7608b3665ae38399659972361d8eda
compatibility::simple-validator-upgrade::single-validator-upgrade : committed: 6377.38 txn/s, latency: 5328.75 ms, (p50: 5900 ms, p70: 6000, p90: 6000 ms, p99: 6400 ms), latency samples: 221260
3. Upgrading rest of first batch to new version: f77a71ec7a7608b3665ae38399659972361d8eda
compatibility::simple-validator-upgrade::half-validator-upgrade : committed: 6330.63 txn/s, latency: 5373.58 ms, (p50: 5900 ms, p70: 5900, p90: 6000 ms, p99: 6200 ms), latency samples: 221500
4. upgrading second batch to new version: f77a71ec7a7608b3665ae38399659972361d8eda
compatibility::simple-validator-upgrade::rest-validator-upgrade : committed: 10376.96 txn/s, latency: 3133.59 ms, (p50: 3200 ms, p70: 3300, p90: 4000 ms, p99: 4700 ms), latency samples: 343460
5. check swarm health
Compatibility test for d409a7017a5f270d45b4da5301fc2c689fcc363c ==> f77a71ec7a7608b3665ae38399659972361d8eda passed
Test Ok

@github-actions

Copy link
Copy Markdown
Contributor

✅ Forge suite realistic_env_max_load success on f77a71ec7a7608b3665ae38399659972361d8eda

Forge report malformed: Expecting property name enclosed in double quotes: line 4 column 1 (char 23)
'{\n  "metrics": [\n    {\n[2026-07-28T01:38:39Z INFO  aptos_forge::report] Test Ok\n      "test_name": "two traffics test: inner traffic",\n      "metric": "submitted_txn",\n      "value": 5954400.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "expired_txn",\n      "value": 0.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "avg_tps",\n      "value": 15942.411181480664\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "avg_latency",\n      "value": 1068.6420388284293\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "p50_latency",\n      "value": 1000.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "p90_latency",\n      "value": 1200.0\n    },\n    {\n      "test_name": "two traffics test: inner traffic",\n      "metric": "p99_latency",\n      "value": 1700.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "submitted_txn",\n      "value": 42600.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "expired_txn",\n      "value": 0.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "avg_tps",\n      "value": 99.99557978881853\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "avg_latency",\n      "value": 797.1960674157303\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "p50_latency",\n      "value": 700.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "p90_latency",\n      "value": 900.0\n    },\n    {\n      "test_name": "two traffics test",\n      "metric": "p99_latency",\n      "value": 1100.0\n    }\n  ],\n  "text": "two traffics test: inner traffic : committed: 15942.41 txn/s, latency: 1068.64 ms, (p50: 1000 ms, p70: 1100, p90: 1200 ms, p99: 1700 ms), latency samples: 5954400\\ntwo traffics test : committed: 100.00 txn/s, latency: 797.20 ms, (p50: 700 ms, p70: 800, p90: 900 ms, p99: 1100 ms), latency samples: 1780\\nLatency breakdown for phase 0: [\\"MempoolToBlockCreation: max: 0.303, avg: 0.267\\", \\"ConsensusProposalToOrdered: max: 0.110, avg: 0.105\\", \\"ConsensusOrderedToCommit: max: 0.140, avg: 0.128\\", \\"ConsensusProposalToCommit: max: 0.248, avg: 0.234\\"]\\nMax non-epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.84s no progress at version 5887932 (avg 0.06s) [limit 15].\\nMax epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.50s no progress at version 2859124 (avg 0.50s) [limit 16].\\nTest Ok"\n}'
Trailing Log Lines:
[2026-07-28T01:38:38Z INFO  ureq::unit] sending request POST http://vmagent-victoria-metrics-agent.victoria-metrics.svc:8429/api/v1/import/prometheus
test CompositeNetworkTest ... ok
Test Statistics: 
two traffics test: inner traffic : committed: 15942.41 txn/s, latency: 1068.64 ms, (p50: 1000 ms, p70: 1100, p90: 1200 ms, p99: 1700 ms), latency samples: 5954400
two traffics test : committed: 100.00 txn/s, latency: 797.20 ms, (p50: 700 ms, p70: 800, p90: 900 ms, p99: 1100 ms), latency samples: 1780
Latency breakdown for phase 0: ["MempoolToBlockCreation: max: 0.303, avg: 0.267", "ConsensusProposalToOrdered: max: 0.110, avg: 0.105", "ConsensusOrderedToCommit: max: 0.140, avg: 0.128", "ConsensusProposalToCommit: max: 0.248, avg: 0.234"]
Max non-epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.84s no progress at version 5887932 (avg 0.06s) [limit 15].
Max epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.50s no progress at version 2859124 (avg 0.50s) [limit 16].
Test Ok

=== BEGIN JUNIT ===
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="forge" tests="1" failures="0" errors="0" uuid="f7443a76-12ba-47e9-a7b1-dd300856f4fa">
    <testsuite name="local" tests="1" disabled="0" errors="0" failures="0">
        <testcase name="CompositeNetworkTest(network:multi-region-network-emulation(two traffics test)) with ">
        </testcase>
    </testsuite>
</testsuites>
=== END JUNIT ===
[2026-07-28T01:38:39Z INFO  aptos_forge::backend::k8s::cluster_helper] Deleting namespace forge-e2e-pr-20249: Some(NamespaceStatus { conditions: None, phase: Some("Terminating") })
[2026-07-28T01:38:39Z INFO  aptos_forge::backend::k8s::cluster_helper] aptos-node resources for Forge removed in namespace: forge-e2e-pr-20249
[2026-07-28T01:38:39Z INFO  ureq::unit] sending request POST http://vmagent-victoria-metrics-agent.victoria-metrics.svc:8429/api/v1/import/prometheus

test result: ok. 1 passed; 0 soft failed; 0 hard failed; 0 filtered out

Debugging output:
NAME                                         READY   STATUS      RESTARTS   AGE
aptos-node-0-validator-0                     1/1     Running     0          12m
aptos-node-1-validator-0                     1/1     Running     0          12m
aptos-node-2-validator-0                     1/1     Running     0          12m
aptos-node-3-validator-0                     1/1     Running     0          12m
aptos-node-4-validator-0                     1/1     Running     0          12m
aptos-node-5-validator-0                     1/1     Running     0          12m
aptos-node-6-validator-0                     1/1     Running     0          12m
forge-pfn-deployer-7rgp4                     0/1     Completed   0          12m
forge-testnet-deployer-jgpmd                 0/1     Completed   0          12m
genesis-aptos-genesis-eforge709f943c-xgrtr   0/1     Completed   0          12m
pfn-0-0                                      1/1     Running     0          12m
pfn-1-0                                      1/1     Running     0          12m
pfn-2-0                                      1/1     Running     0          12m

@vineethk
vineethk merged commit b0a593d into main Jul 28, 2026
61 of 62 checks passed
@vineethk
vineethk deleted the vk/chained-field-fusion branch July 28, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants