new ifelse codegen

This commit is contained in:
Irmen de Jong 2024-02-15 23:41:35 +01:00
parent 7d8cdcbfea
commit 577333f2c4
7 changed files with 408 additions and 175 deletions

View File

@ -235,7 +235,7 @@ class AsmGen6502Internal (
private val anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val ifElseAsmgen = IfElseAsmGen(program, this)
private val ifElseAsmgen = IfElseAsmGen(program, this, assignmentAsmGen)
fun compileToAssembly(): IAssemblyProgram? {

View File

@ -2,20 +2,74 @@ package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
internal class IfElseAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal class IfElseAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val assignmentAsmGen: AssignmentAsmGen) {
fun translate(stmt: PtIfElse) {
require(stmt.condition.type== DataType.BOOL)
if(stmt.ifScope.children.singleOrNull() is PtJump) {
translateIfWithOnlyJump(stmt)
return
val jumpAfterIf = stmt.ifScope.children.singleOrNull() as? PtJump
if(stmt.condition is PtIdentifier ||
stmt.condition is PtFunctionCall ||
stmt.condition is PtBuiltinFunctionCall ||
stmt.condition is PtContainmentCheck)
return fallbackTranslate(stmt, false) // the fallback code for these is optimal, so no warning.
val compareCond = stmt.condition as? PtBinaryExpression
if(compareCond!=null) {
return when(compareCond.right.type) {
in ByteDatatypesWithBoolean -> translateIfByte(stmt, compareCond, jumpAfterIf)
in WordDatatypes -> translateIfWord(stmt, compareCond, jumpAfterIf)
DataType.FLOAT -> translateIfFloat(stmt, compareCond, jumpAfterIf)
else -> throw AssemblyError("weird dt")
}
}
val afterIfLabel = asmgen.makeLabel("afterif")
val prefixCond = stmt.condition as? PtPrefix
if(prefixCond?.operator=="not") {
assignConditionValueToRegisterAndTest(prefixCond.value)
return if(jumpAfterIf!=null)
translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bne", stmt)
}
fun translateIfElseBodies(elseBranchInstr: String) {
// TODO more optimized ifs
println("(if with special condition... ${stmt.condition})")
fallbackTranslate(stmt)
}
private fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
asmgen.assignExpressionToRegister(condition, RegisterOrPair.A, false)
when(condition) {
is PtNumber,
is PtBool,
is PtIdentifier,
is PtMachineRegister,
is PtArrayIndexer,
is PtPrefix,
is PtBinaryExpression -> { /* no cmp necessary the lda has been done just prior */ }
else -> asmgen.out(" cmp #0")
}
}
private fun fallbackTranslate(stmt: PtIfElse, warning: Boolean=true) {
if(warning) println("WARN: FALLBACK IF: ${stmt.position}") // TODO no more of these
val jumpAfterIf = stmt.ifScope.children.singleOrNull() as? PtJump
assignConditionValueToRegisterAndTest(stmt.condition)
if(jumpAfterIf!=null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("beq", stmt)
}
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
// comparison value is already in A
val afterIfLabel = asmgen.makeLabel("afterif")
if(stmt.hasElse()) {
// if and else blocks
val elseLabel = asmgen.makeLabel("else")
@ -32,116 +86,104 @@ internal class IfElseAsmGen(private val program: PtProgram, private val asmgen:
asmgen.out(afterIfLabel)
}
val compareCond = stmt.condition as? PtBinaryExpression
if(compareCond!=null) {
if((compareCond.right as? PtNumber)?.number==0.0 && compareCond.operator in arrayOf("==", "!=")) {
if (compareCond.right.type in ByteDatatypesWithBoolean) {
// equality comparison with 0 TODO temporary optimization
val elseBranch = if (compareCond.operator == "==") "bne" else "beq"
asmgen.assignExpressionToRegister(compareCond.left, RegisterOrPair.A, false)
translateIfElseBodies(elseBranch)
return
}
} else {
// TODO optimize if X comparison Y general case (also with 0 as a special case)
}
}
val prefixCond = stmt.condition as? PtPrefix
if(prefixCond?.operator=="not") {
asmgen.assignExpressionToRegister(prefixCond.value, RegisterOrPair.A, false)
translateIfElseBodies("bne") // inverted condition, just swap the branches
} else {
asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
translateIfElseBodies("beq")
}
}
private fun translateIfWithOnlyJump(stmt: PtIfElse) {
val jump = stmt.ifScope.children.single() as PtJump
val compareCond = stmt.condition as? PtBinaryExpression
fun doJumpAndElse(branchInstr: String, falseBranch: String, jump: PtJump) {
private fun translateJumpElseBodies(branchInstr: String, falseBranch: String, jump: PtJump, elseBlock: PtNodeGroup) {
// comparison value is already in A
val (asmLabel, indirect) = asmgen.getJumpTarget(jump)
if(indirect) {
asmgen.out("""
$falseBranch +
jmp ($asmLabel)
+ """)
+""")
} else {
asmgen.out(" $branchInstr $asmLabel")
}
if(stmt.hasElse())
asmgen.translate(stmt.elseScope)
asmgen.translate(elseBlock)
}
if(compareCond!=null) {
if (compareCond.operator in arrayOf("==", "!=")) {
val compareNumber = compareCond.right as? PtNumber
when (compareCond.right.type) {
in ByteDatatypesWithBoolean -> {
asmgen.assignExpressionToRegister(compareCond.left, RegisterOrPair.A, false)
if(compareNumber==null || compareNumber.number!=0.0)
compareRegisterAwithByte(compareCond.right)
if(compareCond.operator=="==") {
// if X==something goto blah
doJumpAndElse("beq", "bne", jump)
return
} else {
// if X!=something goto blah
doJumpAndElse("bne", "brq", jump)
return
}
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
doJumpAndElse("bne", "beq", jump)
return
// TODO: optimize word
// assignExpressionToRegister(compareCond.left, RegisterOrPair.AY, false)
// compareRegisterAYwithWord(compareCond.operator, compareCond.right, jump)
// if(compareCond.operator=="==") {
// // if X==something goto blah
// doJumpAndElse("beq", "bne", jump)
// return
// } else {
// // if X!=something goto blah
// doJumpAndElse("bne", "brq", jump)
// return
// }
}
DataType.FLOAT -> {
TODO()
}
else -> {
throw AssemblyError("weird dt")
}
}
private fun translateIfByte(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
val signed = condition.left.type in SignedDatatypes
val constValue = condition.right.asConstInteger()
if(constValue==0) {
if(condition.operator=="==") {
// if X==0
assignConditionValueToRegisterAndTest(condition.left)
return if(jumpAfterIf!=null)
translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bne", stmt)
} else if(condition.operator=="!=") {
// if X!=0
assignConditionValueToRegisterAndTest(condition.left)
return if(jumpAfterIf!=null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("beq", stmt)
}
}
asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
doJumpAndElse("bne", "beq", jump)
when (condition.operator) {
"==" -> {
// if X==value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
cmpAwithByteValue(condition.right)
return if(jumpAfterIf!=null)
translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bne", stmt)
}
"!=" -> {
// if X!=value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
cmpAwithByteValue(condition.right)
return if(jumpAfterIf!=null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("beq", stmt)
}
"<" -> {
// TODO()
println("byte if <")
fallbackTranslate(stmt)
}
"<=" -> {
// TODO()
println("byte if <=")
fallbackTranslate(stmt)
}
">" -> {
// TODO()
println("byte if >")
fallbackTranslate(stmt)
}
">=" -> {
// TODO()
println("byte if >=")
fallbackTranslate(stmt)
}
else -> fallbackTranslate(stmt, false)
}
}
private fun compareRegisterAwithByte(value: PtExpression) {
private fun cmpAwithByteValue(value: PtExpression) {
fun cmpViaScratch() {
if(!value.isSimple()) asmgen.out(" pha")
if(assignmentAsmGen.directIntoY(value)) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y, false)
asmgen.out(" sty P8ZP_SCRATCH_REG")
} else {
asmgen.out(" pha")
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type)
if(!value.isSimple()) asmgen.out(" pla")
asmgen.out(" pla")
}
asmgen.out(" cmp P8ZP_SCRATCH_REG")
}
// TODO how is if X==pointeervar[99] translated? can use cmp indirect indexed
when(value) {
is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger()
if(constIndex!=null) {
val offset = constIndex * program.memsizer.memorySize(value.type)
if(offset<256) {
asmgen.out(" ldy #$offset | cmp ${asmgen.asmVariableName(value.variable.name)},y")
return
return asmgen.out(" ldy #$offset | cmp ${asmgen.asmVariableName(value.variable.name)},y")
}
}
cmpViaScratch()
@ -167,4 +209,120 @@ internal class IfElseAsmGen(private val program: PtProgram, private val asmgen:
}
}
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
val signed = condition.left.type in SignedDatatypes
val constValue = condition.right.asConstInteger()
if(constValue==0) {
if(condition.operator=="==") {
// if X==0
// TODO even more optimized code by comparing lsb and msb separately
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.AY, signed)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG")
return if(jumpAfterIf!=null)
translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bne", stmt)
} else if(condition.operator=="!=") {
// if X!=0
// TODO even more optimized code by comparing lsb and msb separately
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.AY, signed)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG")
return if(jumpAfterIf!=null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("beq", stmt)
}
}
when(condition.operator) {
"==" -> {
// TODO
println("word if ==")
fallbackTranslate(stmt)
}
"!=" -> {
// TODO
println("word if !=")
fallbackTranslate(stmt)
}
"<" -> {
// TODO()
println("word if <")
fallbackTranslate(stmt)
}
"<=" -> {
// TODO()
println("word if <=")
fallbackTranslate(stmt)
}
">" -> {
// TODO()
println("word if >")
fallbackTranslate(stmt)
}
">=" -> {
// TODO()
println("word if >=")
fallbackTranslate(stmt)
}
else -> fallbackTranslate(stmt, false)
}
}
private fun translateIfFloat(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
val constValue = (condition.right as? PtNumber)?.number
if(constValue==0.0) {
if (condition.operator == "==") {
// if FL==0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0")
return if (jumpAfterIf != null)
translateJumpElseBodies("beq", "bne", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bne", stmt)
} else if(condition.operator=="!=") {
// if FL!=0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0")
return if (jumpAfterIf != null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("beq", stmt)
}
}
when(condition.operator) {
"==" -> {
// TODO
println("float if ==")
fallbackTranslate(stmt)
}
"!=" -> {
// TODO
println("float if !=")
fallbackTranslate(stmt)
}
"<" -> {
// TODO()
println("float if <")
fallbackTranslate(stmt)
}
"<=" -> {
// TODO()
println("float if <=")
fallbackTranslate(stmt)
}
">" -> {
// TODO()
println("float if >")
fallbackTranslate(stmt)
}
">=" -> {
// TODO()
println("float if >=")
fallbackTranslate(stmt)
}
else -> fallbackTranslate(stmt, false)
}
}
}

View File

@ -568,7 +568,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true
}
private fun directIntoY(expr: PtExpression): Boolean {
internal fun directIntoY(expr: PtExpression): Boolean {
return when(expr) {
is PtIdentifier -> true
is PtMachineRegister -> true

View File

@ -556,7 +556,7 @@ asmsub mouse_config2(byte shape @A) clobbers (A, X, Y) {
asmsub mouse_pos() clobbers(X) -> ubyte @A {
; -- short wrapper around mouse_get() kernal routine:
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status.
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status in A.
%asm {{
ldx #cx16.r0
jmp cx16.mouse_get

View File

@ -5,11 +5,11 @@ package prog8.buildversion
*/
const val MAVEN_GROUP = "prog8"
const val MAVEN_NAME = "compiler"
const val VERSION = "10.2-SNAPSHOT"
const val GIT_REVISION = 4459
const val GIT_SHA = "a1f197d2ff2e23c84cac26efbda75ef3efb1f0aa"
const val GIT_DATE = "2024-02-10T00:33:26Z"
const val GIT_BRANCH = "master"
const val BUILD_DATE = "2024-02-10T00:33:30Z"
const val BUILD_UNIX_TIME = 1707525210318L
const val DIRTY = 0
const val VERSION = "10.2-BOOLEANS"
const val GIT_REVISION = 4485
const val GIT_SHA = "eddf97191959f87119c746d8833b71a247db661d"
const val GIT_DATE = "2024-02-15T23:58:56Z"
const val GIT_BRANCH = "booleans"
const val BUILD_DATE = "2024-02-16T00:19:00Z"
const val BUILD_UNIX_TIME = 1708042740125L
const val DIRTY = 1

View File

@ -55,7 +55,6 @@ boolean trick to go from a compare >= value, to a bool
and #1
Chess: cannot click mouse to start. nothing happens.
Already broken in 10.0: textelite on c64, info on diso -> jams the cpu after printing
IR: add TEST instruction to test memory content and set N/Z flags, without affecting any register.

View File

@ -8,6 +8,15 @@ main {
bool @shared staticbool2
sub start() {
cx16.mouse_config2(1) ; enable mouse cursor (sprite 0)
while cx16.mouse_pos()==0 {
cx16.r0L++
}
while cx16.mouse_pos()!=0 {
cx16.r0L++
}
; boolean_const_and_var(true)
; staticbool1 = boolean_arrays_and_return()
; txt.print_ub(staticbool1 as ubyte)
@ -25,9 +34,11 @@ main {
; while_bool_efficient()
; efficient_compare_0()
; efficient_compare_99()
; efficient_compare_var()
; efficient_assign_cmp_0()
; efficient_assign_cmp_99()
if_gotos()
; efficient_assign_cmp_var()
; if_gotos()
; if_code()
;;sys.exit(1)
; while_equiv()
@ -52,16 +63,16 @@ main {
sub while_bool_efficient() {
while staticbool1 {
cx16.r0++
cx16.r0L++
}
while not staticbool1 {
cx16.r0++
cx16.r0L++
}
while cx16.r0L==0 {
cx16.r0++
cx16.r0L++
}
while cx16.r0L!=0 {
cx16.r0++
cx16.r0L++
}
}
@ -95,29 +106,49 @@ main {
bb2 = fl!=99.0
}
sub efficient_assign_cmp_var() {
ubyte @shared ub
uword @shared uw
float @shared fl
bool @shared bb1, bb2
float @shared fval
bb1 = ub==cx16.r0L
bb2 = ub!=cx16.r0L
bb1 = uw==cx16.r0
bb2 = uw!=cx16.r0
bb1 = fl==0.0
bb2 = fl!=0.0
bb1 = fl==fval
bb2 = fl!=fval
bb1 = bb2==uw[cx16.r0L]
bb2 = bb1!=uw[cx16.r1L]
}
sub efficient_compare_0() {
ubyte @shared ub
uword @shared uw
float @shared fl
if ub==0
cx16.r0++
cx16.r0L++
if uw==0
cx16.r0++
cx16.r0L++
if fl==0
cx16.r0++
cx16.r0L++
if ub!=0
cx16.r0++
cx16.r0L++
else
cx16.r1++
cx16.r1L++
if uw!=0
cx16.r0++
cx16.r0L++
else
cx16.r1++
cx16.r1L++
if fl!=0
cx16.r0++
cx16.r0L++
else
cx16.r1++
cx16.r1L++
}
sub efficient_compare_99() {
@ -126,23 +157,63 @@ main {
float @shared fl
if ub==99
cx16.r0++
cx16.r0L++
if uw==99
cx16.r0++
cx16.r0L++
if fl==99.99
cx16.r0++
cx16.r0L++
if ub!=99
cx16.r0++
cx16.r0L++
else
cx16.r1++
cx16.r1L++
if uw!=99
cx16.r0++
cx16.r0L++
else
cx16.r1++
cx16.r1L++
if fl!=99.99
cx16.r0++
cx16.r0L++
else
cx16.r1++
cx16.r1L++
}
sub efficient_compare_var() {
ubyte @shared ub
uword @shared uw
float @shared fl
float @shared fval
if ub==cx16.r0L
cx16.r0L++
if uw==cx16.r0
cx16.r0L++
if fl==fval
cx16.r0L++
if fl==0.0
cx16.r0L++
if ub!=cx16.r0L
cx16.r0L++
else
cx16.r1L++
if uw!=cx16.r0
cx16.r0L++
else
cx16.r1L++
if fl!=fval
cx16.r0L++
else
cx16.r1L++
if fl!=0.0
cx16.r0L++
else
cx16.r1L++
if ub==uw[cx16.r0L]
cx16.r0L++
if ub!=uw[cx16.r0L]
cx16.r0L++
if staticbool2 or staticbool1
cx16.r0L++
}
sub logical_operand_swap() {
@ -264,29 +335,29 @@ main {
;
; sub while_until_int_errors() {
;; while staticbool1==42 {
;; cx16.r0++
;; cx16.r0L++
;; }
;;
;; do {
;; cx16.r0++
;; cx16.r0L++
;; } until staticbool1==42
;
; ubyte @shared ub1
;
; while not ub1 {
; cx16.r0++
; cx16.r0L++
; }
;
; while intfunc() {
; cx16.r0++
; cx16.r0L++
; }
;
; while not intfunc() {
; cx16.r0++
; cx16.r0L++
; }
;
;; while not cx16.mouse_pos() {
;; cx16.r0++
;; cx16.r0L++
;; }
; }
@ -295,16 +366,16 @@ main {
bool @shared bb
while bb {
cx16.r0++
cx16.r0L++
}
while ub!=0 {
cx16.r0++
cx16.r0L++
}
while not bb {
cx16.r0++
cx16.r0L++
}
while ub==0 {
cx16.r0++
cx16.r0L++
}
}
@ -340,7 +411,7 @@ main {
sub bools_in_array_assigns_inplace() {
bool[] ba = [true, false, true]
cx16.r0++
cx16.r0L++
ba[1] = ba[1] xor staticbool2
ba[2] = staticbool2 xor ba[2]
ba[1] = ba[1] and staticbool2
@ -374,32 +445,37 @@ main {
if_cc
goto label
else
cx16.r0++
cx16.r0L++
if_cs
goto label
else
cx16.r0++
cx16.r0L++
if ub==0
goto label
if ub!=0
goto label
if not ub==99
goto label
if ub==0
goto label
else
cx16.r0++
if ub!=0
goto label
else
cx16.r0++
if not ub==98
goto label
else
cx16.r0++
cx16.r0L++
if ub==0
goto label
else
cx16.r0L++
if ub!=0
goto label
else
cx16.r0L++
if staticbool1
goto label
if not staticbool2
goto label
label:
}
@ -407,28 +483,28 @@ main {
ubyte @shared ub
bool @shared bb
if ub==0
cx16.r0++
cx16.r0L++
if ub!=0
cx16.r0++
cx16.r0L++
if bb
cx16.r0++
cx16.r0L++
if not bb
cx16.r0++
cx16.r0L++
if ub==0
cx16.r0++
cx16.r0L++
else
cx16.r0--
if ub!=0
cx16.r0++
cx16.r0L++
else
cx16.r0--
if bb
cx16.r0++
cx16.r0L++
else
cx16.r0--
if not bb
cx16.r0++
cx16.r0L++
else
cx16.r0--
}
@ -438,6 +514,6 @@ main {
}
sub voidfuncub(ubyte arg) {
cx16.r0++
cx16.r0L++
}
}