mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
when->on..goto: calibrate the jump-table sizeLimit per target
This commit is contained in:
1 parent
3a5f2c33ab
commit
06d2f9ebbe
3 files changed
+12
-11
No files matched your search
@@ -2442,9 +2442,6 @@ internal class AstChecker(private val program: Program,
|
||||
if(whenStmt.condition.constValue(program)!=null)
|
||||
errors.warn("when-value is a constant and will always result in the same choice", whenStmt.condition.position)
|
||||
|
||||
if(whenStmt.onGotoIndexOffset(program, options)!=null)
|
||||
errors.info("when statement can be replaced with on..goto", whenStmt.position)
|
||||
|
||||
super.visit(whenStmt)
|
||||
}
|
||||
|
||||
|
||||
@@ -1614,10 +1614,15 @@ class When(var condition: Expression,
|
||||
* a jump table isn't possible or not worthwhile here.
|
||||
*/
|
||||
fun onGotoIndexOffset(program: Program, compilerOptions: CompilationOptions): Int? {
|
||||
val sizeLimit = when {
|
||||
compilerOptions.compTarget.cpu == CpuType.CPU65C02 -> 4
|
||||
compilerOptions.compTarget.cpu.is68k -> 5
|
||||
else -> 6
|
||||
// Jump-table cutoff per CPU family, picked by the resource that family is short of:
|
||||
// 6502 by size (11; on plain 6502 the table is not even faster below ~15, since its
|
||||
// dispatch lacks jmp (abs,x)), m68k/virtual by speed (6/4, well under the m68k size
|
||||
// break-even), 65C02 keeps 4 (cheap jmp (abs,x); short hot dispatches like gfx_hires).
|
||||
val (sizeLimit, spanFactor) = when {
|
||||
compilerOptions.compTarget.cpu == CpuType.CPU65C02 -> 4 to 2
|
||||
compilerOptions.compTarget.cpu == CpuType.CPU6502 -> 11 to 2
|
||||
compilerOptions.compTarget.cpu.is68k -> 6 to 4
|
||||
else -> 4 to 4
|
||||
}
|
||||
if(choices.size >= sizeLimit) {
|
||||
if (condition.inferType(program).isUnsignedByte) {
|
||||
@@ -1647,8 +1652,9 @@ class When(var condition: Expression,
|
||||
val span = sortedValues.last() - lowest + 1
|
||||
// cap the table so that a sparse set of values can't blow the table up out of all
|
||||
// proportion to the number of choices; within the cap the O(1) dispatch wins even
|
||||
// when the table itself is a bit bigger than the compare chain it replaces
|
||||
if(span <= sortedValues.size * 2)
|
||||
// when the table itself is a bit bigger than the compare chain it replaces.
|
||||
// the cap is looser on m68k/virtual where a bigger table costs no scarce memory
|
||||
if(span <= sortedValues.size * spanFactor)
|
||||
return lowest
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,6 @@ Optimizations
|
||||
- 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: 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``.
|
||||
- ``on..goto``: the index is loaded twice per dispatch. The index is read once for the ``index < numlabels`` bounds check and again for the table lookup, on every backend (cx16 emits ``lda sel`` twice with ``cmp #6``/``bcs`` in between; m68k emits ``move.b sel,p8_regfile+0`` then ``move.b sel,p8_regfile+2``). Making ``CodeDesugarer.after(OnGoto)`` always use a temp var is *not* the fix: measured on cx16 that grows a 6-way dispatch from 13 to 16 bytes, since it replaces the first load with ``lda``/``sta`` and still reloads the temp. The value is genuinely needed twice at the AST level, so this needs register reuse in ``IRPeepholeOptimizer``: ``removeDoubleLoadsAndStores`` only works within one chunk, whereas these two loads sit either side of a ``bge``. Note the file's own comment there already lists "detect multiple loads to the same target registers, only keep first (if source is not I/O memory)" as a known un-implemented optimization. A cross-chunk version must first prove the fall-through is the only way into the second chunk, since another branch or jump there would leave the register undefined; there is no liveness data in the IR to lean on (``RegisterPool`` just hands out the next free register).
|
||||
- Calibrate the ``when`` → ``on..goto`` ``sizeLimit`` per target instead of the current empirical 4 (65C02) / 5 (m68k) / 6 (other), and pick each target's limit by what that target is actually short of. The 6502 targets are severely memory-constrained, so their limit is a **size** decision: on cx16 (6502, 16-bit pointers) the table already wins from 4 choices up (348 vs 353 bytes at 4 choices, 362 vs 375 at 6), growing ~7 bytes per choice against ~14 for the compare chain, so the cx16 limit of 4 is about right. The m68k targets have plenty of RAM (an Amiga is not short of the C64/X16's few kilobytes), so their limit is a **speed** decision, not a size one: the table is ~30% fewer VM instructions for the dispatch at every size measured, and on qemu68k the 32-bit table slots make it larger until ~20 choices (+103 bytes at 6, +63 at 8, break-even at ~12), but that larger table is still the faster dispatch. So the m68k limit should stay low (use the table eagerly) for the speed win, *not* be raised to the ~12-20 size break-even. The "only use the table when it is smaller" rule applies to the 6502 targets only. The sparse-value span cap (``span <= values.size * 2``) is the same kind of size heuristic and should be relaxed or skipped on m68k for the same reason.
|
||||
|
||||
|
||||
Dead Code Elimination bug in 64tass, for nested subroutines
|
||||
|
||||
Reference in new issue
Block a user