mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
IR: fix CONCAT register direction
This commit is contained in:
1 parent
58f544fe14
commit
bc62b2de81
2 files changed
+51
-20
No files matched your search
@@ -94,7 +94,41 @@ does not prevent total stack exhaustion through deep call chains or recursion.
|
||||
The compiler should report the estimated frame size and reject a subroutine
|
||||
whose frame exceeds the target limit.
|
||||
|
||||
## 4. Local Initialization
|
||||
## 4. Compiler Auto-Temporaries on the Stack (Optional)
|
||||
|
||||
The compiler creates a small number of internal temporary variables during AST
|
||||
desugaring. These `auto_heap_value_N` variables are generated by
|
||||
`CodeDesugarer` (ongoto jump tables, ongoto index temporaries, pointer
|
||||
dot-chain address temporaries) and `LiteralsToAutoVarsAndRecombineIdentifiers`
|
||||
(array-literal heap hoisting). There are only four creation sites, each
|
||||
producing one or two variables with short, well-defined lifetimes.
|
||||
|
||||
These auto-temporaries are ideal candidates for frame-slot allocation rather
|
||||
than static storage. They are written once, read a few times within a single
|
||||
expression or statement, then dead. They are never `@shared`, never have their
|
||||
address taken in user code, and never appear in inline assembly.
|
||||
|
||||
The recommended approach is:
|
||||
|
||||
1. Keep the existing `VarDecl` nodes in the AST. They are needed for semantic
|
||||
analysis, type checking, and optimizer passes (even though the optimizer
|
||||
rarely eliminates them).
|
||||
2. Mark them as compiler temporaries in the IR, either via a flag on
|
||||
`IRStStaticVariable` or by introducing a lightweight `IRFrameSlot` storage
|
||||
class.
|
||||
3. The M68K codegen allocates flagged temporaries to frame slots instead of
|
||||
emitting them to `.bss` or static memory.
|
||||
|
||||
This is a minor optimization on its own but becomes a natural part of the
|
||||
full stack frame implementation. Once subroutine locals live on the stack, these
|
||||
temporaries simply become frame-resident alongside user-declared locals. The
|
||||
main benefit is avoiding static storage consumption for variables that never
|
||||
need static addresses.
|
||||
|
||||
For the 6502 targets, these auto-temporaries continue to be statically
|
||||
allocated, consistent with the existing local-variable model.
|
||||
|
||||
## 5. Local Initialization
|
||||
|
||||
Prog8 variables are normally zero-initialized. The M68K implementation should
|
||||
preserve this semantic rule, but need not clear every byte of every frame.
|
||||
@@ -115,13 +149,13 @@ The simplest first implementation is:
|
||||
An alternative is to define stack locals as undefined on M68K, but that would
|
||||
make the target's semantics differ unnecessarily from the other targets.
|
||||
|
||||
## 5. Calling Convention Impact
|
||||
## 6. Calling Convention Impact
|
||||
|
||||
Yes, this changes the internal calling convention unless parameters remain
|
||||
static. Keeping parameters static would allow a partial implementation, but it
|
||||
would not make normal subroutines fully recursive or reentrant.
|
||||
|
||||
### 5.1 Normal Prog8 subroutines
|
||||
### 6.1 Normal Prog8 subroutines
|
||||
|
||||
The current convention writes arguments into named parameter variables before
|
||||
the call. That cannot work when those parameter variables are in the callee's
|
||||
@@ -146,7 +180,7 @@ stable address or when the value must survive a call.
|
||||
The exact choice must also account for byte, word, long, pointer, and float
|
||||
values, including alignment and multi-value returns.
|
||||
|
||||
### 5.2 Returns
|
||||
### 6.2 Returns
|
||||
|
||||
The existing return convention can remain initially: ordinary scalar returns
|
||||
use the established M68K result registers, while explicit `asmsub` register
|
||||
@@ -156,7 +190,7 @@ does not require a frame change, provided the epilogue runs before `rts`.
|
||||
All return paths must branch through a common epilogue or otherwise emit the
|
||||
same A5 restoration and callee-saved-register restoration.
|
||||
|
||||
### 5.3 `asmsub`, `extsub`, and AmigaOS calls
|
||||
### 6.3 `asmsub`, `extsub`, and AmigaOS calls
|
||||
|
||||
The external calling convention should remain separate from normal Prog8
|
||||
subroutines:
|
||||
@@ -170,7 +204,7 @@ subroutines:
|
||||
This separation prevents the normal frame implementation from changing the
|
||||
existing AmigaOS ABI.
|
||||
|
||||
## 6. IR Changes
|
||||
## 7. IR Changes
|
||||
|
||||
The IR should describe storage symbolically rather than naming A5 directly.
|
||||
Possible additions include:
|
||||
@@ -194,7 +228,7 @@ generated from `IRSubroutine` metadata is an implementation choice. Backend-
|
||||
generated prologues are preferable initially because saved-register selection,
|
||||
instruction forms, and frame-pointer choice are backend details.
|
||||
|
||||
## 7. Backend Changes
|
||||
## 8. Backend Changes
|
||||
|
||||
`codeGenM68k` must:
|
||||
|
||||
@@ -212,7 +246,7 @@ The register-allocation design must distinguish A5 from ordinary callee-saved
|
||||
registers. A5 is not merely another register that a subroutine may save and
|
||||
reuse; it is the base for all active frame slots.
|
||||
|
||||
## 8. VM Changes
|
||||
## 9. VM Changes
|
||||
|
||||
To execute the same IR model, the VM needs an activation record per call. A
|
||||
call must allocate the callee's frame slots, make the current frame available
|
||||
@@ -225,7 +259,7 @@ abstract frame-slot semantics.
|
||||
If VM support is deferred, the compiler should reject frame-based IR when the
|
||||
VM is selected rather than silently treating frame slots as static variables.
|
||||
|
||||
## 9. Address-Taking and Lifetime
|
||||
## 10. Address-Taking and Lifetime
|
||||
|
||||
`&local` would produce an address into the current frame. That address is valid
|
||||
only until the subroutine returns. The compiler must either:
|
||||
@@ -237,7 +271,7 @@ Variables referenced by inline assembly, external code, `@shared`, or a stored
|
||||
pointer should remain static unless the compiler can prove that the reference
|
||||
does not escape.
|
||||
|
||||
## 10. Defer, sys.exit(), and Stack Unwinding
|
||||
## 11. Defer, sys.exit(), and Stack Unwinding
|
||||
|
||||
Mechanical stack unwinding on M68K is straightforward: the epilogue restores the
|
||||
caller's frame pointer and stack pointer with `unlk a5`, and `rts` pops the
|
||||
@@ -284,7 +318,7 @@ Possible approaches:
|
||||
2. **Keep defer-referenced locals static.** Variables whose addresses are taken,
|
||||
that are referenced by inline assembly, or that are referenced by `defer`
|
||||
stay in static storage; everything else can move to the stack. This matches
|
||||
the address-escape rule in Section 9.
|
||||
the address-escape rule in Section 10.
|
||||
3. **Pass the parent frame pointer to the handler.** The handler subroutine
|
||||
receives the parent's A5 value and uses it to access parent locals. This is
|
||||
awkward because the handler itself may need a frame, requiring two frame
|
||||
@@ -294,7 +328,7 @@ For a first implementation, option 1 (inlining) or option 2 (hybrid static
|
||||
locals) is recommended. Option 3 adds calling-convention complexity that is not
|
||||
justified until the rest of the stack model is stable.
|
||||
|
||||
## 11. Effects on Other Targets
|
||||
## 12. Effects on Other Targets
|
||||
|
||||
The 6502 targets remain unchanged:
|
||||
|
||||
@@ -306,7 +340,7 @@ The IR and frontend changes must therefore be conditional on the compilation
|
||||
target or represented in a way that the 6502 code generators can continue to
|
||||
lower without seeing frame storage.
|
||||
|
||||
## 12. Benefits
|
||||
## 13. Benefits
|
||||
|
||||
- Recursive M68K subroutines become possible.
|
||||
- Subroutines become reentrant and safer for callbacks and task-like use.
|
||||
@@ -314,7 +348,7 @@ lower without seeing frame storage.
|
||||
- Multiple simultaneous invocations receive independent local state.
|
||||
- The M68K backend gets a conventional, efficient local-access mechanism.
|
||||
|
||||
## 13. Risks and Open Questions
|
||||
## 14. Risks and Open Questions
|
||||
|
||||
- Stack exhaustion becomes a runtime possibility.
|
||||
- Prologues, epilogues, argument marshalling, and selective initialization add
|
||||
@@ -323,7 +357,7 @@ lower without seeing frame storage.
|
||||
- Address-taking and pointer lifetime rules need to be defined.
|
||||
- `defer` code must be able to access the parent subroutine's locals; the
|
||||
current handler-subroutine design needs to be replaced or restricted when
|
||||
locals live on the stack (see Section 10).
|
||||
locals live on the stack (see Section 11).
|
||||
- Inline assembly must respect A5 reservation.
|
||||
- Frame layout must cooperate with register allocation and spill slots.
|
||||
- Zero initialization may require additional analysis and generated code.
|
||||
@@ -331,7 +365,7 @@ lower without seeing frame storage.
|
||||
- Existing assembly or code that assumes parameter variables have static symbols
|
||||
may need to remain explicitly static.
|
||||
|
||||
## 14. Suggested Implementation Order
|
||||
## 15. Suggested Implementation Order
|
||||
|
||||
1. Define the M68K frame-slot metadata and A5-based layout rules.
|
||||
2. Keep all parameters static temporarily and implement ordinary local frames
|
||||
|
||||
@@ -812,9 +812,6 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.MUL to InstructionFormat.from("BWL,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.MULM to InstructionFormat.from("BWL,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.SUBIM to InstructionFormat.from("BWL,<i,<>a | F,<i,<>a"),
|
||||
Opcode.MULR to InstructionFormat.from("BWL,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
Opcode.MUL to InstructionFormat.from("BWL,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.MULM to InstructionFormat.from("BWL,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.MULSR to InstructionFormat.from("BWL,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
Opcode.MULS to InstructionFormat.from("BWL,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.MULSM to InstructionFormat.from("BWL,<r1,<>a | F,<fr1,<>a"),
|
||||
@@ -913,7 +910,7 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.POP to InstructionFormat.from("BWL,>r1 | F,>fr1"),
|
||||
Opcode.PUSHST to InstructionFormat.from("N"),
|
||||
Opcode.POPST to InstructionFormat.from("N"),
|
||||
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
|
||||
Opcode.CONCAT to InstructionFormat.from("BW,>r1,<r2,<r3"),
|
||||
Opcode.CLC to InstructionFormat.from("N"),
|
||||
Opcode.SEC to InstructionFormat.from("N"),
|
||||
Opcode.CLI to InstructionFormat.from("N"),
|
||||
|
||||
Reference in new issue
Block a user