diff --git a/codeCore/test/prog8tests/codecore/TestConfigFileTarget.kt b/codeCore/test/prog8tests/codecore/TestConfigFileTarget.kt index 43c1a960f..b28149054 100644 --- a/codeCore/test/prog8tests/codecore/TestConfigFileTarget.kt +++ b/codeCore/test/prog8tests/codecore/TestConfigFileTarget.kt @@ -1,7 +1,7 @@ package prog8tests.codecore -import io.kotest.core.spec.style.FunSpec import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import prog8.code.core.BaseDataType import prog8.code.target.ConfigFileTarget diff --git a/codeCore/test/prog8tests/codecore/TestDataType.kt b/codeCore/test/prog8tests/codecore/TestDataType.kt index 29daf15b9..b89f0df9e 100644 --- a/codeCore/test/prog8tests/codecore/TestDataType.kt +++ b/codeCore/test/prog8tests/codecore/TestDataType.kt @@ -2,8 +2,6 @@ package prog8tests.codecore import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.booleans.shouldBeFalse -import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import prog8.code.core.* diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/SymbolPrefixer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/SymbolPrefixer.kt index dc00f4c0f..9ff2c00d6 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/SymbolPrefixer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/SymbolPrefixer.kt @@ -2,7 +2,10 @@ package prog8.codegen.intermediate import prog8.code.* import prog8.code.ast.* -import prog8.code.core.* +import prog8.code.core.AssemblyError +import prog8.code.core.CompilationOptions +import prog8.code.core.DataType +import prog8.code.core.ISubType /** diff --git a/codeGenIntermediate/test/TestRegisterPacker.kt b/codeGenIntermediate/test/TestRegisterPacker.kt index 4d4d18200..221449aaf 100644 --- a/codeGenIntermediate/test/TestRegisterPacker.kt +++ b/codeGenIntermediate/test/TestRegisterPacker.kt @@ -10,7 +10,6 @@ import prog8.code.core.ZeropageType import prog8.code.target.VMTarget import prog8.codegen.intermediate.RegisterPacker import prog8.intermediate.* -import prog8tests.helpers.ErrorReporterForTests class TestRegisterPacker: FunSpec({ diff --git a/codeGenM68k/src/prog8/codegen/m68k/InstrControl.kt b/codeGenM68k/src/prog8/codegen/m68k/InstrControl.kt index ea6cbbea0..992454ac5 100644 --- a/codeGenM68k/src/prog8/codegen/m68k/InstrControl.kt +++ b/codeGenM68k/src/prog8/codegen/m68k/InstrControl.kt @@ -17,7 +17,6 @@ package prog8.codegen.m68k import prog8.code.core.CpuType -import prog8.code.core.Statusflag import prog8.code.core.toHex import prog8.code.target.Amiga500Target import prog8.intermediate.* @@ -216,7 +215,6 @@ internal fun AsmGen.translateControl(insn: IRInstruction, forwardedImmediateCall Opcode.MSIGB -> { val dstReg = r1 ?: error("MSIGB needs reg1") val srcReg = r2 ?: error("MSIGB needs reg2") - val type = insn.type ?: IRDataType.WORD // Big-endian: MSB is at byte offset 0 for both word and long. emitLine("move.b ${regAddrByte(srcReg, 0)}, d0") emitLine("move.b d0, ${regAddr(dstReg)}") @@ -742,7 +740,7 @@ private fun AsmGen.translateReturnValue(ret: FunctionCallArgs.RegSpec) { // FAC1, FAC2) used by target-independent builtins (e.g. divmod returns its // quotient in AY). Map them onto distinct M68k hardware registers so the // backend can emit code for them. -fun m68kSlotRegister(slot: prog8.intermediate.CallingConventionSlot): String = when (slot.value) { +fun m68kSlotRegister(slot: CallingConventionSlot): String = when (slot.value) { in 0..7 -> error("slots 0-7 should never be used on the M68K they are 6502 cpu registers") in 10..17 -> "d${slot.value - 10}" // M68k slots: D0-D7 in 18..24 -> "a${slot.value - 18}" // M68k slots: A0-A6 diff --git a/codeGenM68k/test/TestInstructionSelectionOptimizations.kt b/codeGenM68k/test/TestInstructionSelectionOptimizations.kt index c05331311..b4bc5ecb7 100644 --- a/codeGenM68k/test/TestInstructionSelectionOptimizations.kt +++ b/codeGenM68k/test/TestInstructionSelectionOptimizations.kt @@ -4,11 +4,7 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.engine.spec.tempdir import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.shouldBe -import prog8.code.core.CompilationOptions -import prog8.code.core.OutputType -import prog8.code.core.Position -import prog8.code.core.Statusflag -import prog8.code.core.ZeropageType +import prog8.code.core.* import prog8.code.target.Amiga500Target import prog8.code.target.Qemu68kTarget import prog8.codegen.m68k.AsmGen diff --git a/codeGenNew6502/test/TestZeropageAllocator.kt b/codeGenNew6502/test/TestZeropageAllocator.kt index 56e96dab1..39be9f3b5 100644 --- a/codeGenNew6502/test/TestZeropageAllocator.kt +++ b/codeGenNew6502/test/TestZeropageAllocator.kt @@ -7,8 +7,8 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import prog8.code.core.* import prog8.code.target.Cx16Target -import prog8.intermediate.* import prog8.codegen.new6502.ZeropageAllocator +import prog8.intermediate.* class TestZeropageAllocator : FunSpec({ diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 5c076501b..59be979e8 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -6,8 +6,8 @@ import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.core.parse import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple -import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.default +import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.multiple import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.choice diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index 8fe776452..2e8831a99 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -9,7 +9,10 @@ import prog8.ast.expressions.StaticStructInitializer import prog8.ast.expressions.StringLiteral import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor -import prog8.code.core.* +import prog8.code.core.BuiltinFunctions +import prog8.code.core.ICompilationTarget +import prog8.code.core.IErrorReporter +import prog8.code.core.Position import prog8.code.target.VMTarget /** diff --git a/compiler/src/prog8/compiler/astprocessing/M68kAstChecker.kt b/compiler/src/prog8/compiler/astprocessing/M68kAstChecker.kt index 0f35f1981..93381b085 100644 --- a/compiler/src/prog8/compiler/astprocessing/M68kAstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/M68kAstChecker.kt @@ -8,11 +8,7 @@ import prog8.ast.statements.Directive import prog8.ast.statements.Subroutine import prog8.ast.statements.VarDecl import prog8.ast.walk.IAstVisitor -import prog8.code.core.ICompilationTarget -import prog8.code.core.IErrorReporter -import prog8.code.core.RegisterOrPair -import prog8.code.core.SplitWish -import prog8.code.core.ZeropageWish +import prog8.code.core.* internal class M68kAstChecker(private val errors: IErrorReporter, private val target: ICompilationTarget) : IAstVisitor { diff --git a/compiler/src/prog8/compiler/astprocessing/PrivateAccessChecker.kt b/compiler/src/prog8/compiler/astprocessing/PrivateAccessChecker.kt index 6b05a31ee..ea2b0c4e5 100644 --- a/compiler/src/prog8/compiler/astprocessing/PrivateAccessChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/PrivateAccessChecker.kt @@ -1,10 +1,6 @@ package prog8.compiler.astprocessing -import prog8.ast.Module -import prog8.ast.Node -import prog8.ast.ParentSentinel -import prog8.ast.Program -import prog8.ast.findParentNode +import prog8.ast.* import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.IdentifierReference import prog8.ast.statements.* diff --git a/compiler/test/TestPointers.kt b/compiler/test/TestPointers.kt index f33786d23..b7a23c0ec 100644 --- a/compiler/test/TestPointers.kt +++ b/compiler/test/TestPointers.kt @@ -14,11 +14,7 @@ import prog8.code.core.BaseDataType import prog8.code.core.DataType import prog8.code.core.IMemSizer import prog8.code.core.ISubType -import prog8.code.target.Amiga500Target -import prog8.code.target.C64Target -import prog8.code.target.Cx16Target -import prog8.code.target.Qemu68kTarget -import prog8.code.target.VMTarget +import prog8.code.target.* import prog8.vm.VmRunner import prog8tests.helpers.DummyMemsizer import prog8tests.helpers.ErrorReporterForTests @@ -1015,7 +1011,6 @@ main { sub1peek.builtin shouldBe true ((sub1peek.args[0] as PtBinaryExpression).right as PtNumber).number shouldBe 10.0 - val sub2expr1 = sub2[1] as PtAssignment val sub2expr2 = ((sub2[2] as PtAssignment).value as PtBinaryExpression) sub2expr2.operator shouldBe "-" val sub2expr3 = ((sub2[3] as PtAssignment).value as PtFunctionCall) @@ -2088,10 +2083,10 @@ main { } }""" - compileText(VMTarget(), false, src, outputDir) shouldNotBe null - compileText(Amiga500Target(), false, src, outputDir) shouldNotBe null - compileText(C64Target(), false, src, outputDir) shouldNotBe null - compileText(Cx16Target(), false, src, outputDir) shouldNotBe null + compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null + compileText(Amiga500Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null + compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null + compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null } test("array indexing a pointer and a pointer array both work") { diff --git a/compiler/test/ast/TestBuilders.kt b/compiler/test/ast/TestBuilders.kt index 24d4c8cf4..7be39c06d 100644 --- a/compiler/test/ast/TestBuilders.kt +++ b/compiler/test/ast/TestBuilders.kt @@ -2,7 +2,7 @@ package prog8tests.ast import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe -import prog8.ast.expressions.* +import prog8.ast.expressions.NumericLiteral import prog8.ast.statements.* import prog8.code.core.* diff --git a/compiler/test/codegeneration/TestArrayThings.kt b/compiler/test/codegeneration/TestArrayThings.kt index bd5417735..89308e62b 100644 --- a/compiler/test/codegeneration/TestArrayThings.kt +++ b/compiler/test/codegeneration/TestArrayThings.kt @@ -13,8 +13,8 @@ import prog8.code.target.Cx16Target import prog8.code.target.VMTarget import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText -import prog8tests.helpers.simulate import prog8tests.helpers.shouldContainInOrder +import prog8tests.helpers.simulate import kotlin.io.path.readText class TestArrayThings: FunSpec({ diff --git a/compiler/test/vm/TestRng.kt b/compiler/test/vm/TestRng.kt index 7d9e678ca..6f950320d 100644 --- a/compiler/test/vm/TestRng.kt +++ b/compiler/test/vm/TestRng.kt @@ -4,7 +4,9 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.engine.spec.tempdir import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import prog8.code.target.* +import prog8.code.target.Cx16Target +import prog8.code.target.Qemu68kTarget +import prog8.code.target.VMTarget import prog8.vm.VmRunner import prog8tests.helpers.CapturingSerialIO import prog8tests.helpers.compileText diff --git a/compilerAst/src/prog8/ast/Program.kt b/compilerAst/src/prog8/ast/Program.kt index f4c1e96e5..943d7301c 100644 --- a/compilerAst/src/prog8/ast/Program.kt +++ b/compilerAst/src/prog8/ast/Program.kt @@ -7,7 +7,10 @@ import prog8.ast.walk.IAstVisitor import prog8.code.GENERATED_LABEL_PREFIX import prog8.code.INTERNED_STRINGS_MODULENAME import prog8.code.PROG8_CONTAINER_MODULES -import prog8.code.core.* +import prog8.code.core.DataType +import prog8.code.core.ICompilationTarget +import prog8.code.core.Position +import prog8.code.core.ZeropageWish import prog8.code.source.SourceCode /*********** Everything starts from here, the Program; zero or more modules *************/ diff --git a/ideas/arrays-of-structs.md b/ideas/arrays-of-structs.md index 96afa3bc2..a38cf9515 100644 --- a/ideas/arrays-of-structs.md +++ b/ideas/arrays-of-structs.md @@ -9,7 +9,7 @@ the changes needed to support `structtype name[size]`. The feature is **explicitly gated** in the parser. Declaring an array of structures throws a hard error: -- `compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt:235-236` +- `compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt:234-235` ```kotlin } else if(baseDt.isStructInstance) @@ -20,21 +20,28 @@ The recommended workaround today is `structtype[size]*` — an array of pointers to separately-allocated structs — because the pointer element size is fixed (2 bytes on 6502, 4 on m68k) and needs no struct-size stride. +A second gate exists in `codeCore/src/prog8/code/core/DataTypes.kt:194-195`: +```kotlin +if(dt.isStructInstance) + TODO("cannot use struct instance as a data type (yet) - use a pointer instead") +``` +This must also be relaxed for struct-typed array elements. + ## What already exists (the hard parts are partly done) A surprising amount of the supporting machinery is already present: - **Array indexer accepts struct elements.** `PtArrayIndexer` - (`simpleAst/src/prog8/code/ast/AstExpressions.kt:239-240`) already permits + (`simpleAst/src/prog8/code/ast/AstExpressions.kt:235`) already permits `elementType.isStructInstance`, so the AST node is ready. -- **Index × struct-size math exists.** `ExpressionGen.kt:161-185` computes +- **Index × struct-size math exists.** `ExpressionGen.kt:167,183` computes `offset = constIndex * struct.size` and `multiplyByConst(indexReg, - struct.size)` when resolving a struct field; `:1850-1922` handles + struct.size)` when resolving a struct field; `:1858-1866` handles "index * structsize + field" for struct field access. The element-base arithmetic for arrays of structs is essentially the same code. -- **Array iteration uses element size.** `IRCodeGen.kt:752` computes - `elementSize = program.memsizer.memorySize(elementDt)` and `:760` does - `lengthBytes = iterableLength * elementSize`. If `elementDt` is a struct, +- **Array iteration uses element size.** `IRCodeGen.kt:776` computes + `elementSize = program.memsizer.memorySize(arrElementDt, null)` and `:777` does + `lengthBytes = iterableLength * elementSize`. If `arrElementDt` is a struct, this already yields `struct.size`. So this is not a from-scratch feature: the gates are the parser throw plus @@ -44,8 +51,10 @@ stride handling. ## Change overview ### 1. Parser / declaration -- Remove (or relax) the `SyntaxError` at `Antlr2KotlinVisitor.kt:235-236` so a +- Remove (or relax) the `SyntaxError` at `Antlr2KotlinVisitor.kt:234-235` so a struct `DataType` can flow into the array-type builder. +- Also relax the `TODO` at `DataTypes.kt:194-195` so a struct instance can be + used as a data type. - Allow `structtype name[size]` (and `const` / initialized forms). ### 2. DataType model (`codeCore/.../DataTypes.kt`) @@ -58,7 +67,7 @@ stride handling. ### 3. Memory layout & sizing - Variable declaration must size the array as `length * struct.size` and align the base to the struct's alignment. The element-size plumbing already exists - (`IRCodeGen.kt:752,760`); once the element type is legal it computes the + (`IRCodeGen.kt:776-777`); once the element type is legal it computes the right total. - The `IRStStaticVariable` / static-var representation must carry the struct element type so sizing and alignment are correct. @@ -70,7 +79,7 @@ stride handling. - `LOADX` / `STOREX` and pointer arithmetic must use this stride. For structs you typically compute the element base `base + index*struct.size` and then access fields within it, rather than a single wide load. The field-access - machinery in `ExpressionGen.kt:161-185` and `:1850-1922` is the template to + machinery in `ExpressionGen.kt:167,183` and `:1858-1866` is the template to reuse. ### 5. Member access `arr[i].field` @@ -106,7 +115,8 @@ stride handling. ## Bottom line -The single hard blocker is one parser throw. The datatype/sizing path already +The two hard blockers are the parser throw (`Antlr2KotlinVisitor.kt:234-235`) +and the `TODO` in `DataTypes.kt:194-195`. The datatype/sizing path already multiplies by element size in the right places. The real work is: (a) make struct a legal array element type end-to-end, (b) ensure the *stride* is `struct.size` consistently in all index math (not just field access), diff --git a/ideas/m68k-register-allocation.md b/ideas/m68k-register-allocation.md index a3d609bad..c8d023c33 100644 --- a/ideas/m68k-register-allocation.md +++ b/ideas/m68k-register-allocation.md @@ -2,8 +2,9 @@ This document describes the design for a **true register allocator** for the m68k code generation backend (`codeGenM68k`). It supersedes the earlier -`register-packing.md` plan, which only compressed the *memory* register file -(`p8_regfile` BSS block) and never placed values in real CPU registers. +memory-slot packing approach in `codeGenIntermediate/RegisterPacker.kt`, which +only compressed the *memory* register file (`p8_regfile` BSS block) and never +placed values in real CPU registers. The goal here is different and bigger: keep virtual register (vreg) values in the m68k's actual hardware registers (D0–D7, A0–A6, FP0–FP7) for as long as @@ -18,10 +19,10 @@ of targeting a register-rich 32-bit CPU. The m68k backend today (`AsmGen.kt`) spills **every** virtual register to a single flat BSS memory block: -- `regFileLayout` (`AsmGen.kt:80-92`) lays out **all** registers returned by +- `regFileLayout` (`AsmGen.kt:157-169`) lays out **all** registers returned by `program.registersUsed().regsTypes` — program-wide — into one block labelled - `p8_regfile`. -- `regAddr(reg)` returns `p8_regfile+offset`, so every IR instruction becomes a + `p8_regfile` (label constant at `:100`). +- `regAddr(reg)` (`AsmGen.kt:188`) returns `p8_regfile+offset`, so every IR instruction becomes a load from memory into D0/D1, compute, store result back to memory. - `RegisterPool` (`codeGenIntermediate/RegisterPool.kt`) assigns globally unique, monotonically increasing register numbers; `nextRegister` is never @@ -39,13 +40,13 @@ adding a proper allocator. ### 2.1 No stack arguments -Prog8 already has a stack-free calling convention (see `AsmGen.kt:12-17`): +Prog8 already has a stack-free calling convention (see `AsmGen.kt:87-91`): - **Normal subs:** the caller writes arguments into the callee's *parameter variables* (memory/BSS, possibly ZP) before the `jsr`; the callee reads them from there. - **asmsub/extsub:** arguments go into fixed hardware slots via - `CallingConventionSlot` (`InstrControl.kt:640`): slots 10..17 → D0..D7, + `CallingConventionSlot` (`InstrControl.kt:645`): slots 10..17 → D0..D7, 18..24 → A0..A6, 25..32 → FP0..FP7. In practice asmsub args/returns use D0–D2 and FP0–FP1. - **Returns:** come back through virtual registers mapped to the caller's @@ -233,7 +234,7 @@ apply here: only a handful of hardware registers are involved. already implements, per subroutine: CFG construction, intraprocedural liveness (gen/kill fixed-point), live-interval building, interval merging, conflict-graph construction, greedy coloring, and instruction rewriting. Its only production -call site (`IRCodeGen.generate()`) is commented out, so today it runs only under +call site (`IRCodeGen.kt:102-107`) is commented out, so today it runs only under `TestRegisterPacker`; its file header comment documents what it does in its present form. @@ -366,8 +367,8 @@ This document is **m68k-specific**. On the 6502 the situation differs: caller/callee-saved split applied to A/X/Y and ZP scratch) applies, but the payoff is far smaller than on m68k. -A 6502-specific design is left to a separate document; the depth-range packer -approach from `register-packing.md` remains directly relevant there. +A 6502-specific design is left to a separate document; the depth-range packing +approach from `RegisterPacker.kt` remains directly relevant there. --- diff --git a/ideas/ptr-assignment-todo.md b/ideas/ptr-assignment-todo.md index d20f20af9..d2e137b48 100644 --- a/ideas/ptr-assignment-todo.md +++ b/ideas/ptr-assignment-todo.md @@ -10,7 +10,7 @@ code generation with `NotImplementedError`. ### Pointer dereference assignment helpers - Line 47: assign a float from AY to a pointer dereference. -- Lines 760-797: assign float, byte, word, and long values to indexed pointer dereferences. +- Lines 813-850: assign float, byte, word, and long values to indexed pointer dereferences. These helpers are still explicit `TODO()` paths, but representative source cases such as `pointers[idx]^^ = value` currently compile successfully for the @@ -21,7 +21,7 @@ rather than a confirmed compiler crash. ### Pointer-array index scaling -- Lines 450, 467, 539, 557, 656, and 681: multiply an index by the element +- Lines 503, 520, 592, 610, 709, and 734: multiply an index by the element size for pointer-indexed byte, word, long, or struct arrays. Representative variable-index assignments for byte, word, and long pointees @@ -31,9 +31,9 @@ severity is currently unverified rather than a confirmed compiler crash. ### Pointer augmented multiplication and division -- Lines 337 and 346: byte `*=` and `/=` through pointers. -- Lines 340 and 349: long `*=` and `/=` through pointers. -- Line 357: byte `%=` through a pointer. +- Lines 390 and 399: byte `*=` and `/=` through pointers. +- Lines 393 and 402: long `*=` and `/=` through pointers. +- Line 410: byte `%=` through a pointer. Representative direct pointer dereference operations for byte and long values, including `*=`, `/=`, and byte `%=`, compile successfully for C64 and CX16 with @@ -43,9 +43,9 @@ confirmed compiler crash. ### Pointer augmented float operations -- Lines 1952-1954: variable, expression, and register operands for float `+=` +- Lines 2005-2007: variable, expression, and register operands for float `+=` and `*=`. -- Lines 2205-2207: variable, expression, and register operands for float `-=` +- Lines 2258-2260: variable, expression, and register operands for float `-=` and `/=`. Representative direct pointer dereferences using shared variable operands for @@ -58,9 +58,9 @@ confirmed compiler crash. ### Pointer augmented operations with register operands -- Line 2003: word subtraction from a register. -- Lines 2295 and 2344: byte addition and subtraction from a register. -- Lines 2387, 2431, 2474, 2518, 2561, and 2605: byte and word XOR, OR, and +- Line 2056: word subtraction from a register. +- Lines 2348 and 2397: byte addition and subtraction from a register. +- Lines 2440, 2484, 2527, 2571, 2614, and 2658: byte and word XOR, OR, and AND from a register. The corresponding literal, variable, or expression paths are implemented in @@ -68,16 +68,16 @@ many cases. Severity: medium-high, compiler crash for register-source paths. ### Long pointer augmented operations -- Lines 1796 and 1866: unsupported source kinds for long add and subtract. -- Lines 2676, 2747, and 2818: long AND, OR, and XOR from a register. +- Lines 1849 and 1919: unsupported source kinds for long add and subtract. +- Lines 2729, 2800, and 2871: long AND, OR, and XOR from a register. Severity: medium-high, compiler crash for the affected source forms. ### Struct and large-offset dereferences -- Line 1026: read through an address-of dereference when the final offset is +- Line 1079: read through an address-of dereference when the final offset is greater than 255. -- Line 1047: obtain a struct pointer from a pointer dereference before field +- Line 1100: obtain a struct pointer from a pointer dereference before field access. Small offsets and simpler pointer chains work. Severity: high for large @@ -85,28 +85,28 @@ structures or nested pointer/struct access. ### Pointer comparisons -- Lines 889 and 1012: byte pointer comparisons using `<`, `<=`, `>`, and `>=`. +- Lines 942 and 1065: byte pointer comparisons using `<`, `<=`, `>`, and `>=`. Equality and inequality are implemented. Severity: medium, compiler crash for relational comparisons. ### Signed byte pointer shifts -- Line 1490: signed byte right shift through a pointer. +- Line 1543: signed byte right shift through a pointer. Unsigned shifts and other shift widths have implementations. Severity: medium, compiler crash for signed `>>=` cases. ## Lower Impact or Non-Functional TODOs -- Lines 2890 and 2925: save and restore combined long register pairs on the +- Lines 2943 and 2978: save and restore combined long register pairs on the stack. This is likely an internal register-allocation path and is less likely to be reached by ordinary source code, but still crashes if selected. -- Line 419: comment that the zeropage scratch-variable detection is not robust. +- Line 472: comment that the zeropage scratch-variable detection is not robust. This is a code-quality concern, not an unimplemented operation. -- Line 1069: comment about avoiding an unnecessary zeropage scratch register. +- Line 1122: comment about avoiding an unnecessary zeropage scratch register. This is an optimization opportunity, not a correctness issue. -- Lines 359-360: commented-out float and long modulo TODOs. They are inactive +- Lines 412-413: commented-out float and long modulo TODOs. They are inactive and have no current effect. ## Overall Assessment diff --git a/ideas/scaled-indexing-IR.md b/ideas/scaled-indexing-IR.md index c65a52e52..a0b8a9b04 100644 --- a/ideas/scaled-indexing-IR.md +++ b/ideas/scaled-indexing-IR.md @@ -14,14 +14,14 @@ targets and fixing the same problem uniformly for every backend. The IR generator pre-scales the array index: -- `loadIndexReg` (`codeGenIntermediate/IRCodeGen.kt:2100-2128`) loads the +- `loadIndexReg` (`codeGenIntermediate/IRCodeGen.kt:2228-2264`) loads the element index and immediately multiplies it by the element size - (`itemsize`), both on the 16-bit path (`:2115`, `multiplyByConst(UWORD, ...)`) - and on the 8-bit path (`:2126`, `multiplyByConst(UBYTE, ...)`). + (`itemsize`), both on the 16-bit path (`:2243`, `multiplyByConst(UWORD, ...)`) + and on the 8-bit path (`:2263`, `multiplyByConst(UBYTE, ...)`). - Therefore every consumer of `LOADX` / `STOREX` / `STOREZX` assumes the index register already holds a **byte offset** into the array: - The VM adds the raw index to the base address (`InsLOADX`/`InsSTOREX`/ - `InsSTOREZX` in `VirtualMachine.kt:576/646/693`). + `InsSTOREZX` in `VirtualMachine.kt:590/666/723`). - new6502 uses the raw index as a byte offset into the array (`indexedLoad`/`storeExchange`/`zeroMemoryIndexed` in `InstrLoadStore.kt:675/753/845`). @@ -65,10 +65,10 @@ Struct field access never goes through the scaled index register. Field offsets are always constant byte offsets: - Pointer field access: `LOADI` / `STOREI` with `immediate = fieldOffset` - (`ExpressionGen.kt:1886-1897`, `AssignmentGen.kt:295-315`). + (`ExpressionGen.kt:1886-1897`, `AssignmentGen.kt:698`). - Struct-variable field access: `LOADM` / `STOREM` with `symbolOffset`. - Array of structs: index is multiplied by the struct size and added to the - pointer via `ADDR` (genuine pointer arithmetic, `ExpressionGen.kt:1866-1868`). + pointer via `ADDR` (genuine pointer arithmetic, `ExpressionGen.kt:1863-1865`). So the "absolute fixed byte index" of a struct field never appears in a scaled `LOADX`/`STOREX`/`STOREZX` index register. The `scale` operand applies only to @@ -87,7 +87,7 @@ changes freely. The serialized `scale` is always written for the three opcodes ### 3.1 The `scale` field - Add `val scale: Int = 1` to `IRInstruction` - (`intermediate/IRInstructions.kt`, constructor around `:902`). + (`intermediate/IRInstructions.kt`, constructor at `:918`). - Validation: - `scale >= 1`. - `scale != 1` is only allowed on `LOADX`, `STOREX`, `STOREZX`. @@ -102,9 +102,9 @@ storx.b r1,r2,floatarray,S=5 storezx.b r3,arr,S=1 ``` -Parsing: `parseIRCodeLine` (`intermediate/Utils.kt:87`) strips an optional +Parsing: `parseIRCodeLine` (`intermediate/Utils.kt:92`) strips an optional trailing `,S=N`; defaults to 1 when absent. The existing label + offset parsing -(`arr+1`, `Utils.kt:242-253`) is unchanged and composes with it: +(`arr+1`, `Utils.kt:248-257`) is unchanged and composes with it: ``` storx.b r1,r2,arr+1,S=4 @@ -115,8 +115,8 @@ storx.b r1,r2,arr+1,S=4 ### 3.3 Documentation updates -- `instructionFormats` header comment (`IRInstructions.kt`, around `:710`). -- The 32-bit word-index note in `IRProgram.kt:315`. +- `instructionFormats` header comment (`IRInstructions.kt:724`). +- The 32-bit word-index note in `IRProgram.kt:318`. --- @@ -133,7 +133,7 @@ storx.b r1,r2,arr+1,S=4 Constraints that make everything fit: -- 6502: `ARRAY_SIZE_LIMIT = 256` is a **byte budget** (`AstChecker.kt:1080-1099`): +- 6502: `ARRAY_SIZE_LIMIT = 256` is a **byte budget** (`AstChecker.kt:1107-1122`): word arrays <= 128 elements, long <= 64, float <= 51. A scaled byte offset therefore always fits in a byte (max 50*5 + 4 = 254), so 8-bit index arithmetic suffices. @@ -152,28 +152,28 @@ Constraints that make everything fit: 1. Add `scale: Int = 1` to `IRInstruction` with the validation from Section 3.1. 2. Serialize `,S=N` in `toString()` for `LOADX`/`STOREX`/`STOREZX`; parse it in `Utils.parseIRCodeLine`. -3. Update the comments in `IRInstructions.kt` and `IRProgram.kt:315`. +3. Update the comments in `IRInstructions.kt` and `IRProgram.kt:318`. ### 5.2 `codeGenIntermediate` - stop pre-scaling -**`loadIndexReg`** (`IRCodeGen.kt:2100`): +**`loadIndexReg`** (`IRCodeGen.kt:2228`): -- Delete the two `multiplyByConst` calls (`:2115` and `:2126`). +- Delete the two `multiplyByConst` calls (`:2243` and `:2263`). - Drop the now-unused `itemsize` and `arrayIsSplitWords` parameters. - Keep the byte-to-word `EXT` so 32-bit targets get a word-sized index. **Array-indexing sites - emit `scale = eltSize` on the instruction:** - `ExpressionGen.kt:646` (`indexByExpression`). -- `AssignmentGen.kt:746/766/787` (`translateRegularAssignArrayIndexed`). -- Regular-array `for` loop (`IRCodeGen.kt:752-769`): LOADX gets +- `AssignmentGen.kt:802/822/843` (`translateRegularAssignArrayIndexed`). +- Regular-array `for` loop (`IRCodeGen.kt:774-792`): LOADX gets `scale = elementSize`; the loop increment changes from `addConstByteToReg(indexReg, elementSize)` to `+1`; the loop compare changes from `lengthBytes` (= length*elementSize) to `iterableLength`. -- `setlsb`/`setmsb`, variable-index case (`BuiltinFuncGen.kt:931/952`): +- `setlsb`/`setmsb`, variable-index case (`BuiltinFuncGen.kt:934/944`): `scale = eltSize`, `labelSymbolOffset = byteOffset(eltSize)`, and drop the `ADD indexReg, byteOffset` that is no longer needed. The const-index branches - (`:923-940`, `:944-950`) keep baking the full byte offset into the `LOAD` + (`:928-945`, `:950-966`) keep baking the full byte offset into the `LOAD` immediate with default `scale = 1`. **Pointer-arithmetic sites - keep the explicit multiply:** @@ -183,37 +183,38 @@ combined with a pointer via `ADDR` or `LOADI`/`STOREI`). They must multiply by the element size themselves after `loadIndexReg`: - `ExpressionGen.kt:681` (`translatePointerIndexing`). -- `ExpressionGen.kt:1866` (array-of-structs pointer math, struct size). -- `AssignmentGen.kt:703/713/835` (pointer store paths). -- `BuiltinFuncGen.kt:867` (`setlsb`/`setmsb` on a pointer target). +- `ExpressionGen.kt:1863` (array-of-structs pointer math, struct size). +- `AssignmentGen.kt:759/769/891` (pointer store paths). +- `BuiltinFuncGen.kt:873` (`setlsb`/`setmsb` on a pointer target). These paths do **not** use the `scale` operand; they keep genuine pointer arithmetic, unchanged in behavior. ### 5.3 `virtualmachine` -`InsLOADX` (`:576`), `InsSTOREX` (`:646`), `InsSTOREZX` (`:693`): multiply the +`InsLOADX` (`:590`), `InsSTOREX` (`:666`), `InsSTOREZX` (`:723`): multiply the index register by `i.scale` before adding it to the base address. The index register is: - `reg2` for integer `LOADX` / `STOREX`, - `reg1` for float `LOADX` / `STOREX` and for all `STOREZX` - (matches `instructionFormats`, `IRInstructions.kt:714/721/725`). + (matches `instructionFormats`, `IRInstructions.kt:728/735/739`). `labelSymbolOffset` is already folded into the base address by `VmProgramLoader.kt:177-201`, so no change is needed there. ### 5.4 `codeGenM68k` -In `InstrLoadStore.kt` (including the float variants at `:198/252/282`), after -the index has been loaded into `d0` (`loadIndexToD0`, `AsmGen.kt:162`): +In `InstrLoadStore.kt` (including the float variants at `:205/264/295`), after +the index has been loaded into `d0` (inline `move.w` in `InstrLoadStore.kt`, +previously `loadIndexToD0` in `AsmGen.kt`): - **M68020 (qemu68k):** use the hardware scaled-index mode: - `(a0,d0.w*N)` for the integer forms, - `(0,a0,d0.w*N)` for the float forms (the index is a word value, always <= 32767, so `.w*N` is correct). - `labelSymbolOffset` folds into the displacement via `resolveAddress` - (`AsmGen.kt:212`), producing `(B,a0,d0.w*N)`. + (`AsmGen.kt:233`), producing `(B,a0,d0.w*N)`. - **M68000 (amiga500):** there is no scaled-index mode, so shift explicitly: - `lsl.w #1,d0` for scale 2, - `lsl.l #2,d0` for scale 4, @@ -221,7 +222,7 @@ the index has been loaded into `d0` (`loadIndexToD0`, `AsmGen.kt:162`): - Refresh the stale "index pre-scaled" comments. - `scale = 1` paths are unchanged. -Gate on `AsmGen.cpu` (`AsmGen.kt:55`); only the 68020 path uses the scaled +Gate on `AsmGen.cpu` (`AsmGen.kt:97`); only the 68020 path uses the scaled addressing mode. ### 5.5 `codeGenNew6502` @@ -243,7 +244,7 @@ The helper is used by `indexedLoad` (`:675`, byte path), `storeExchange` base address via `resolveAddress`, so setlsb/setmsb needs no extra handling. Float `LOADX`/`STOREX`/`STOREZX` remain `TODO` in this backend -(`:711/861-862`), unrelated to this change. +(`:711/862`), unrelated to this change. --- @@ -289,7 +290,7 @@ Float `LOADX`/`STOREX`/`STOREZX` remain `TODO` in this backend |---------|------| | IR instruction definition, formats, validation, serialization | `intermediate/src/prog8/intermediate/IRInstructions.kt` | | IR text parsing (`,S=N`) | `intermediate/src/prog8/intermediate/Utils.kt` | -| IR comment (32-bit word index note) | `intermediate/src/prog8/intermediate/IRProgram.kt:315` | +| IR comment (32-bit word index note) | `intermediate/src/prog8/intermediate/IRProgram.kt:318` | | IR code generator, `loadIndexReg`, for loop | `codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt` | | Scaled store sites | `codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt` | | Indexed loads, pointer math | `codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt` | @@ -297,6 +298,6 @@ Float `LOADX`/`STOREX`/`STOREZX` remain `TODO` in this backend | VM execution of the three opcodes | `virtualmachine/src/prog8/vm/VirtualMachine.kt` | | VM label + offset resolution | `virtualmachine/src/prog8/vm/VmProgramLoader.kt:177-201` | | m68k load/store emission | `codeGenM68k/src/prog8/codegen/m68k/InstrLoadStore.kt` | -| m68k `cpu`, `loadIndexToD0`, `resolveAddress` | `codeGenM68k/src/prog8/codegen/m68k/AsmGen.kt` | +| m68k `cpu`, `resolveAddress` | `codeGenM68k/src/prog8/codegen/m68k/AsmGen.kt` | | new6502 load/store emission | `codeGenNew6502/src/prog8/codegen/new6502/InstrLoadStore.kt` | -| Array byte budget limits | `compiler/src/prog8/compiler/astprocessing/AstChecker.kt:1080-1099` | +| Array byte budget limits | `compiler/src/prog8/compiler/astprocessing/AstChecker.kt:1107-1122` | diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index a62b25cf9..006009da4 100644 --- a/intermediate/test/TestIRFileInOut.kt +++ b/intermediate/test/TestIRFileInOut.kt @@ -1,12 +1,7 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.shouldBe -import prog8.code.core.BaseDataType -import prog8.code.core.CompilationOptions -import prog8.code.core.DataType -import prog8.code.core.OutputType -import prog8.code.core.Position -import prog8.code.core.ZeropageType +import prog8.code.core.* import prog8.code.target.Amiga500Target import prog8.code.target.Cx16Target import prog8.intermediate.* diff --git a/simpleAst/src/prog8/code/SymbolTableMaker.kt b/simpleAst/src/prog8/code/SymbolTableMaker.kt index 7750a578a..5b9d13398 100644 --- a/simpleAst/src/prog8/code/SymbolTableMaker.kt +++ b/simpleAst/src/prog8/code/SymbolTableMaker.kt @@ -2,9 +2,6 @@ package prog8.code import prog8.code.ast.* import prog8.code.core.* -import prog8.code.target.Amiga500Target -import prog8.code.target.Qemu68kTarget -import prog8.code.target.VMTarget /** diff --git a/simpleAst/test/prog8tests/code/TestPtBuilders.kt b/simpleAst/test/prog8tests/code/TestPtBuilders.kt index 5faec0350..278acd56e 100644 --- a/simpleAst/test/prog8tests/code/TestPtBuilders.kt +++ b/simpleAst/test/prog8tests/code/TestPtBuilders.kt @@ -1,8 +1,8 @@ package prog8tests.code import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.shouldBe import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe import prog8.code.ast.* import prog8.code.core.* diff --git a/virtualmachine/src/prog8/vm/VmVariableAllocator.kt b/virtualmachine/src/prog8/vm/VmVariableAllocator.kt index 9b538f47f..e7b67e29f 100644 --- a/virtualmachine/src/prog8/vm/VmVariableAllocator.kt +++ b/virtualmachine/src/prog8/vm/VmVariableAllocator.kt @@ -1,10 +1,6 @@ package prog8.vm -import prog8.code.core.BaseDataType -import prog8.code.core.DataType -import prog8.code.core.IMemSizer -import prog8.code.core.IStringEncoding -import prog8.code.core.InternalCompilerException +import prog8.code.core.* import prog8.intermediate.IRSymbolTable import prog8.intermediate.IRVariableInitializer