Skip to content

Add -goto_for_early_return transformation#731

Open
nikswamy wants to merge 1 commit intomasterfrom
_goto_for_early_return
Open

Add -goto_for_early_return transformation#731
nikswamy wants to merge 1 commit intomasterfrom
_goto_for_early_return

Conversation

@nikswamy
Copy link
Copy Markdown
Contributor

@nikswamy nikswamy commented Apr 15, 2026

Adds a new -goto_for_early_return CLI option that rewrites functions
containing early returns to use goto-based control flow. When enabled,
functions with non-tail return statements are transformed so that:

  • A return variable (result) is declared at the top with a type-specific
    initializer (e.g. 0 for int32_t, 0U for uint32_t, NULL for pointers;
    omitted for void functions).
  • Each return is replaced by an assignment to result followed by goto exit.
  • The original body is wrapped in an inner compound to avoid VLA scope
    issues with goto.
  • An exit label and final return result are appended at the end.

Fresh name generation avoids collisions with existing identifiers.

This is useful for targets that do not support early returns or where
goto-based control flow is preferred (e.g., certain embedded C dialects).

Changes:

  • lib/C11.ml: Add Goto and Label constructors to the stmt type.
  • lib/Options.ml: Add goto_for_early_return option.
  • lib/GotoForEarlyReturn.ml: New pass implementing the transformation.
  • lib/PrintC.ml: Print Goto and Label statements.
  • src/Karamel.ml: Wire up CLI flag and pipeline integration.
  • test/goto_return/: Diff-based regression test.
  • test/Makefile: Add goto-return-test to all target.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

@protz
Copy link
Copy Markdown
Collaborator

protz commented Apr 16, 2026

Thanks for the PR. Before I review this in detail, two questions:

Let me know once this is ready for review -- I'm assuming draft means draft. Thanks!

@nikswamy
Copy link
Copy Markdown
Contributor Author

It is AI co-authored (the commit message is tagged as such).

I have been testing this in EverParse and it seems to work well. I have a couple of small patches coming and then I'll mark it ready for review.

Thanks for the heads-up on including the test ... will do.

@nikswamy nikswamy force-pushed the _goto_for_early_return branch from 32a8e8d to 78b5c99 Compare April 16, 2026 23:43
@nikswamy nikswamy marked this pull request as ready for review April 16, 2026 23:44
@nikswamy
Copy link
Copy Markdown
Contributor Author

ping @protz

Would be great to have your feedback and get this to a mergeable state ASAP. We have some clients waiting on the feature.

@protz
Copy link
Copy Markdown
Collaborator

protz commented Apr 22, 2026

Thanks for the ping. I am at a workshop until tomorrow but expect to have time to review this before the end of the week. Thanks!

Copy link
Copy Markdown
Collaborator

@protz protz left a comment

Choose a reason for hiding this comment

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

High-level points.

  • I think it this transformation would be easier on CStar instead of C11. C11 is just an abstract syntax tree ready for pretty-printing. I expected this to operate over CStar because 1) we have no transformations currently operating on C11, because it's very low-level and hard to work with, 2) there are already helpers that exist on CStar, such as vars_of and 3) it would save you the contortions you're doing in replace_ident where you have to unpeel the spec-and-decl in a convoluted way (see point 1). Also, returns are already explicit in CStar so it sounds like you'd be better off there, and I don't think it's a big deal to add Label and Goto to CStar for such purposes (it's not type-checked).
  • Why do multiple returns imply early return? (comment on has_early_return): void f() { if (true) return; else return; } does not have early returns in my opinion, because both are in terminal position (they do not happen before we reach the end of the function). Perhaps this is a terminology thing, or perhaps the check needs to be updated. Can you clarify? I expected has_early_return would take an extra parameter that indicates whether you're in terminal position, and throw Found when hitting a Return node.
  • It might be that you can achieve better code quality by operating on CStar. I notice that you rewrite Return e into { ret_var = e; goto exit } with an additional C.Compound. It feels like it'd be easier to write flatten_compounds on CStar than C11.
  • I would error out if a void function returns a value (line 120). Actually, I'd be curious to see what let f (x: unit) = if true then x else () -- I suspect unit-to-void elimination will kick in, but a test case would be helpful
  • Another instance of point 3) above: I don't think it's super clear what is_void_return does on the C11 syntactic structure, and this function would be crystal clear in CStar -- also you would avoid matching on bool vs BOOLEAN, etc.
  • Your zero-initializer can be { 0 } for all types. This works:
int main() {
  _Bool x = { 0 };
  return x;
}
  • Would it make sense to have your test be a pulse test? It would then automatically track the generated C. (And I don't think it has to use pulse.)

Requested changes: switch the pass to operate over CStar + answers to questions above. Thank you!

@nikswamy
Copy link
Copy Markdown
Contributor Author

Thanks! A quick response to this part:

Your zero-initializer can be { 0 } for all types.

This was the initial version. I then had it changed to use a type-specific initializer, as this is more idiomatic in the context where we are using it. Would you be okay with retaining a type-specific initializer?

Adds a -goto_for_early_return CLI option. When enabled, functions with
early returns are transformed to use goto-based control flow:
- A result variable is allocated at the top (for non-void functions)
- Every early return is replaced by assignment + goto
- A label and final return are appended at the end

The pass operates on the CStar AST (before lowering to C11) for simpler
type access, natural block flattening, and use of existing helpers.

Detection uses a position-aware walk that distinguishes terminal returns
(in tail position) from early returns (in non-terminal position).

Includes tests covering int32, uint32, uint64, int64, bool, void, struct,
name collision avoidance, no-early-return passthrough, and unit/void
interaction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nikswamy nikswamy force-pushed the _goto_for_early_return branch from 78b5c99 to e67ac84 Compare April 22, 2026 20:20
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