Skip to content

Support returning structures from functions#243

Merged
laurenthuberdeau merged 9 commits into
mainfrom
laurent/returning-structs-from-functions-take-2
Jun 11, 2026
Merged

Support returning structures from functions#243
laurenthuberdeau merged 9 commits into
mainfrom
laurent/returning-structs-from-functions-take-2

Conversation

@laurenthuberdeau

@laurenthuberdeau laurenthuberdeau commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Context

The bootstrap of TCC's 64-bit integers is complicated and fragile, requiring a large set of patches and workarounds. It would be much easier (and correct) to bootstrap TCC with a compiler that already supports 64-bit integers, removing the need for much of these patches, and potentially reducing the number of intermediate TCC binaries that must be generated.

However, pnut's integers are limited by the machine's word size, as anything wider breaks the assumption that integers fit in a single machine word. This PR adds support for functions returning structures, which, with the preexisting support for passing structures as argument, unblocks support for 64-bit (and possibly floating point) arithmetic by replacing all wide arithmetic operations by function calls.

Implementation Details

The implementation follows a variant of the System V ABI, requiring the caller to pass to the callee a pointer to the memory location where to write the result. To keep the implementation simple, callers always allocate on the stack a temporary buffer where the value is written to by the callee. After the callee returns control to the caller, the temporary value is then written to its final location or dropped.

A temporary may be used as part of a larger expression, meaning it must outlive the expression that created it. To simplify the code generator, we don't track the exact lifetime of temporaries (nor reuse them) and instead just let them accumulate on the stack until the end of the statement, where they are all freed at once by codegen_statement's stack cleanup.

However, there are cases where a temporary must be freed before the end of the statement to keep the stack balanced:

  • When evaluating a condition, the temporary never escapes the condition expression, and would pile up on the stack during loops if not freed right away. Handled by codegen_rvalue_and_cmp_0.
  • When evaluating a ternary operator condition, since the individual arms may allocate a different number of temporaries. Handled by codegen_ternary_arm.
  • When generating a function call, since the temporary would sit between the arguments and/or function pointer (for indirect calls). Handled by codegen_rvalue_and_drop_temps.
  • When evaluating a local variable initializer, since temporary values would offset the local variable's address which is assumed to not change. Handled by codegen_rvalue_and_drop_temps.

Impact on bootstrap scripts

No size difference for pnut-sh and pnut-awk. 20 more lines for minimal pnut-exe.

Main:

Variant                C     Clean  Preproc  .sh   Runtime  Blank  .awk  R(C)  R(clean)  R(preproc)
pnut-sh (min)          9171  6994   4214     6133  430      236    4858  .668  .876      1.455
pnut-sh                9171  6994   5242     7644  517      265    6039  .833  1.092     1.458
pnut-awk (min)         7459  5598   3569     5338  428      220    4224  .715  .953      1.495
pnut-awk               7459  5598   4202     6293  515      241    4874  .843  1.124     1.497
pnut-i386_linux (min)  9480  6853   4087     5684  432      284    4617  .599  .829      1.390
pnut-i386_linux        9480  6853   5036     7036  509      320    5592  .742  1.026     1.397

PR:

Variant                C     Clean  Preproc  .sh   Runtime  Blank  .awk  R(C)  R(clean)  R(preproc)
pnut-sh (min)          9171  6994   4214     6133  430      236    4858  .668  .876      1.455
pnut-sh                9171  6994   5242     7644  517      265    6039  .833  1.092     1.458
pnut-awk (min)         7459  5598   3569     5338  428      220    4224  .715  .953      1.495
pnut-awk               7459  5598   4202     6293  515      241    4874  .843  1.124     1.497
pnut-i386_linux (min)  9726  6967   4087     5704  432      289    4618  .586  .818      1.395
pnut-i386_linux        9726  6967   5118     7161  509      327    5685  .736  1.027     1.399

@laurenthuberdeau laurenthuberdeau merged commit c966fda into main Jun 11, 2026
60 checks passed
@laurenthuberdeau laurenthuberdeau deleted the laurent/returning-structs-from-functions-take-2 branch June 11, 2026 15:30
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