Skip to content

[WIP] Reify unstructured control-flow into thunk values.#39

Draft
eddyb wants to merge 7 commits intoeddyb/structurize-noretfrom
eddyb/thunk
Draft

[WIP] Reify unstructured control-flow into thunk values.#39
eddyb wants to merge 7 commits intoeddyb/structurize-noretfrom
eddyb/thunk

Conversation

@eddyb
Copy link
Copy Markdown
Member

@eddyb eddyb commented Apr 23, 2026

Note: this PR is a draft to avoid accidental merging onto its "base" branch (used as a form of ad-hoc PR stacking), and will remain as such, until its "base" branch can be set to main, i.e. all prerequisite PRs will have landed, up to and including this PR (whose branch is the "base" of this one):


Before this PR, cf::unstructured::ControlFlowGraph contained an ad-hoc CFG, by attaching a ControlInst to each Region (as its "terminator") participating in the unstructured control-flow graph.

This PR starts to move more of that information into the structured side of SPIR-T, introducing the concept of a thunk, which encapsulates an unstructured destination, and necessary inputs, into one Value (akin to high-level closures, or more specifically CPS-style "continuations"), where the type is always thunk (hiding any details).

While such a high-level-looking feature seems out of place, these thunks:

  • cannot be written to memory or passed between functions, so they don't need ABI considerations
  • imply a CFG from dataflow, without introducing more dynamic control-flow
  • must be created in a "tail" position, which effectively always "invokes" them just after they're "bound"

For example, a ControlInstKind::SelectBranch between targets L3(v1) and L5(v1, v2), now becomes:

v9: thunk = if cond {
  v7: thunk = thunk.bind(L3, (v1))
  v7
} else {
  v8: thunk = thunk.bind(L5, (v1, v2))
  v8
}
v9

(some of this verbosity could be cleaned up, in general, in SPIR-T pretty-printing, but I wanted to be explicit here)

This encoding, if allowed to be used a bit more dynamically, also mirrors the worst-case structurization needs to handle: a reified destination and inputs for all possible destination choices. Long-term, thunks may allow a more "local" definition of structurization, as reducing the use of thunks where possible, and encoding the remaining thunks almost like a Rust enum being constructed and then pattern-matched on.


The original motivation for attempting this refactor, interestingly enough, would involve writing thunks to memory (an emulated stack, for a form of CPS-based recursion/indirect-call emulation), but a specific design which allows both the "unstructured CFG", and said stackful CPS usecase, has yet to be finalized.


TODO: better documentation, consider implementing the thunk switch/loop-switch described in comments (see src/cf/unstructured.rs, the long // FIXME(eddyb) comment above struct ControlFlowGraph).

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.

1 participant