From c1aa5d4e47402cc4a48e8351e42a923d8365bb66 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 16 Jul 2023 12:10:46 +0200 Subject: [PATCH 1/3] IR: optimized when statement translation --- .../prog8/codegen/intermediate/IRCodeGen.kt | 43 ++++++++----------- examples/test.p8 | 26 ++++++++--- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index e865a599b..4d1b987f7 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -399,42 +399,35 @@ class IRCodeGen( } private fun translate(whenStmt: PtWhen): IRCodeChunks { - if(whenStmt.choices.children.isEmpty()) - return emptyList() val result = mutableListOf() val valueDt = irType(whenStmt.value.type) val valueTr = expressionEval.translateExpression(whenStmt.value) addToResult(result, valueTr, valueTr.resultReg, -1) - val choices = whenStmt.choices.children.map {it as PtWhenChoice } + + val choices = mutableListOf>() val endLabel = createLabelName() - for (choice in choices) { + whenStmt.choices.children.forEach { + val choice = it as PtWhenChoice if(choice.isElse) { result += translateNode(choice.statements) + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) } else { - val skipLabel = createLabelName() - val values = choice.values.children.map {it as PtNumber} - if(values.size==1) { - val chunk = IRCodeChunk(null, null) - chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel) - result += chunk - result += translateNode(choice.statements) - if(choice.statements.children.last() !is PtReturn) - addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) - } else { - val matchLabel = createLabelName() - val chunk = IRCodeChunk(null, null) - for (value in values) { - chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel) - } - chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel) - result += chunk - result += labelFirstChunk(translateNode(choice.statements), matchLabel) - if(choice.statements.children.last() !is PtReturn) - addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) + val choiceLabel = createLabelName() + choices.add(choiceLabel to choice) + choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value -> + addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null) } - result += IRCodeChunk(skipLabel, null) } } + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) + + choices.forEach { (label, choice) -> + result += labelFirstChunk(translateNode(choice.statements), label) + val lastStatement = choice.statements.children.last() + if(lastStatement !is PtReturn && lastStatement !is PtJump) + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) + } + result += IRCodeChunk(endLabel, null) return result } diff --git a/examples/test.p8 b/examples/test.p8 index 9d4d528e3..7af43942d 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,13 +5,27 @@ main { sub start() { - ubyte a=1 - ubyte b=2 - ubyte c=3 - ubyte d=4 + ubyte res + ubyte val=3 + when val*10 { + 1 -> res=1 + 10 -> res=10 + 20 -> res=20 + 30 -> res=30 + 40 -> res=40 + else -> res=0 + } - ubyte xx = (a*b)+(c*d) - xx++ + txt.print_ub(res) ; 30 + txt.nl() + when val { + 5,7,9,1,3 -> res=100 + 2,4,6,8 -> res=200 + else -> res=0 + } + + txt.print_ub(res) ; 100 + ; 102 instructions, 31 registers, 122 steps } } From 450eaf7c4a1b7d54bba280ab67639e49eca9459a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 16 Jul 2023 20:05:59 +0200 Subject: [PATCH 2/3] fixed lsb() to uword problem --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 22 ++++++++- codeGenCpu6502/test/TestCodegen.kt | 14 +++++- examples/test.p8 | 46 +++++++++++-------- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 1b997ac93..4084419f3 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -1160,9 +1160,27 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, // and will not generate another cmp when lsb() is directly used inside a comparison expression. } RegisterOrPair.Y -> { - asmgen.out(" pha") asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) - asmgen.out(" tay | pla | cpy #0") + asmgen.out(" tay | cpy #0") + } + RegisterOrPair.AY -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) + asmgen.out(" ldy #0 | cmp #0") + } + RegisterOrPair.AX -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX) + asmgen.out(" ldx #0 | cmp #0") + } + RegisterOrPair.XY -> { + asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY) + asmgen.out(" ldy #0 | cpx #0") + } + in Cx16VirtualRegisters -> { + asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister) + val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY) + zero.parent=fcall + assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE) + asmgen.out(" lda cx16.r0L") } else -> throw AssemblyError("invalid reg") } diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index e61e53aeb..0516e4537 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -1,7 +1,9 @@ package prog8tests.codegencpu6502 import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual import io.kotest.matchers.shouldBe import prog8.code.SymbolTableMaker import prog8.code.ast.* @@ -107,9 +109,19 @@ class TestCodegen: FunSpec({ test("64tass assembler available? - if this fails you need to install 64tass in the path") { val command = mutableListOf("64tass", "--version") shouldNotThrowAny { - val proc = ProcessBuilder(command).inheritIO().start() + val proc = ProcessBuilder(command).start() + val output = String(proc.inputStream.readBytes()) val result = proc.waitFor() result.shouldBe(0) + val (_, version) = output.split('V') + val (major, minor, _) = version.split('.') + val majorNum = major.toInt() + val minorNum = minor.toInt() + withClue("64tass version should be 1.58 or newer") { + majorNum shouldBeGreaterThanOrEqual 1 + if (majorNum == 1) + minorNum shouldBeGreaterThanOrEqual 58 + } } } }) diff --git a/examples/test.p8 b/examples/test.p8 index 7af43942d..22c4deb4b 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,27 +5,37 @@ main { sub start() { - ubyte res - ubyte val=3 - when val*10 { - 1 -> res=1 - 10 -> res=10 - 20 -> res=20 - 30 -> res=30 - 40 -> res=40 - else -> res=0 - } + uword zc = 54321 + ubyte zb = 123 + ubyte shift = 2 - txt.print_ub(res) ; 30 +; txt.print_uw(zc<>shift) +; txt.nl() +; txt.print_ub(zb<>shift) +; txt.nl() +; +; word szc = -12345 +; byte szb = -123 +; txt.print_w(szc<>shift) +; txt.nl() +; txt.print_b(szb<>shift) +; txt.nl() +; + txt.print_uw(~zc as ubyte) + txt.spc() + txt.print_uw(~zb as uword) txt.nl() - when val { - 5,7,9,1,3 -> res=100 - 2,4,6,8 -> res=200 - else -> res=0 - } - txt.print_ub(res) ; 100 - ; 102 instructions, 31 registers, 122 steps +; cx16.r1L = (zc< Date: Sun, 16 Jul 2023 21:52:28 +0200 Subject: [PATCH 3/3] fixed assigning byte to word not clearing msb sometimes --- .../cpu6502/assignment/AssignmentAsmGen.kt | 145 ++++++++++++------ .../src/prog8/buildversion/BuildVersion.kt | 12 +- .../test/codegeneration/TestVariousCodeGen.kt | 33 ++++ examples/test.p8 | 45 ++---- 4 files changed, 156 insertions(+), 79 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index d65aa3709..ee9c16ac9 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -45,12 +45,12 @@ internal class AssignmentAsmGen(private val program: PtProgram, val variable = assign.source.asmVarname when (assign.target.datatype) { DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable) - DataType.WORD -> assignVariableWord(assign.target, variable) + DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype) DataType.UWORD -> { if(assign.source.datatype in PassByReferenceDatatypes) assignAddressOf(assign.target, variable) else - assignVariableWord(assign.target, variable) + assignVariableWord(assign.target, variable, assign.source.datatype) } DataType.FLOAT -> assignVariableFloat(assign.target, variable) DataType.STR -> assignVariableString(assign.target, variable) @@ -288,20 +288,45 @@ internal class AssignmentAsmGen(private val program: PtProgram, } is PtPrefix -> { if(assign.target.array==null) { - // First assign the value to the target then apply the operator in place on the target. - // This saves a temporary variable - translateNormalAssignment( - AsmAssignment( - AsmAssignSource.fromAstSource(value.value, program, asmgen), - assign.target, program.memsizer, assign.position - ), scope - ) - when (value.operator) { - "+" -> {} - "-" -> inplaceNegate(assign, true, scope) - "~" -> inplaceInvert(assign, scope) - "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") - else -> throw AssemblyError("invalid prefix operator") + if(assign.source.datatype==assign.target.datatype) { + // First assign the value to the target then apply the operator in place on the target. + // This saves a temporary variable + translateNormalAssignment( + AsmAssignment( + AsmAssignSource.fromAstSource(value.value, program, asmgen), + assign.target, program.memsizer, assign.position + ), scope + ) + when (value.operator) { + "+" -> {} + "-" -> inplaceNegate(assign, true, scope) + "~" -> inplaceInvert(assign, scope) + "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") + else -> throw AssemblyError("invalid prefix operator") + } + } else { + // use a temporary variable + val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1" + assignExpressionToVariable(value.value, tempvar, value.type) + when (value.operator) { + "+" -> {} + "-", "~" -> { + val assignTempvar = AsmAssignment( + AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar), + AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar), + program.memsizer, assign.position) + if(value.operator=="-") + inplaceNegate(assignTempvar, true, scope) + else + inplaceInvert(assignTempvar, scope) + } + "not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") + else -> throw AssemblyError("invalid prefix operator") + } + if(value.type in ByteDatatypes) + assignVariableByte(assign.target, tempvar) + else + assignVariableWord(assign.target, tempvar, value.type) } } else { assignPrefixedExpressionToArrayElt(assign, scope) @@ -338,7 +363,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, asmgen.translateNormalAssignment(assignToTempvar, scope) when(assign.target.datatype) { in ByteDatatypes -> assignVariableByte(assign.target, tempvar) - in WordDatatypes -> assignVariableWord(assign.target, tempvar) + in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype) DataType.FLOAT -> assignVariableFloat(assign.target, tempvar) else -> throw AssemblyError("weird dt") } @@ -827,7 +852,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, DataType.UWORD -> { asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.out(" jsr math.divmod_uw_asm") - assignVariableWord(assign.target, "P8ZP_SCRATCH_W2") + assignVariableWord(assign.target, "P8ZP_SCRATCH_W2", DataType.UWORD) return true } else -> return false @@ -2049,19 +2074,29 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) { + private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) { + require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE) when(target.kind) { TargetStorageKind.VARIABLE -> { - asmgen.out(""" - lda $sourceName - ldy $sourceName+1 - sta ${target.asmVarname} - sty ${target.asmVarname}+1""") + if(sourceDt==DataType.UBYTE) { + asmgen.out(" lda $sourceName | sta ${target.asmVarname}") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz ${target.asmVarname}") + else + asmgen.out(" lda #0 | sta ${target.asmVarname}") + } + else + asmgen.out(""" + lda $sourceName + ldy $sourceName+1 + sta ${target.asmVarname} + sty ${target.asmVarname}+1""") } TargetStorageKind.MEMORY -> { throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast") } TargetStorageKind.ARRAY -> { + if(sourceDt==DataType.UBYTE) TODO("assign byte to word array") target.array!! if(target.constArrayIndexValue!=null) { val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt() @@ -2137,29 +2172,53 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } TargetStorageKind.REGISTER -> { - when(target.register!!) { - RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName") - RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName") - RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName") - in Cx16VirtualRegisters -> { - asmgen.out( - """ - lda $sourceName - sta cx16.${target.register.toString().lowercase()} - lda $sourceName+1 - sta cx16.${target.register.toString().lowercase()}+1 - """) + if(sourceDt==DataType.UBYTE) { + when(target.register!!) { + RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName") + RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName") + RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName") + in Cx16VirtualRegisters -> { + asmgen.out(" lda $sourceName | sta cx16.${target.register.toString().lowercase()}") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1") + else + asmgen.out(" lda #0 | sta cx16.${target.register.toString().lowercase()}+1") + } + else -> throw AssemblyError("can't load word in a single 8-bit register") + } + } else { + when(target.register!!) { + RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName") + RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName") + RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName") + in Cx16VirtualRegisters -> { + asmgen.out( + """ + lda $sourceName + sta cx16.${target.register.toString().lowercase()} + lda $sourceName+1 + sta cx16.${target.register.toString().lowercase()}+1 + """) + } + else -> throw AssemblyError("can't load word in a single 8-bit register") } - else -> throw AssemblyError("can't load word in a single 8-bit register") } } TargetStorageKind.STACK -> { - asmgen.out(""" - lda $sourceName - sta P8ESTACK_LO,x - lda $sourceName+1 - sta P8ESTACK_HI,x - dex""") + if(sourceDt==DataType.UBYTE) { + asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz P8ESTACK_HI,x") + else + asmgen.out(" lda #0 | sta P8ESTACK_HI,x") + } + else + asmgen.out(""" + lda $sourceName + sta P8ESTACK_LO,x + lda $sourceName+1 + sta P8ESTACK_HI,x + dex""") } } } diff --git a/compiler/src/prog8/buildversion/BuildVersion.kt b/compiler/src/prog8/buildversion/BuildVersion.kt index b50ae16cd..931d56c39 100644 --- a/compiler/src/prog8/buildversion/BuildVersion.kt +++ b/compiler/src/prog8/buildversion/BuildVersion.kt @@ -5,11 +5,11 @@ package prog8.buildversion */ const val MAVEN_GROUP = "prog8" const val MAVEN_NAME = "compiler" -const val VERSION = "9.1-SNAPSHOT" -const val GIT_REVISION = 3930 -const val GIT_SHA = "b4e94ae4dd1c29a966bb5ea0454ab31f1e9863cb" -const val GIT_DATE = "2023-07-05T21:15:04Z" +const val VERSION = "9.2-SNAPSHOT" +const val GIT_REVISION = 3964 +const val GIT_SHA = "aaa30e4a583fa7c8da61998cf7cbb2a60b52afb0" +const val GIT_DATE = "2023-07-16T21:16:18Z" const val GIT_BRANCH = "master" -const val BUILD_DATE = "2023-07-05T21:15:07Z" -const val BUILD_UNIX_TIME = 1688591707834L +const val BUILD_DATE = "2023-07-16T21:16:23Z" +const val BUILD_UNIX_TIME = 1689542183583L const val DIRTY = 1 diff --git a/compiler/test/codegeneration/TestVariousCodeGen.kt b/compiler/test/codegeneration/TestVariousCodeGen.kt index aeb5c4465..54de41379 100644 --- a/compiler/test/codegeneration/TestVariousCodeGen.kt +++ b/compiler/test/codegeneration/TestVariousCodeGen.kt @@ -285,4 +285,37 @@ derp { compileText(VMTarget(), true, src, writeAssembly = true) shouldNotBe null compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null } + + test("prefix expressions with typecasting") { + val src=""" +main +{ + sub start() + { + uword uw = 54321 + ubyte ub = 123 + word sw = -12345 + byte sb = -123 + + func_uw(~ub as uword) + func_ub(~uw as ubyte) + func_uw(~sb as uword) + func_ub(~sw as ubyte) + func_w(-sb as word) + func_b(-sw as byte) + } + + sub func_uw(uword arg) { + } + sub func_w(word arg) { + } + sub func_ub(ubyte arg) { + } + sub func_b(byte arg) { + } +}""" + + compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null + compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null + } }) \ No newline at end of file diff --git a/examples/test.p8 b/examples/test.p8 index 22c4deb4b..89dba048d 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,37 +5,22 @@ main { sub start() { - uword zc = 54321 - ubyte zb = 123 - ubyte shift = 2 + uword uw = 54321 + ubyte ub = 123 + word sw = -12345 + byte sb = -123 -; txt.print_uw(zc<>shift) -; txt.nl() -; txt.print_ub(zb<>shift) -; txt.nl() -; -; word szc = -12345 -; byte szb = -123 -; txt.print_w(szc<>shift) -; txt.nl() -; txt.print_b(szb<>shift) -; txt.nl() -; - txt.print_uw(~zc as ubyte) - txt.spc() - txt.print_uw(~zb as uword) + txt.print_uw(~ub as uword) ;132 + txt.nl() + txt.print_ub(~uw as ubyte) ;206 + txt.nl() + txt.print_uw(~sb as uword) ;122 + txt.nl() + txt.print_ub(~sw as ubyte) ;56 + txt.nl() + txt.print_w(-sb as word) ;123 + txt.nl() + txt.print_b(-sw as byte) ;57 txt.nl() - - -; cx16.r1L = (zc<