diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index 2a066c847..0429e875e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -12,11 +12,35 @@ import prog8.intermediate.* internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) { private fun normalizeArrayIndex(result: MutableList, indexTr: ExpressionCodeResult): Pair { - if(codeGen.options.compTarget.POINTER_MEM_SIZE <= 2u || indexTr.dt != IRDataType.BYTE) - return indexTr.resultReg to DataType.UBYTE - val indexReg = codeGen.registers.next(IRDataType.WORD) - addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=indexTr.resultReg), null) - return indexReg to DataType.UWORD + val indexRegType = codeGen.options.compTarget.indexRegType + if(indexTr.dt == indexRegType) + return indexTr.resultReg to if(indexRegType==IRDataType.WORD) DataType.UWORD else DataType.UBYTE + // handle POINTER as its underlying size for the purpose of index canonicalization + val effectiveDt = if(indexTr.dt==IRDataType.POINTER) codeGen.options.compTarget.pointerIRType else indexTr.dt + if(effectiveDt == indexRegType) + return indexTr.resultReg to if(indexRegType==IRDataType.WORD) DataType.UWORD else DataType.UBYTE + val newReg = codeGen.registers.next(indexRegType) + when { + indexRegType==IRDataType.WORD && indexTr.dt==IRDataType.BYTE -> + addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.WORD && indexTr.dt==IRDataType.LONG -> + addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.WORD && indexTr.dt==IRDataType.POINTER -> + addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.BYTE && indexTr.dt==IRDataType.WORD -> + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.BYTE && indexTr.dt==IRDataType.LONG -> + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.BYTE && indexTr.dt==IRDataType.POINTER -> { + val ptrType = codeGen.options.compTarget.pointerIRType + if(ptrType==IRDataType.WORD) + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=newReg, reg2=indexTr.resultReg), null) + else + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + } + else -> throw IllegalArgumentException("unexpected index conversion ${indexTr.dt} -> $indexRegType") + } + return newReg to if(indexRegType==IRDataType.WORD) DataType.UWORD else DataType.UBYTE } internal fun translate(assignment: PtAssignment): IRCodeChunks { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 15238230b..f1e8b587a 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -890,32 +890,36 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe val varName = target.variable!!.name + if(msb) "_msb" else "_lsb" if(isConstZeroValue) { if(constIndex!=null) { - val offsetReg = codeGen.registers.next(IRDataType.BYTE) + val idxType = codeGen.options.compTarget.indexRegType + val offsetReg = codeGen.registers.next(idxType) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) + it += IRInstruction(Opcode.LOAD, idxType, reg1=offsetReg, immediate = constIndex) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName) } } else { val indexTr = exprGen.translateExpression(target.index) addToResult(result, indexTr, indexTr.resultReg, -1) + val indexReg = codeGen.canonicalizeIndexReg(result, indexTr) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = varName) + it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexReg, labelSymbol = varName) } } } else { val valueTr = exprGen.translateExpression(call.args[1]) addToResult(result, valueTr, valueTr.resultReg, -1) if(constIndex!=null) { - val offsetReg = codeGen.registers.next(IRDataType.BYTE) + val idxType = codeGen.options.compTarget.indexRegType + val offsetReg = codeGen.registers.next(idxType) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) + it += IRInstruction(Opcode.LOAD, idxType, reg1=offsetReg, immediate = constIndex) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName) } } else { val indexTr = exprGen.translateExpression(target.index) addToResult(result, indexTr, indexTr.resultReg, -1) + val indexReg = codeGen.canonicalizeIndexReg(result, indexTr) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = varName) + it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexReg, labelSymbol = varName) } } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index a9a1e4088..0adf16612 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -621,10 +621,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } else { val tr = translateExpression(arrayIx.index) addToResult(result, tr, tr.resultReg, -1) + val indexReg = codeGen.canonicalizeIndexReg(result, tr) result += IRCodeChunk(null, null).also { val tmpRegMsb = codeGen.registers.next(IRDataType.BYTE) - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_msb") - it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, labelSymbol= "${arrayVarSymbol}_lsb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = indexReg, labelSymbol= "${arrayVarSymbol}_msb") + it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = indexReg, labelSymbol= "${arrayVarSymbol}_lsb") it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister) } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index f76dc269c..570a3f862 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -31,7 +31,7 @@ class IRCodeGen( private set // on 32-bit targets, LOADX/STOREX/STOREZX use a word index register (0-32767) instead of a byte (0-255) - internal val wordArrayIndex: Boolean = options.compTarget.POINTER_MEM_SIZE > 2u + internal val wordArrayIndex: Boolean = options.compTarget.indexRegType == IRDataType.WORD /** * The IR data type to use for a memory address on the current target. @@ -2259,7 +2259,7 @@ class IRCodeGen( addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1) // LOADX/STOREX use word-sized indices on targets with 32-bit pointers. - val indexRegType = if(options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = options.compTarget.indexRegType var indexReg = byteIndexTr.resultReg var indexDt = byteIndexTr.dt if(indexDt != indexRegType) { @@ -2285,6 +2285,37 @@ class IRCodeGen( return Pair(result, indexReg) } + internal fun canonicalizeIndexReg(result: MutableList, indexTr: ExpressionCodeResult): Int { + val indexRegType = options.compTarget.indexRegType + if(indexTr.dt == indexRegType) + return indexTr.resultReg + val effectiveDt = if(indexTr.dt==IRDataType.POINTER) options.compTarget.pointerIRType else indexTr.dt + if(effectiveDt == indexRegType) + return indexTr.resultReg + val newReg = registers.next(indexRegType) + when { + indexRegType==IRDataType.WORD && indexTr.dt==IRDataType.BYTE -> + addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.WORD && indexTr.dt==IRDataType.LONG -> + addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.WORD && indexTr.dt==IRDataType.POINTER -> + addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.BYTE && indexTr.dt==IRDataType.WORD -> + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.BYTE && indexTr.dt==IRDataType.LONG -> + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + indexRegType==IRDataType.BYTE && indexTr.dt==IRDataType.POINTER -> { + val ptrType = options.compTarget.pointerIRType + if(ptrType==IRDataType.WORD) + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=newReg, reg2=indexTr.resultReg), null) + else + addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=newReg, reg2=indexTr.resultReg), null) + } + else -> throw IllegalArgumentException("unexpected index conversion ${indexTr.dt} -> $indexRegType") + } + return newReg + } + internal fun irType(type: DataType): IRDataType { if(type.base.isPassByRef) return IRDataType.POINTER diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 26d51b130..444e76a83 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -1005,7 +1005,7 @@ jump p8_label_gen_2 // lsigb.? rX,rA + ext.b rY,rX -> loadr.? rY,rA ; and.? rY,#$ff // Only applied when the intermediate register is not read anywhere else in this chunk. if(chunk.instructions.size<2) return false - val readCounts = chunk.usedRegisters().readRegs.withDefault { 0 } + val readCounts = chunk.usedRegisters(irprog.options.compTarget.indexRegType).readRegs.withDefault { 0 } class Mod(val idx: Int, val replacement: IRInstruction?) // null replacement = delete val mods = mutableListOf() diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt index 57dad60d1..9fdd4bfe4 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt @@ -1,6 +1,7 @@ package prog8.codegen.intermediate import prog8.code.core.IErrorReporter +import prog8.intermediate.indexRegType import prog8.intermediate.* @@ -80,7 +81,7 @@ class IRUnusedCodeRemover( } // remove stray loads - val indexRegType = if(irprog.options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = irprog.options.compTarget.indexRegType val readRegs = mutableSetOf() val readFpRegs = mutableSetOf() instructions.forEach { ins -> @@ -89,8 +90,7 @@ class IRUnusedCodeRemover( val writeRegsCounts = mutableMapOf() val writeFpRegsCounts = mutableMapOf() val regsTypes = mutableMapOf() - val indexGuessRegs = mutableSetOf() - ins.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes, indexGuessRegs, null, indexRegType) + ins.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes, null, indexRegType) readRegs.addAll(readRegsCounts.keys.map { it.value }) readFpRegs.addAll(readFpRegsCounts.keys.map { it.value }) } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/RegisterPacker.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/RegisterPacker.kt index a15289003..d30de0563 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/RegisterPacker.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/RegisterPacker.kt @@ -1,5 +1,6 @@ package prog8.codegen.intermediate +import prog8.intermediate.indexRegType import prog8.intermediate.* /* @@ -71,7 +72,7 @@ object RegisterPacker { data class Interval(val register: Int, val start: Int, val end: Int, val type: IRDataType) fun pack(irProg: IRProgram) { - val indexRegType = if(irProg.options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = irProg.options.compTarget.indexRegType val allRegTypes = mutableMapOf() irProg.foreachCodeChunk { chunk -> for (instr in chunk.instructions) { @@ -101,7 +102,7 @@ object RegisterPacker { // This avoids the strict per-instruction validation in usedRegisters() that POINTER↔LONG↔WORD // cross-type packing can trigger. fun rebuildTypeMap(irProg: IRProgram): Map { - val indexRegType = if(irProg.options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = irProg.options.compTarget.indexRegType val newTypes = mutableMapOf() irProg.foreachCodeChunk { chunk -> for (instr in chunk.instructions) { diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt index 0e4dbd2e8..32b0f286f 100644 --- a/compiler/test/vm/TestCompilerVirtual.kt +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -1284,6 +1284,76 @@ main { result shouldNotBe null } + test("indexed array with byte index on 32-bit target canonicalizes to word") { + // Regression for index-register type metadata: LOADX/STOREX/STOREZX index width is WORD + // on 32-bit targets. A BYTE index must be widened to WORD before the indexed op, + // otherwise metadata recomputation would report "register given multiple types". + val src = """ + %zeropage basicsafe + %option no_sysinit + main { + word @shared dummy + sub start() { + word[] arr = [1000,2000,3000,4000,5000] + ubyte idx = 2 + word v = arr[idx] + arr[idx] = 9999 + arr[idx] = 0 + v = arr[idx] + dummy = v + } + } + """.trimIndent() + val result = compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly = true) + result shouldNotBe null + val ir = result!!.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir").readText() + // the index should have been canonicalized to WORD (EXT) on this 32-bit target + ir shouldContain "ext" + VmRunner().runAndTestProgram(ir) { vm -> + // just verify it ran without crashing + } + } + + test("byte array with byte index on 32-bit target") { + val src = """ + %zeropage basicsafe + %option no_sysinit + main { + ubyte @shared dummy + sub start() { + ubyte[] arr = [10,20,30,40,50] + ubyte idx = 1 + ubyte v = arr[idx] + arr[idx] = 99 + arr[idx] = 0 + dummy = v + } + } + """.trimIndent() + val result = compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly = true) + result shouldNotBe null + } + + test("word index on 8-bit target canonicalizes to byte for indexed access") { + // On 8-bit targets indexRegType is BYTE. A WORD index must be narrowed via LSIGB. + val src = """ + %zeropage basicsafe + %option no_sysinit + main { + sub start() { + ubyte[] arr = [10,20,30,40,50] + uword idx = 3 + ubyte v = arr[idx] + arr[idx] = 99 + arr[idx] = 0 + cx16.r0 = v + } + } + """.trimIndent() + val result = compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly = true) + result shouldNotBe null + } + test("private_symbols with public override compiles and runs correctly") { val src = """ %zeropage basicsafe diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a52324b27..01bf87922 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,7 @@ TODO Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ +- Consider extending ``var xx = `` type inference (recently added for for-loop counter variables) to all variable declarations. Investigate pros/cons: desired syntax (``var x = expr`` vs ``var x := expr``), interaction with existing ``ubyte``/``uword``/``word`` inference defaults, const vs var, scope and shadowing, error messages for ambiguous types, documentation, impact on block/sub scope, and whether ``var`` without initializer should be allowed. - DataType.ARRAY_POINTER depends on the compilation target to be either a split word array or not. This is horrible because now we have to check with the compilation target everywhere to see if a DataType enumeration value is split word array, and PtVariable and PtArrayIndexer need an explicit boolean to tell us if this is the case. See ideas/remove_array_pointer_plan.md for the plan. - m68k codegen: make use of scaling factors in the indexed instructions on 68020+ ? see ideas/scaled-indexing-IR.md - support arrays-of-structs, see ideas/arrays-of-structs.md @@ -43,9 +44,6 @@ Romable (%option romable) IR/VM ^^^^^ - encode indexed scaling into IR (so that m68k codegen can use scale factor addressing) see ideas/scaled-indexing-IR.md -- **Index register width guessing in register type metadata**: ``LOADX``/``STOREX``/``STOREZX`` do not encode the width of their index register, so ``determineReg1Type``/``determineReg2Type`` (``IRInstructions.kt``) guess it from a caller-supplied ``indexRegType`` (byte on 16-bit-pointer targets, word on 32-bit-pointer ones). But the IR generator actually reuses registers of any width as index (e.g. a uword loop counter that doubles as a ``loadx.b`` index), so the guess can contradict the register's one-and-only allocated type from ``RegisterPool``. This currently throws "register given multiple types" when chunk metadata is recomputed (see ``setRegType``). Workaround in place: guessed typings are tracked in ``indexGuessRegs`` and never conflict with definitive ones. Cleaner alternatives (pick one): - - make ``IRCodeGen`` always widen the index to the canonical ``indexRegType`` right before emitting ``loadx``/``storex``/``storezx``, so the guess is always true; or - - stop recording guessed types altogether and thread the authoritative ``RegisterPool`` types into ``usedRegisters()`` so the metadata layer only reports facts. Note: the m68k regfile layout computation requires every used register to have a recorded type, so simply returning ``null`` for these cases breaks slot sizing. - maybe change all branch instructions to have 2 exits (label if branch condition true, and label if false) instead of 1, and get rid of the implicit "next code chunk" link between chunks. - implement more TODOs in AssignmentGen? - add even more optimizations in IRPeepholeOptimizer? diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 67de0ae81..338aed117 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -169,7 +169,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } private fun writeCodeChunk(chunk: IRCodeChunk) { - val indexRegType = if(irProgram.options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = irProgram.options.compTarget.indexRegType val usedRegs = chunk.usedRegisters(indexRegType) val regs = StringBuilder() if(usedRegs.readRegs.any() || usedRegs.writeRegs.any()) { diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 78f30d0b9..8d1432bf1 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -1066,7 +1066,6 @@ data class IRInstruction( readFpRegsCounts: MutableMap, writeFpRegsCounts: MutableMap, regsTypes: MutableMap, - indexGuessRegs: MutableSet, chunk: IRCodeChunk?, indexRegType: IRDataType = IRDataType.BYTE ) { @@ -1076,65 +1075,49 @@ data class IRInstruction( fun incWriteFp(reg: RegisterNum) = writeFpRegsCounts.merge(reg, 1, Int::plus) // For LOADX/STOREX/STOREZX the width of the index register is not encoded in the - // instruction itself; it is guessed from the target's pointer size (indexRegType). - // That guess can legitimately differ from the register's one-and-only allocated type - // (for instance a word loop counter that also serves as a loadx index). Index-register - // type recordings are therefore treated as guesses, tracked across the whole scan in - // indexGuessRegs: they never overwrite nor contradict a definitively-known type, and a - // definitive type always replaces an earlier guess. - fun setRegType(reg: RegisterNum, type: IRDataType, isIndexGuess: Boolean = false) { + // instruction itself; it is inferred from the target's pointer size (indexRegType). + // IR generation now guarantees the index register has the canonical width + // (see IMemSizer.indexRegType and IRCodeGen.canonicalizeIndexReg), so the + // inference is correct by construction and no guessing suppression is needed. + fun setRegType(reg: RegisterNum, type: IRDataType) { val existingType = regsTypes[reg] if (existingType == null) { regsTypes[reg] = type - if(isIndexGuess) indexGuessRegs.add(reg) } else if (existingType != type) { // POINTER is compatible with WORD or LONG (size depends on target) val compatible = (existingType==IRDataType.POINTER && type in setOf(IRDataType.WORD, IRDataType.LONG)) || (type==IRDataType.POINTER && existingType in setOf(IRDataType.WORD, IRDataType.LONG)) - val existingIsGuess = reg in indexGuessRegs - when { - compatible -> {} // keep first recording, as before - isIndexGuess -> {} // never contradict a definitive type - existingIsGuess -> { // definitive replaces earlier guess - regsTypes[reg] = type - indexGuessRegs.remove(reg) - } - else -> throw IllegalArgumentException("register $reg given multiple types! $existingType and $type while processing $this in $chunk") - } + if(!compatible) + throw IllegalArgumentException("register $reg given multiple types! $existingType and $type while processing $this in $chunk") } } - fun reg1IsIndexGuess() = - (this.type == IRDataType.FLOAT && opcode in setOf(Opcode.LOADX, Opcode.STOREX, Opcode.STOREZX)) || - (opcode == Opcode.STOREZX) - when (this.reg1direction) { OperandDirection.UNUSED -> {} OperandDirection.READ -> { incReadReg(RegisterNum(this.reg1!!)) - determineReg1Type(indexRegType)?.let { setRegType(RegisterNum(this.reg1), it, reg1IsIndexGuess()) } + determineReg1Type(indexRegType)?.let { setRegType(RegisterNum(this.reg1), it) } } OperandDirection.WRITE -> { incWriteReg(RegisterNum(this.reg1!!)) - determineReg1Type(indexRegType)?.let { setRegType(RegisterNum(this.reg1), it, reg1IsIndexGuess()) } + determineReg1Type(indexRegType)?.let { setRegType(RegisterNum(this.reg1), it) } } OperandDirection.READWRITE -> { incReadReg(RegisterNum(this.reg1!!)) incWriteReg(RegisterNum(this.reg1)) - determineReg1Type(indexRegType)?.let { setRegType(RegisterNum(this.reg1), it, reg1IsIndexGuess()) } + determineReg1Type(indexRegType)?.let { setRegType(RegisterNum(this.reg1), it) } } } - val reg2IsIndexGuess = opcode == Opcode.LOADX || opcode == Opcode.STOREX when (this.reg2direction) { OperandDirection.UNUSED -> {} OperandDirection.READ -> { incReadReg(RegisterNum(this.reg2!!)) - determineReg2Type(indexRegType)?.let { setRegType(RegisterNum(this.reg2), it, reg2IsIndexGuess) } + determineReg2Type(indexRegType)?.let { setRegType(RegisterNum(this.reg2), it) } } OperandDirection.READWRITE -> { incReadReg(RegisterNum(this.reg2!!)) incWriteReg(RegisterNum(this.reg2)) - determineReg2Type(indexRegType)?.let { setRegType(RegisterNum(this.reg2), it, reg2IsIndexGuess) } + determineReg2Type(indexRegType)?.let { setRegType(RegisterNum(this.reg2), it) } } else -> throw IllegalArgumentException("reg2 can only be read or readwrite") } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 29cbde88c..5680c8f8c 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -5,6 +5,9 @@ import prog8.code.core.* val IMemSizer.pointerIRType: IRDataType get() = if(POINTER_MEM_SIZE > 2u) IRDataType.LONG else IRDataType.WORD +val IMemSizer.indexRegType: IRDataType + get() = if(POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + /* Intermediate Represenation of the compiled program. @@ -316,9 +319,8 @@ class IRProgram(val name: String, } // on 32-bit targets LOADX/STOREX/STOREZX use a word index register (0-32767) instead of a byte (0-255) - val indexRegType = if(options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = options.compTarget.indexRegType - val globalInitIndexGuessRegs = mutableSetOf() globalInits.instructions.forEach { it.addUsedRegistersCounts( readRegsCounts, @@ -326,7 +328,6 @@ class IRProgram(val name: String, readFpRegsCounts, writeFpRegsCounts, regsTypes, - globalInitIndexGuessRegs, globalInits, indexRegType ) @@ -481,7 +482,7 @@ class IRProgram(val name: String, * which this strict check would reject. Use RegisterPacker.rebuildTypeMap() instead. */ fun verifyRegisterTypes(registerTypes: Map) { - val indexRegType = if(options.compTarget.POINTER_MEM_SIZE > 2u) IRDataType.WORD else IRDataType.BYTE + val indexRegType = options.compTarget.indexRegType for(block in blocks) { for(bc in block.children) { when(bc) { @@ -609,8 +610,7 @@ class IRCodeChunk(label: String?, next: IRCodeChunkBase?): IRCodeChunkBase(label val readFpRegsCounts = mutableMapOf().withDefault { 0 } val writeFpRegsCounts = mutableMapOf().withDefault { 0 } val writeRegsCounts = mutableMapOf().withDefault { 0 } - val indexGuessRegs = mutableSetOf() - instructions.forEach { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes, indexGuessRegs, this, indexRegType) } + instructions.forEach { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes, this, indexRegType) } return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes) } @@ -716,7 +716,6 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU val writeFpRegsCounts = mutableMapOf().withDefault { 0 } if(isIR) { - val indexGuessRegs = mutableSetOf() assembly.lineSequence().forEach { line -> val t = line.trim() if(t.isNotEmpty()) { @@ -728,7 +727,6 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU readFpRegsCounts, writeFpRegsCounts, regsTypes, - indexGuessRegs, null ) is ParsedIRLine.Label -> { /* labels can be skipped */ }