Skip to content

Count JALR return sites in loop promotion - #19

Merged
jserv merged 1 commit into
sysprog21:mainfrom
thc1006:rvopt-jalr-call-loop-entry
Aug 3, 2026
Merged

Count JALR return sites in loop promotion#19
jserv merged 1 commit into
sysprog21:mainfrom
thc1006:rvopt-jalr-call-loop-entry

Conversation

@thc1006

@thc1006 thc1006 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

compute_promotions() counts the return site of a direct jal ra, ...
as an external loop entry, but misses the return site of a resolved
jalr ra, .... successors() and return-site collection treat both
as calls. A loop entered through the jalr ra, ... return can
therefore look single-entry, so promotion attaches its preload only to
the direct-jump entry and execution through the return reads a zeroed
promotion cell.

The image below loads the word at address 84, increments it, stores it
back, then writes its low byte. The image emitted by main writes
0x01; the guest program should write B (0x42).

05400513,00100393,00000293,00028663,
0100006f,00000013,05000413,000400e7,
00052303,00130313,00652023,fff38393,
fe0398e3,00100513,05400593,00100613,
04000893,00000073,05d00893,00000073,
00008067,00000041

Use a shared is_link_call() predicate in successors(),
compute_promotions(), and return-site collection. Add an executable
regression that lowers and runs the image and requires the exact output
B.

The differential fuzzer does not cover this shape because generated
loop bodies contain no nested control flow. The current RV32I test
ELFs, including rv32ui, lower to byte-identical images before and
after this change.

make check-all passes.
make fuzz-rvopt passes.


Summary by cubic

Fix loop promotion to count return sites from resolved jalr ra, ... calls, so loops entered via a jalr return get the preload. Unifies call detection and adds a regression that must print B.

  • Bug Fixes
    • Added is_link_call() and used it in successors, loop promotion, and return-site collection to treat jal ra and resolved jalr ra consistently.
    • Added a hand-encoded image and verify-mux check (RVOPT_LOOPCALL_ENC) that fails if the loop preload is bypassed; expects exact output B.

Written for commit 0fd2320. Summary will update on new commits.

Review in cubic

successors() and the return-site table both treat a resolved 'jalr ra'
as a call, so control comes back to the word after it. Loop promotion
counted only 'jal ra' that way, so a loop entered through such a return
looked single-entry. Promotion then attached its preload to the direct
jump, and a run arriving by the return reached the loop without it and
read a zeroed cell instead of guest memory.

Share one is_link_call() predicate between the three passes. Add a
hand-encoded image that enters its loop both ways and has to write B;
counting only the direct jump makes it write 01.

make check-all passes.
make fuzz-rvopt passes.

Signed-off-by: thc1006 <hctsai1006@cs.nctu.edu.tw>
cubic-dev-ai[bot]

This comment was marked as resolved.

@jserv
jserv merged commit c89bf4e into sysprog21:main Aug 3, 2026
6 checks passed
@jserv

jserv commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Thank @thc1006 for contributing!

@thc1006
thc1006 deleted the rvopt-jalr-call-loop-entry branch August 3, 2026 18:38
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.

2 participants