mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
todo cleanup
This commit is contained in:
1 parent
05d8acc4bb
commit
da7ea294ef
3 files changed
+31
-85
No files matched your search
@@ -46,6 +46,8 @@ Missing VM Implementations (VirtualMachine.kt)
|
||||
- ``IRInlineAsmChunk`` with ``isIR=true`` (IR-form inline asm) is not executable: ``VmProgramLoader.kt`` only converts the trivial ``"clc"``/``"sec"`` chunks and throws on everything else. This is the designed-for home of IR-form asm, since ``AstChecker`` restricts ``asm { %ir }`` to the virtual target only; the VM should parse and run the IR instruction lines contained in such chunks. Raw (non-IR) inline asm and ``IRAsmSubroutine`` should NOT be made executable (the VM has no 6502/68k CPU): instead, reject non-IR ``asm`` blocks and ``asmsub`` with a frontend error on the virtual target (currently they pass ``AstChecker`` and only crash at VM load). ``IRInlineBinaryChunk`` (``%binary`` include) stays deferred: the program is not loaded as data, so an embedded blob has no place.
|
||||
Source line tracking in new codegen backends
|
||||
- Improve source line tracking across the IR into the generated assembly code in the new codegen backends (new6502, m68k). Currently, the IR preserves some source position information, but this is not consistently propagated through to the final assembly output. Better tracking would improve debugging experience (e.g., in monitor/debugger tools) and make it easier to correlate generated assembly back to the original Prog8 source code. Consider adding source location metadata to IR instructions and ensuring code generators emit appropriate ``.line`` directives or comments in the assembly output.
|
||||
Recursion-sound defers via framebase-as-parameter
|
||||
- Push ``(id, framebase)`` pairs on the defer handler stack, pass the parent's bottom-of-frame address as a hidden trailing parameter, lower parent-local accesses to indirect framebase-relative accesses. Lifts the current rejection of recursive deferring subs on m68k targets. VM parity only (not needed for m68k): running such IR on the VM would also need VM activation records in linear memory so saved frame bases are addresses (currently won't-do, see "Won't do: VM stack frames" above).
|
||||
|
||||
|
||||
Libraries
|
||||
@@ -61,7 +63,7 @@ Optimizations
|
||||
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once new 6502 codegen is done from IR code, those 6502 only optimizations should probably be removed
|
||||
- M68K: replace prog8 routines in math library by optimized 68000 assembly versions.
|
||||
- M68K stack/frame follow-ups (see ``ideas/m68k-frames-followups.md``): the stack-based memory model for locals and parameters is implemented, including frame slots for locals, parameters in the caller-pushed incoming area (word-packed argument slots are now the default convention), per-activation virtual registers for re-entrant subroutines, prologue zero-store coalescing, and diagnostics/polish items. Remaining optimization work: register argument passing (section 2), reserved outgoing-argument area (section 3), recursion-sound defers via framebase-as-parameter (section 4), and loop-aware liveness for frame slot reuse (section 6). See the user-facing description in ``technical.rst``.
|
||||
- M68K stack/frame follow-ups: the stack-based memory model for locals and parameters is implemented, including frame slots for locals, parameters in the caller-pushed incoming area (word-packed argument slots are now the default convention), per-activation virtual registers for re-entrant subroutines, prologue zero-store coalescing, and diagnostics/polish items. Remaining low-pri optimization work: reserved outgoing-argument area (allocate the max call-site arg size once in the caller's ``link`` frame; benchmark-driven, likely marginal) and loop-aware liveness for frame slot reuse (needs real backward live-in/live-out dataflow over the CFG; frame-size-driven). Register argument passing lives in ``ideas/m68k-register-allocation.md`` §9; recursion-sound defers is an IR/VM item above. See the user-facing description in ``technical.rst``.
|
||||
|
||||
|
||||
Dead Code Elimination bug in 64tass, for nested subroutines
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# M68K Frames Follow-ups
|
||||
|
||||
**Status: v1 implemented** on `m68k-stack` (uniform all-stack convention, caller cleanup).
|
||||
User-facing description: `docs/source/technical.rst` ("M68K memory model and calling convention").
|
||||
Implementation: `StackFrameLayout`, `AddressBase.FrameSlot` / `CallLocation.FrameSlot`,
|
||||
`IRSubroutine.frameSize/incomingSize/frameVregSlots`. Decision: the VM does not get
|
||||
activation records (see `docs/source/todo.rst`, "Won't do: VM stack frames"); it keeps
|
||||
rejecting frame-based IR with an explicit error.
|
||||
|
||||
This file preserves the convention-level rationale from the deleted
|
||||
`ideas/m68k-stack-memory-model.md` (§6.1/§6.4) so future optimizations stay variants
|
||||
of v1, never second conventions.
|
||||
|
||||
## Why v1 is uniform 4-byte slots
|
||||
|
||||
- v1 follows the strict System V m68k scheme: every argument gets one padded longword slot,
|
||||
narrow values right-justified (`move.b x,3(sp)` / `move.w x,2(sp)` / `move.l x,(sp)`),
|
||||
param `i` of `N` at `8+4*(N-i)(a5)`, caller pops `N*4` after `jsr`, callee epilogue is
|
||||
always `unlk a5` / `rts` regardless of arity.
|
||||
- Rejected for the baseline: all-register (struct-by-value params have no register form),
|
||||
hybrid first-scalar-in-D0/first-pointer-in-A0 (needs the register allocator to pay off;
|
||||
without it, it just swaps `move.l x,-(sp)` for `move.l x,d0` plus clobber-ordering hazards
|
||||
and mandatory copy-ins, doubling marshalling paths and tests).
|
||||
- `asmsub`/`extsub` keep their explicit register-annotation ABI as a separate lane; normal
|
||||
subs calling them (or vice versa) follow §6.3 push rules. A Linux/GCC-style word-packed
|
||||
scheme (natural sizes, 16-bit alignment) is legal on 68000; **it has since been implemented
|
||||
as the default convention** (IR format 8), so the sections about the uniform 4-byte layout
|
||||
in this and older documents no longer describe the current behavior.
|
||||
|
||||
## Follow-ups (each needs its own design review + test pass)
|
||||
|
||||
### 2. Register argument passing
|
||||
|
||||
Paired caller/callee specialization over the fixed stack convention (e.g. first scalar
|
||||
in D0, first pointer in A0, rest on stack), applied only where it measurably wins (small
|
||||
scalar signatures). Requires the register allocator (`ideas/m68k-register-allocation.md`)
|
||||
first.
|
||||
|
||||
### 3. Recursion-sound defers via framebase-as-parameter
|
||||
|
||||
Push (id, framebase) pairs on the defer handler stack, pass the parent's bottom-of-frame
|
||||
address as a hidden trailing parameter, lower parent-local accesses to indirect
|
||||
framebase-relative accesses. Lifts the current rejection of recursive deferring subs.
|
||||
Also needs VM activation records in linear memory so saved frame bases are addresses.
|
||||
|
||||
### 4. Loop-aware liveness for frame slot reuse
|
||||
|
||||
**Priority: low, frame-size-driven.**
|
||||
Today vreg frame slots are shared only when live ranges provably don't overlap, and any
|
||||
back edge (loop, backward branch, cross-sub jump) falls back to one slot per register.
|
||||
Lifting needs real backward live-in/live-out dataflow over the CFG; interval extension
|
||||
alone is unsound.
|
||||
|
||||
### 5. Reserved outgoing-argument area
|
||||
|
||||
**Priority: low, benchmark-driven.** Allocate the max call-site arg size once in the
|
||||
caller's `link` frame, write args via negative A5 offsets, and drop per-call `addq/lea`
|
||||
cleanup. This may help tight loops with repeated multi-argument calls, but gains are
|
||||
likely marginal: direct frame stores can be larger, frameless callers would gain a
|
||||
frame, and every caller's frame would grow. Frame-size limits must count the area.
|
||||
|
||||
### 6. Prologue zero-store coalescing (done)
|
||||
|
||||
**Status: implemented.** Clean frame locals are grouped and merged into widest aligned
|
||||
stores; long runs lower to a `dbra` loop in the m68k backend. Floats and
|
||||
dirty/initialized locals remain boundaries. Details were in
|
||||
`ideas/m68k-prologue-zero-coalescing.md` (removed now that the work is done).
|
||||
|
||||
### 7. Word-packed argument slots (done)
|
||||
|
||||
**Status: implemented.**: implemented as the default convention (IR format 8, documented in
|
||||
`docs/source/technical.rst`). Linux/GCC scheme: byte/bool = 2 bytes incl. pad,
|
||||
word = 2, long/float/pointer = 4, still even-aligned. Saves stack space and bus traffic
|
||||
per narrow arg. Cost: frame layout sums per-arg rounded sizes instead of constant
|
||||
stride; documented `asmsub` push rules change; hand-written asm (e.g. the stdlib
|
||||
`die()` subs) and `.p8ir` consumers were updated accordingly.
|
||||
@@ -112,13 +112,15 @@ on the convention decisions in §2, not on the allocator existing.
|
||||
|
||||
Per the sequencing decision, the stack-frame implementation
|
||||
lands *before* this allocator, so the convention this allocator is built
|
||||
against is already the final one - the uniform all-stack convention documented
|
||||
for M68K targets:
|
||||
against is already the final one - the word-packed all-stack convention documented
|
||||
for M68K targets (IR format 8):
|
||||
|
||||
- **Normal subs:** the caller pushes every argument to a longword slot
|
||||
left-to-right (`-(sp)`), `jsr`s, and pops the area after the return
|
||||
- **Normal subs:** the caller pushes every argument left-to-right into a
|
||||
word-packed slot (byte/bool/word = 2 bytes, long/float/pointer = 4),
|
||||
`jsr`s, and pops the whole area after the return
|
||||
(caller cleanup). The callee reads arguments from positive A5 offsets as
|
||||
incoming frame slots.
|
||||
incoming frame slots, accumulating from the rightmost argument at `8(a5)`
|
||||
by per-argument slot width.
|
||||
- **asmsub/extsub:** arguments continue to go into fixed hardware slots via
|
||||
`CallingConventionSlot`: slots 10..17 → D0..D7, 18..24 → A0..A6,
|
||||
25..32 → FP0..FP7. In practice asmsub args/returns use D0–D2 and FP0–FP1
|
||||
@@ -131,9 +133,16 @@ Consequences for the allocator:
|
||||
callee-side parameters need no special allocator treatment while they stay
|
||||
memory-resident. Once the callee loads a parameter into a vreg to compute
|
||||
with it, that vreg is allocated like any other.
|
||||
- Word-packed slot widths constrain callee-side reads: a byte/bool parameter
|
||||
occupies a 2-byte slot whose second byte is garbage, so it must be read
|
||||
byte-size-only (`move.b`), never as a word load of the slot. Word params
|
||||
fill their 2-byte slot and wide params their 4-byte slot, so those read at
|
||||
full width. This feeds the §7.1 #1 width semantics: loading an incoming
|
||||
parameter at the wrong width reads garbage.
|
||||
- Caller-side pushes are convention traffic, not allocation: the argument
|
||||
value (in whatever register its vreg is allocated) is stored to `-(sp)`
|
||||
like any memory move; the `addq` cleanup between pushes and the call
|
||||
value (in whatever register its vreg is allocated) is stored to its slot
|
||||
(`move.b` / `move.w` / `move.l x,-(sp)`) like any memory move; the
|
||||
caller-side cleanup after the call
|
||||
disturbs nothing held in registers (SP itself is never allocated).
|
||||
- Register argument *passing* is a separate later optimization, layered on top
|
||||
of this convention once the allocator works.
|
||||
@@ -531,7 +540,8 @@ points. Three possible spill targets, with different soundness properties:
|
||||
spills become frame-resident: per-invocation, recursion-safe, and cheaper
|
||||
addressing (`move.l -8(a5),d0`). The frame layout pass does not yet budget
|
||||
generic "compiler spill slots" for the allocator — it currently lays out
|
||||
locals, the incoming argument area, and `frameVregSlots` for re-entrant
|
||||
locals, the incoming argument area (sized by summed rounded slot widths,
|
||||
always even), and `frameVregSlots` for re-entrant
|
||||
subroutines only — so the slot-allocation policy is Stage-2 work (§7.1 #3).
|
||||
- **SP-reserved spill area (proposed fallback for frameless subroutines):** for
|
||||
subroutines the frame-layout pass leaves frameless (`hasFrame == false`,
|
||||
@@ -893,3 +903,13 @@ structure) covering:
|
||||
- Caller and callee both spilling in a call chain: stack/frame spills do not
|
||||
collide; BSS spill slots are program-wide unique.
|
||||
- Interrupt handlers save/restore every register they touch.
|
||||
|
||||
---
|
||||
|
||||
## 9. Future: register argument passing
|
||||
|
||||
Paired caller/callee specialization over the fixed stack convention (e.g. first scalar
|
||||
in D0, first pointer in A0, rest on stack), applied only where it measurably wins (small
|
||||
scalar signatures). Builds on the allocator in this document (§2.1, §2.6); when it lands,
|
||||
fold immediate-forwarding into the call-boundary marshalling on allocator liveness and
|
||||
delete `ImmediateCallOptimization` (Stage 2 step 9).
|
||||
Reference in new issue
Block a user