diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 5e5b901c5..edb8d8054 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -98,6 +98,8 @@ class PtIfElse(position: Position) : PtNode(position) { get() = children[1] as PtNodeGroup val elseScope: PtNodeGroup get() = children[2] as PtNodeGroup + + fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty() } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index c26142c9a..9d157d5d3 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -235,6 +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) fun compileToAssembly(): IAssemblyProgram? { @@ -594,7 +595,7 @@ class AsmGen6502Internal ( } is PtLabel -> translate(stmt) is PtConditionalBranch -> translate(stmt) - is PtIfElse -> translate(stmt) + is PtIfElse -> ifElseAsmgen.translate(stmt) is PtForLoop -> forloopsAsmGen.translate(stmt) is PtRepeatLoop -> translate(stmt) is PtWhen -> translate(stmt) @@ -767,26 +768,6 @@ class AsmGen6502Internal ( } } - private fun translate(stmt: PtIfElse) { - require(stmt.condition.type==DataType.BOOL) - assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false) - val afterIfLabel = makeLabel("if") - if(stmt.children.size>1) { - // if and else blocks - val elseLabel = makeLabel("else") - out(" beq $elseLabel") - translate(stmt.ifScope) - jmp(afterIfLabel, false) - out(elseLabel) - translate(stmt.elseScope) - } else { - // no else block - out(" beq $afterIfLabel") - translate(stmt.ifScope) - } - out(afterIfLabel) - } - private fun translate(stmt: PtRepeatLoop) { val endLabel = makeLabel("repeatend") loopEndLabels.push(endLabel) @@ -880,7 +861,6 @@ $repeatLabel""") out(endLabel) } - private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) { require(count in 2..256) { "invalid repeat count ${stmt.position}" } val repeatLabel = makeLabel("repeat") @@ -1041,7 +1021,7 @@ $repeatLabel""") } } - private fun getJumpTarget(jump: PtJump): Pair { + internal fun getJumpTarget(jump: PtJump): Pair { val ident = jump.identifier val addr = jump.address return when { @@ -1307,609 +1287,6 @@ $repeatLabel""") } } - private fun translateUwordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - - fun code(msbCpyOperand: String, lsbCmpOperand: String) { - out(""" - cpy $msbCpyOperand - bcc $jumpIfFalseLabel - bne + - cmp $lsbCmpOperand - bcc $jumpIfFalseLabel -+""") - } - - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") - return - } - } - } - } - - if(wordJumpForSimpleRightOperands(left, right, ::code)) - return - - assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2") - return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel") - } - - private fun translateWordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - - fun code(msbCpyOperand: String, lsbCmpOperand: String) { - out(""" - cmp $lsbCmpOperand - tya - sbc $msbCpyOperand - bvc + - eor #$80 -+ bmi $jumpIfFalseLabel""") - } - - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}") - } - else { - val name = asmVariableName(left) - out(" lda $name+1 | bmi $jumpIfFalseLabel") - } - } - } - } - - if(wordJumpForSimpleRightOperands(left, right, ::code)) - return - - assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2") - return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel") - } - - private fun translateByteEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - fun code(cmpOperand: String) { - out(" cmp $cmpOperand | bne $jumpIfFalseLabel") - } - - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal!=leftConstVal) - jmp(jumpIfFalseLabel) - return - } else { - if (left is PtIdentifier) { - assignExpressionToRegister(left, RegisterOrPair.A) - return if(rightConstVal.number.toInt()!=0) - code("#${rightConstVal.number.toInt()}") - else - out(" bne $jumpIfFalseLabel") - } - else if (left is PtMemoryByte) { - translateDirectMemReadExpressionToRegA(left) - return if(rightConstVal.number.toInt()!=0) - code("#${rightConstVal.number.toInt()}") - else - out(" bne $jumpIfFalseLabel") - } - } - } - - if(byteJumpForSimpleRightOperand(left, right, ::code)) - return - - if(left.isSimple()) { - assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE) - assignExpressionToRegister(left, RegisterOrPair.A) - } else if(right.isSimple()) { - assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE) - assignExpressionToRegister(right, RegisterOrPair.A) - } else { - pushCpuStack(DataType.UBYTE, left) - assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE) - restoreRegisterStack(CpuRegister.A, false) - } - out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel") - } - - private fun translateByteNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - - fun code(cmpOperand: String) { - out(" cmp $cmpOperand | beq $jumpIfFalseLabel") - } - - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal==leftConstVal) - jmp(jumpIfFalseLabel) - return - } else { - if (left is PtIdentifier) { - assignExpressionToRegister(left, RegisterOrPair.A) - return if(rightConstVal.number.toInt()!=0) - code("#${rightConstVal.number.toInt()}") - else - out(" beq $jumpIfFalseLabel") - } - else if (left is PtMemoryByte) { - translateDirectMemReadExpressionToRegA(left) - return if(rightConstVal.number.toInt()!=0) - code("#${rightConstVal.number.toInt()}") - else - out(" beq $jumpIfFalseLabel") - } - } - } - - if(byteJumpForSimpleRightOperand(left, right, ::code)) - return - - if(left.isSimple()) { - assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE) - assignExpressionToRegister(left, RegisterOrPair.A) - } else if(right.isSimple()) { - assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE) - assignExpressionToRegister(right, RegisterOrPair.A) - } else { - pushCpuStack(DataType.UBYTE, left) - assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE) - restoreRegisterStack(CpuRegister.A, false) - } - return code("P8ZP_SCRATCH_B1") - } - - private fun translateWordEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal!=leftConstVal) - jmp(jumpIfFalseLabel) - return - } else { - if (left is PtIdentifier) { - val name = asmVariableName(left) - if(rightConstVal.number!=0.0) { - val rightNum = rightConstVal.number.toHex() - out(""" - lda $name - cmp #<$rightNum - bne $jumpIfFalseLabel - lda $name+1 - cmp #>$rightNum - bne $jumpIfFalseLabel""") - } - else { - out(""" - lda $name - bne $jumpIfFalseLabel - lda $name+1 - bne $jumpIfFalseLabel""") - } - return - } - } - } - - when (right) { - is PtBool -> TODO("word equals for bool operand") - is PtNumber -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val number = right.number.toHex() - out(""" - cmp #<$number - bne $jumpIfFalseLabel - cpy #>$number - bne $jumpIfFalseLabel""") - } - is PtIdentifier -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - out(""" - cmp ${asmVariableName(right)} - bne $jumpIfFalseLabel - cpy ${asmVariableName(right)}+1 - bne $jumpIfFalseLabel""") - } - is PtAddressOf -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val name = asmSymbolName(right.identifier) - if(right.isFromArrayElement) { - TODO("address-of array element $name at ${right.position}") - // assignmentAsmGen.assignAddressOf(target, name, right.arrayIndexExpr) - } else { - out(""" - cmp #<$name - bne $jumpIfFalseLabel - cpy #>$name - bne $jumpIfFalseLabel""") - } - } - else -> { - if(left.isSimple()) { - assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD) - assignExpressionToRegister(left, RegisterOrPair.AY) - } else if(right.isSimple()) { - assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD) - assignExpressionToRegister(right, RegisterOrPair.AY) - } else { - pushCpuStack(DataType.UWORD, left) - assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD) - restoreRegisterStack(CpuRegister.Y, false) - restoreRegisterStack(CpuRegister.A, false) - } - out(""" - cmp P8ZP_SCRATCH_W2 - bne $jumpIfFalseLabel - cpy P8ZP_SCRATCH_W2+1 - bne $jumpIfFalseLabel""") - } - } - - } - - private fun translateWordNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal==leftConstVal) - jmp(jumpIfFalseLabel) - return - } else { - if (left is PtIdentifier) { - val name = asmVariableName(left) - if(rightConstVal.number.toInt()!=0) { - val number = rightConstVal.number.toHex() - out(""" - lda $name - cmp #<$number - bne + - lda $name+1 - cmp #>$number - beq $jumpIfFalseLabel -+""") - } - else - out(""" - lda $name - bne + - lda $name+1 - beq $jumpIfFalseLabel -+""") - return - } - } - } - - when (right) { - is PtBool -> TODO("word not equals for bool operand") - is PtNumber -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val number = right.number.toHex() - out(""" - cmp #<$number - bne + - cpy #>$number - beq $jumpIfFalseLabel -+""") - } - is PtIdentifier -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - out(""" - cmp ${asmVariableName(right)} - bne + - cpy ${asmVariableName(right)}+1 - beq $jumpIfFalseLabel -+""") - } - is PtAddressOf -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val name = asmSymbolName(right.identifier) - if(right.isFromArrayElement) { - TODO("address-of array element $name at ${right.position}") - } else { - out(""" - cmp #<$name - bne + - cpy #>$name - beq $jumpIfFalseLabel - +""") - } - } - else -> { - TODO("word not equals boolean in special case?") - if(left.isSimple()) { - assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD) - assignExpressionToRegister(left, RegisterOrPair.AY) - } else if (right.isSimple()) { - assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD) - assignExpressionToRegister(right, RegisterOrPair.AY) - } else { - pushCpuStack(DataType.UWORD, left) - assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD) - restoreRegisterStack(CpuRegister.Y, false) - restoreRegisterStack(CpuRegister.A, false) - } - out(""" - cmp P8ZP_SCRATCH_W2 - bne + - cpy P8ZP_SCRATCH_W2+1 - beq $jumpIfFalseLabel -+""" - ) - } - } - - } - - private fun translateFloatEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal!=leftConstVal) - jmp(jumpIfFalseLabel) - return - } else { - if (left is PtIdentifier) { - val name = asmVariableName(left) - when(rightConstVal.number) - { - 0.0 -> { - out(""" - lda $name - clc - adc $name+1 - adc $name+2 - adc $name+3 - adc $name+4 - bne $jumpIfFalseLabel""") - return - } - 1.0 -> { - out(""" - lda $name - cmp #129 - bne $jumpIfFalseLabel - lda $name+1 - clc - adc $name+2 - adc $name+3 - adc $name+4 - bne $jumpIfFalseLabel""") - return - } - } - } - } - } - - if(leftConstVal!=null && rightConstVal!=null) { - throw AssemblyError("const-compare should have been optimized away") - } - else if(leftConstVal!=null && right is PtIdentifier) { - throw AssemblyError("const-compare should have been optimized to have const as right operand") - } - else if(left is PtIdentifier && rightConstVal!=null) { - val leftName = asmVariableName(left) - val rightName = allocator.getFloatAsmConst(rightConstVal.number) - out(""" - lda #<$leftName - ldy #>$leftName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #<$rightName - ldy #>$rightName - jsr floats.vars_equal_f - beq $jumpIfFalseLabel""") - } - else if(left is PtIdentifier && right is PtIdentifier) { - val leftName = asmVariableName(left) - val rightName = asmVariableName(right) - out(""" - lda #<$leftName - ldy #>$leftName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #<$rightName - ldy #>$rightName - jsr floats.vars_equal_f - beq $jumpIfFalseLabel""") - } else { - val subroutine = left.definingSub()!! - subroutineExtra(subroutine).usedFloatEvalResultVar1 = true - assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT) - assignExpressionToRegister(left, RegisterOrPair.FAC1) - out(""" - lda #<$subroutineFloatEvalResultVar1 - ldy #>$subroutineFloatEvalResultVar1 - jsr floats.var_fac1_notequal_f - bne $jumpIfFalseLabel""") - } - } - - private fun translateFloatNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) { - if(rightConstVal!=null) { - if(leftConstVal!=null) { - if(rightConstVal==leftConstVal) - jmp(jumpIfFalseLabel) - return - } else { - if (left is PtIdentifier) { - val name = asmVariableName(left) - when(rightConstVal.number) - { - 0.0 -> { - out(""" - lda $name - clc - adc $name+1 - adc $name+2 - adc $name+3 - adc $name+4 - beq $jumpIfFalseLabel""") - return - } - 1.0 -> { - out(""" - lda $name - cmp #129 - bne + - lda $name+1 - clc - adc $name+2 - adc $name+3 - adc $name+4 - beq $jumpIfFalseLabel -+""") - return - } - } - } - } - } - - if(leftConstVal!=null && rightConstVal!=null) { - throw AssemblyError("const-compare should have been optimized away") - } - else if(leftConstVal!=null && right is PtIdentifier) { - throw AssemblyError("const-compare should have been optimized to have const as right operand") - } - else if(left is PtIdentifier && rightConstVal!=null) { - val leftName = asmVariableName(left) - val rightName = allocator.getFloatAsmConst(rightConstVal.number) - out(""" - lda #<$leftName - ldy #>$leftName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #<$rightName - ldy #>$rightName - jsr floats.vars_equal_f - bne $jumpIfFalseLabel""") - } - else if(left is PtIdentifier && right is PtIdentifier) { - val leftName = asmVariableName(left) - val rightName = asmVariableName(right) - out(""" - lda #<$leftName - ldy #>$leftName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #<$rightName - ldy #>$rightName - jsr floats.vars_equal_f - bne $jumpIfFalseLabel""") - } else { - val subroutine = left.definingSub()!! - subroutineExtra(subroutine).usedFloatEvalResultVar1 = true - assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT) - assignExpressionToRegister(left, RegisterOrPair.FAC1) - out(""" - lda #<$subroutineFloatEvalResultVar1 - ldy #>$subroutineFloatEvalResultVar1 - jsr floats.var_fac1_notequal_f - beq $jumpIfFalseLabel""") - } - } - - private fun translateStringEqualsOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { - val leftNam = asmVariableName(left) - val rightNam = asmVariableName(right) - out(""" - lda #<$rightNam - sta P8ZP_SCRATCH_W2 - lda #>$rightNam - sta P8ZP_SCRATCH_W2+1 - lda #<$leftNam - ldy #>$leftNam - jsr prog8_lib.strcmp_mem - bne $jumpIfFalseLabel""") - } - - private fun translateStringNotEqualsOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { - val leftNam = asmVariableName(left) - val rightNam = asmVariableName(right) - out(""" - lda #<$rightNam - sta P8ZP_SCRATCH_W2 - lda #>$rightNam - sta P8ZP_SCRATCH_W2+1 - lda #<$leftNam - ldy #>$leftNam - jsr prog8_lib.strcmp_mem - beq $jumpIfFalseLabel""") - } - - private fun translateStringLessOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { - val leftNam = asmVariableName(left) - val rightNam = asmVariableName(right) - out(""" - lda #<$rightNam - sta P8ZP_SCRATCH_W2 - lda #>$rightNam - sta P8ZP_SCRATCH_W2+1 - lda #<$leftNam - ldy #>$leftNam - jsr prog8_lib.strcmp_mem - bpl $jumpIfFalseLabel""") - } - - private fun translateStringGreaterOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { - val leftNam = asmVariableName(left) - val rightNam = asmVariableName(right) - out(""" - lda #<$rightNam - sta P8ZP_SCRATCH_W2 - lda #>$rightNam - sta P8ZP_SCRATCH_W2+1 - lda #<$leftNam - ldy #>$leftNam - jsr prog8_lib.strcmp_mem - beq $jumpIfFalseLabel - bmi $jumpIfFalseLabel""") - } - - private fun translateStringLessOrEqualOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { - val leftNam = asmVariableName(left) - val rightNam = asmVariableName(right) - out(""" - lda #<$rightNam - sta P8ZP_SCRATCH_W2 - lda #>$rightNam - sta P8ZP_SCRATCH_W2+1 - lda #<$leftNam - ldy #>$leftNam - jsr prog8_lib.strcmp_mem - beq + - bpl $jumpIfFalseLabel -+""") - } - - private fun translateStringGreaterOrEqualOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) { - val leftNam = asmVariableName(left) - val rightNam = asmVariableName(right) - out(""" - lda #<$rightNam - sta P8ZP_SCRATCH_W2 - lda #>$rightNam - sta P8ZP_SCRATCH_W2+1 - lda #<$leftNam - ldy #>$leftNam - jsr prog8_lib.strcmp_mem - beq + - bmi $jumpIfFalseLabel -+""") - } - internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) { fun assignViaExprEval() { @@ -1940,100 +1317,6 @@ $repeatLabel""") } } - private fun wordJumpForSimpleLeftOperand(left: PtExpression, right: PtExpression, code: (String, String)->Unit): Boolean { - when (left) { - is PtBool -> TODO("word jump for bool operand") - is PtNumber -> { - assignExpressionToRegister(right, RegisterOrPair.AY) - val number = left.number.toHex() - code("#>$number", "#<$number") - return true - } - is PtAddressOf -> { - assignExpressionToRegister(right, RegisterOrPair.AY) - val name = asmSymbolName(left.identifier) - if(left.isFromArrayElement) { - TODO("address-of array element $name at ${left.position}") - } else { - code("#>$name", "#<$name") - return true - } - } - is PtIdentifier -> { - assignExpressionToRegister(right, RegisterOrPair.AY) - val varname = asmVariableName(left) - code("$varname+1", varname) - return true - } - else -> return false - } - } - - private fun byteJumpForSimpleRightOperand(left: PtExpression, right: PtExpression, code: (String)->Unit): Boolean { - when (right) { - is PtBool -> { - assignExpressionToRegister(left, RegisterOrPair.A) - code("#${right.asInt()} ; TODO can we get rid of this cmp when dealing with booleans?") // TODO can we get rid of the cmp when dealing with booleans? - return true - } - is PtNumber -> { - assignExpressionToRegister(left, RegisterOrPair.A) - code("#${right.number.toHex()}") - return true - } - - is PtIdentifier -> { - assignExpressionToRegister(left, RegisterOrPair.A) - code(asmVariableName(right)) - return true - } - - else -> { - var memread = right as? PtMemoryByte - if (memread == null && right is PtTypeCast) - memread = right.value as? PtMemoryByte - if (memread != null) { - val address = memread.address as? PtNumber - if (address != null) { - assignExpressionToRegister(left, RegisterOrPair.A) - code(address.number.toHex()) - return true - } - } - return false - } - } - } - - private fun wordJumpForSimpleRightOperands(left: PtExpression, right: PtExpression, code: (String, String)->Unit): Boolean { - when (right) { - is PtBool -> TODO("word jump for bool operand") - is PtNumber -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val number = right.number.toHex() - code("#>$number", "#<$number") - return true - } - is PtAddressOf -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val name = asmSymbolName(right.identifier) - if(right.isFromArrayElement) { - TODO("address-of array element $name at ${right.position}") - } else { - code("#>$name", "#<$name") - return true - } - } - is PtIdentifier -> { - assignExpressionToRegister(left, RegisterOrPair.AY) - val varname = asmVariableName(right) - code("$varname+1", varname) - return true - } - else -> return false - } - } - internal fun popCpuStack(asmsub: PtAsmSub, parameter: PtSubroutineParameter, reg: RegisterOrStatusflag) { val shouldKeepA = asmsub.parameters.any { it.first.registerOrPair==RegisterOrPair.AX || it.first.registerOrPair==RegisterOrPair.AY} if(reg.statusflag!=null) { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index 524031a91..69c72db4f 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -46,7 +46,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as else if(sub is PtSub) { if(optimizeIntArgsViaRegisters(sub)) { if(sub.parameters.size==1) { - val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY + val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register) } else { // 2 byte params, second in Y, first in A @@ -222,6 +222,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { if(argType isAssignableTo paramType) return true + if(argType==DataType.BOOL && paramType==DataType.BOOL) + return true if(argType in ByteDatatypes && paramType in ByteDatatypes) return true if(argType in WordDatatypes && paramType in WordDatatypes) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt new file mode 100644 index 000000000..a4327a295 --- /dev/null +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt @@ -0,0 +1,170 @@ +package prog8.codegen.cpu6502 + +import prog8.code.ast.* +import prog8.code.core.* + +internal class IfElseAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) { + + fun translate(stmt: PtIfElse) { + require(stmt.condition.type== DataType.BOOL) + + if(stmt.ifScope.children.singleOrNull() is PtJump) { + translateIfWithOnlyJump(stmt) + return + } + + val afterIfLabel = asmgen.makeLabel("afterif") + + fun translateIfElseBodies(elseBranchInstr: String) { + if(stmt.hasElse()) { + // if and else blocks + val elseLabel = asmgen.makeLabel("else") + asmgen.out(" $elseBranchInstr $elseLabel") + asmgen.translate(stmt.ifScope) + asmgen.jmp(afterIfLabel, false) + asmgen.out(elseLabel) + asmgen.translate(stmt.elseScope) + } else { + // no else block + asmgen.out(" $elseBranchInstr $afterIfLabel") + asmgen.translate(stmt.ifScope) + } + 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) { + 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) + } + + 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") + } + } + } + } + + asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false) + doJumpAndElse("bne", "beq", jump) + } + + private fun compareRegisterAwithByte(value: PtExpression) { + fun cmpViaScratch() { + if(!value.isSimple()) asmgen.out(" pha") + asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type) + if(!value.isSimple()) 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 + } + } + cmpViaScratch() + } + is PtMemoryByte -> { + val constAddr = value.address.asConstInteger() + if(constAddr!=null) { + asmgen.out(" cmp ${constAddr.toHex()}") + } else { + cmpViaScratch() + } + } + is PtIdentifier -> { + asmgen.out(" cmp ${asmgen.asmVariableName(value.name)}") + } + is PtNumber -> { + if(value.number!=0.0) + asmgen.out(" cmp #${value.number.toInt()}") + } + else -> { + cmpViaScratch() + } + } + } + +} \ No newline at end of file diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index b087145d9..1a61f1750 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -173,6 +173,7 @@ internal class ProgramAndVarsGen( for(num in 1..count) { val name = asmgen.buildTempVarName(dt, num) when (dt) { + DataType.BOOL -> asmgen.out("$name .byte ?") DataType.BYTE -> asmgen.out("$name .char ?") DataType.UBYTE -> asmgen.out("$name .byte ?") DataType.WORD -> asmgen.out("$name .sint ?") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index cb0dfb687..17373302c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -44,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, } init { - if(register!=null && datatype !in NumericDatatypes) - throw AssemblyError("register must be integer or float type") + if(register!=null && datatype !in NumericDatatypesWithBoolean) + throw AssemblyError("must be numeric type") } companion object { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 70ff4d103..16c044518 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -340,7 +340,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } else { // use a temporary variable - val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1" + val tempvar = if(value.type in ByteDatatypesWithBoolean) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1" assignExpressionToVariable(value.value, tempvar, value.type) when (value.operator) { "+" -> {} @@ -394,16 +394,15 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true) assignFAC1float(assign.target) } else { - // array[x] = -value ... use a tempvar then store that back into the array. - val tempvar = asmgen.getTempVarName(assign.target.datatype) - val assignToTempvar = AsmAssignment(assign.source, - AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position, - variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position) - asmgen.translateNormalAssignment(assignToTempvar, scope) + val register = if(assign.source.datatype in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY + val assignToRegister = AsmAssignment(assign.source, + AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, assign.target.datatype, assign.target.scope, assign.target.position, + register = register, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position) + asmgen.translateNormalAssignment(assignToRegister, scope) + val signed = assign.target.datatype in SignedDatatypes when(assign.target.datatype) { - in ByteDatatypesWithBoolean -> assignVariableByte(assign.target, tempvar) - in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype) - DataType.FLOAT -> assignVariableFloat(assign.target, tempvar) + in ByteDatatypesWithBoolean -> assignRegisterByte(assign.target, CpuRegister.A, signed, false) + in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) else -> throw AssemblyError("weird dt") } } @@ -1149,6 +1148,22 @@ internal class AssignmentAsmGen(private val program: PtProgram, return true } } + val rightArray = expr.right as? PtArrayIndexer + if(rightArray!=null) { + val constIndex = rightArray.index.asConstInteger() + if(constIndex!=null) { + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) + val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${constIndex*program.memsizer.memorySize(rightArray.type)}" + when(expr.operator) { + "&" -> asmgen.out(" and $valueVarname") + "|" -> asmgen.out(" ora $valueVarname") + "^" -> asmgen.out(" eor $valueVarname") + else -> throw AssemblyError("invalid logical operator") + } + assignRegisterByte(target, CpuRegister.A, false, true) + return true + } + } assignExpressionToRegister(expr.left, RegisterOrPair.A, false) if(directIntoY(expr.right)) { @@ -1162,7 +1177,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, when (expr.operator) { "&" -> asmgen.out(" and P8ZP_SCRATCH_B1") "|" -> asmgen.out(" ora P8ZP_SCRATCH_B1") - "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") + "^" -> asmgen.out(" eor P8ZP_SCRATCH_B1") else -> throw AssemblyError("invalid bitwise operator") } assignRegisterByte(target, CpuRegister.A, false, true) @@ -1183,7 +1198,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, when (expr.operator) { "&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa") "|" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa") - "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa") + "^" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa") else -> throw AssemblyError("invalid bitwise operator") } assignRegisterpairWord(target, RegisterOrPair.AY) @@ -1193,61 +1208,103 @@ internal class AssignmentAsmGen(private val program: PtProgram, } private fun optimizedLogicalExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean { - if (expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean) { - if (expr.right is PtBool || expr.right is PtNumber || expr.right is PtIdentifier) { - assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right) - return true - } - else if (expr.left is PtBool || expr.left is PtNumber || expr.left is PtIdentifier) { - assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left) - return true - } - if(!expr.right.isSimple() && expr.operator!="xor") { - // shortcircuit evaluation into A - val shortcutLabel = asmgen.makeLabel("shortcut") - when (expr.operator) { - "and" -> { - // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) - assignExpressionToRegister(expr.left, RegisterOrPair.A, false) - asmgen.out(" beq $shortcutLabel") - assignExpressionToRegister(expr.right, RegisterOrPair.A, false) - asmgen.out(shortcutLabel) - } - "or" -> { - // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT - assignExpressionToRegister(expr.left, RegisterOrPair.A, false) - asmgen.out(" bne $shortcutLabel") - assignExpressionToRegister(expr.right, RegisterOrPair.A, false) - asmgen.out(shortcutLabel) - } - else -> throw AssemblyError("invalid logical operator") - } - } else { - // normal evaluation into A, it is *likely* shorter and faster because of the simple operands. - assignExpressionToRegister(expr.left, RegisterOrPair.A, false) - if(directIntoY(expr.right)) { - assignExpressionToRegister(expr.right, RegisterOrPair.Y, false) + fun swapOperands(): Boolean = + if(expr.right is PtIdentifier || expr.right is PtMemoryByte) + false + else + expr.left is PtIdentifier || expr.left is PtMemoryByte + + fun assignResultIntoA(left: PtExpression, operator: String, right: PtExpression) { + // non short-circuit evaluation it is *likely* shorter and faster because of the simple operands. + + fun assignViaScratch() { + if(directIntoY(right)) { + assignExpressionToRegister(right, RegisterOrPair.Y, false) asmgen.out(" sty P8ZP_SCRATCH_B1") } else { asmgen.out(" pha") - assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) + assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE) asmgen.out(" pla") } - when (expr.operator) { + when (operator) { "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") else -> throw AssemblyError("invalid logical operator") } } - assignRegisterByte(target, CpuRegister.A, false, true) - return true + + assignExpressionToRegister(left, RegisterOrPair.A, false) + when(right) { + is PtBool -> throw AssemblyError("bool literal in logical expr should have been optimized away") + is PtIdentifier -> { + val varname = asmgen.asmVariableName(right) + when (operator) { + "and" -> asmgen.out(" and $varname") + "or" -> asmgen.out(" ora $varname") + "xor" -> asmgen.out(" eor $varname") + else -> throw AssemblyError("invalid logical operator") + } + } + is PtMemoryByte -> { + val constAddress = right.address.asConstInteger() + if(constAddress!=null) { + when (operator) { + "and" -> asmgen.out(" and ${constAddress.toHex()}") + "or" -> asmgen.out(" ora ${constAddress.toHex()}") + "xor" -> asmgen.out(" eor ${constAddress.toHex()}") + else -> throw AssemblyError("invalid logical operator") + } + } + else assignViaScratch() + } + is PtArrayIndexer -> { + val constIndex = right.index.asConstInteger() + if(constIndex!=null) { + val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${constIndex*program.memsizer.memorySize(right.type)}" + when(operator) { + "and" -> asmgen.out(" and $valueVarname") + "or" -> asmgen.out(" ora $valueVarname") + "xor" -> asmgen.out(" eor $valueVarname") + else -> throw AssemblyError("invalid logical operator") + } + } + else assignViaScratch() + } + else -> assignViaScratch() + } } - else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { - throw AssemblyError("logical and/or/xor on words should be done on typecast to bytes instead") + + if(!expr.right.isSimple() && expr.operator!="xor") { + // shortcircuit evaluation into A + val shortcutLabel = asmgen.makeLabel("shortcut") + when (expr.operator) { + "and" -> { + // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) + asmgen.out(" beq $shortcutLabel") + assignExpressionToRegister(expr.right, RegisterOrPair.A, false) + asmgen.out(shortcutLabel) + } + "or" -> { + // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT + assignExpressionToRegister(expr.left, RegisterOrPair.A, false) + asmgen.out(" bne $shortcutLabel") + assignExpressionToRegister(expr.right, RegisterOrPair.A, false) + asmgen.out(shortcutLabel) + } + else -> throw AssemblyError("invalid logical operator") + } + } else if(swapOperands()) { + // non short-circuit evaluation is *likely* shorter and faster because of the simple operands. + assignResultIntoA(expr.right, expr.operator, expr.left) + } else { + assignResultIntoA(expr.left, expr.operator, expr.right) } - return false + + assignRegisterByte(target, CpuRegister.A, false, true) + return true } private fun assignBitwiseWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { @@ -1266,26 +1323,6 @@ internal class AssignmentAsmGen(private val program: PtProgram, assignRegisterByte(target, CpuRegister.A, false, true) } - private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { - // normal evaluation, not worth to shortcircuit the simple right operand - assignExpressionToRegister(left, RegisterOrPair.A, false) - if(directIntoY(right)) { - assignExpressionToRegister(right, RegisterOrPair.Y, false) - asmgen.out(" sty P8ZP_SCRATCH_B1") - } else { - asmgen.out(" pha") - assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE) - asmgen.out(" pla") - } - when (operator) { - "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") - "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") - "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") - else -> throw AssemblyError("invalid logical operator") - } - assignRegisterByte(target, CpuRegister.A, false, true) - } - private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) { assignExpressionToRegister(left, RegisterOrPair.AY, false) when(right) { @@ -1555,7 +1592,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } - if(valueDt in ByteDatatypes) { + if(valueDt in ByteDatatypesWithBoolean) { when(target.register) { RegisterOrPair.A, RegisterOrPair.X, @@ -2763,7 +2800,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, if(indexVar!=null) { asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y") } else { - require(target.array.index.type in ByteDatatypes) + require(target.array.index.type in ByteDatatypesWithBoolean) asmgen.saveRegisterStack(register, false) asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false) asmgen.out(" pla | sta ${target.asmVarname},y") @@ -3487,7 +3524,10 @@ internal class AssignmentAsmGen(private val program: PtProgram, else -> throw AssemblyError("invalid reg dt for byte invert") } } - TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope) + TargetStorageKind.ARRAY -> { + val invertOperator = if(assign.target.datatype==DataType.BOOL) "not" else "~" + assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign(invertOperator, assign), scope) + } } } DataType.UWORD -> { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index c479ad350..f77f09303 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -63,9 +63,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}" - if(value.kind==SourceStorageKind.LITERALBOOLEAN) - TODO("inplace modification literalboolean") - when (target.kind) { TargetStorageKind.VARIABLE -> { when (target.datatype) { @@ -240,7 +237,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, // normal array val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}" when (target.datatype) { - in ByteDatatypes -> { + in ByteDatatypesWithBoolean -> { when(value.kind) { SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt()) SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt()) @@ -309,7 +306,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, return when (target.datatype) { - in ByteDatatypes -> { + in ByteDatatypesWithBoolean -> { if(value.kind==SourceStorageKind.EXPRESSION && value.expression is PtTypeCast && tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) @@ -1092,27 +1089,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) { - val shortcutLabel = asmgen.makeLabel("shortcut") - when (operator) { - "and" -> { - // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) - asmgen.out(" lda $name | beq $shortcutLabel") - asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes) - asmgen.out(""" - and $name - sta $name + if(!value.isSimple()) { + // attempt short-circuit (McCarthy) evaluation + when (operator) { + "and" -> { + // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) + val shortcutLabel = asmgen.makeLabel("shortcut") + asmgen.out(" lda $name | beq $shortcutLabel") + asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes) + asmgen.out(""" + and $name + sta $name $shortcutLabel:""") - return - } - "or" -> { - // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT - asmgen.out(" lda $name | bne $shortcutLabel") - asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes) - asmgen.out(""" - ora $name - sta $name + return + } + "or" -> { + // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT + val shortcutLabel = asmgen.makeLabel("shortcut") + asmgen.out(" lda $name | bne $shortcutLabel") + asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes) + asmgen.out(""" + ora $name + sta $name $shortcutLabel:""") - return + return + } } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 6184d902e..3330ea51f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -247,8 +247,8 @@ class IRCodeGen( val result = mutableListOf() val goto = branch.trueScope.children.firstOrNull() as? PtJump - if(goto is PtJump && branch.falseScope.children.isEmpty()) { - // special case the form: if_cc goto + if (goto is PtJump) { + // special case the form: if_cc goto (with optional else) val address = goto.address?.toInt() if(address!=null) { val branchIns = when(branch.condition) { @@ -276,10 +276,11 @@ class IRCodeGen( } addInstr(result, branchIns, null) } + if(branch.falseScope.children.isNotEmpty()) + result += translateNode(branch.falseScope) return result } - val elseLabel = createLabelName() // note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part val branchIns = when(branch.condition) { @@ -1093,7 +1094,7 @@ class IRCodeGen( else -> throw AssemblyError("weird operator") } - if (ifElse.elseScope.children.isNotEmpty()) { + if (ifElse.hasElse()) { // if and else parts val elseLabel = createLabelName() val afterIfLabel = createLabelName() @@ -1126,12 +1127,11 @@ class IRCodeGen( private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List { val result = mutableListOf() - val hasElse = ifElse.elseScope.children.isNotEmpty() fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) { val tr = expressionEval.translateExpression(condition) result += tr.chunks - if(hasElse) { + if(ifElse.hasElse()) { val elseLabel = createLabelName() val afterIfLabel = createLabelName() addInstr(result, IRInstruction(jumpFalseOpcode, labelSymbol = elseLabel), null) @@ -1178,7 +1178,7 @@ class IRCodeGen( else -> throw AssemblyError("invalid comparison operator") } - if (hasElse) { + if (ifElse.hasElse()) { // if and else parts val elseLabel = createLabelName() val afterIfLabel = createLabelName() @@ -1252,7 +1252,7 @@ class IRCodeGen( else -> throw AssemblyError("invalid comparison operator") } - if (hasElse) { + if (ifElse.hasElse()) { // if and else parts val elseLabel = createLabelName() val afterIfLabel = createLabelName() diff --git a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt index 14aee4d1c..9fcea56ff 100644 --- a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt @@ -112,10 +112,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr if (!leftIDt.isKnown || !rightIDt.isKnown) throw FatalAstException("can't determine datatype of both expression operands $expr") - // ConstValue X --> X ConstValue - if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr)) - return listOf(IAstModification.SwapOperands(expr)) - // NonBinaryExpression BinaryExpression --> BinaryExpression NonBinaryExpression if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) { if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr)) diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index 955c44005..8523342bf 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -103,25 +103,40 @@ class StatementOptimizer(private val program: Program, } } - // remove obvious dangling elses (else after a return) - if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) { - val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) - return listOf( - IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse), - IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) - ) - } + if(ifElse.elsepart.isNotEmpty()) { + // remove obvious dangling elses (else after a return) + if(ifElse.truepart.statements.singleOrNull() is Return) { + val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) + return listOf( + IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse), + IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) + ) + } - // switch if/else around if the else is just a jump or branch - if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) { - val jump = ifElse.elsepart.statements[0] - if(jump is Jump) { - val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position) + // switch if/else around if the else is just a jump or branch + if(ifElse.elsepart.statements.size==1) { + val jump = ifElse.elsepart.statements[0] + if(jump is Jump) { + val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position) + val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position) + return listOf( + IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse), + IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse), + IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse) + ) + } + } + + // switch if/else around if the condition is a not + val prefixCond = ifElse.condition as? PrefixExpression + if(prefixCond?.operator=="not") { + errors.info("invert conditon and swap if/else blocks", ifElse.condition.position) + val newTruePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position) return listOf( IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse), IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse), - IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse) + IAstModification.ReplaceNode(ifElse.condition, prefixCond.expression, ifElse) ) } } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt index 042fc2646..ce29456a7 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt @@ -103,7 +103,9 @@ internal class BeforeAsmTypecastCleaner(val program: Program, val arg2 = bfcs.args[1] val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED) val dt2 = arg2.inferType(program).getOr(DataType.UNDEFINED) - if(dt1 in ByteDatatypes) { + if(dt1==DataType.BOOL && dt2==DataType.BOOL) + return noModifications + else if(dt1 in ByteDatatypes) { if(dt2 in ByteDatatypes) return noModifications val (replaced, cast) = arg1.typecastTo(if(dt1== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt1, true) diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 7fb358f0f..1d61ffce5 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -1,9 +1,7 @@ package prog8.compiler.astprocessing import prog8.ast.* -import prog8.ast.expressions.ArrayLiteral -import prog8.ast.expressions.BinaryExpression -import prog8.ast.expressions.IdentifierReference +import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification @@ -173,15 +171,18 @@ internal class StatementReorderer( } override fun after(expr: BinaryExpression, parent: Node): Iterable { - // ConstValue X --> X ConstValue + // simplething X -> X simplething // (this should be done by the ExpressionSimplifier when optimizing is enabled, // but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.) - if (expr.left.constValue(program) != null - && expr.operator in AssociativeOperators - && expr.right.constValue(program) == null - && maySwapOperandOrder(expr)) - return listOf(IAstModification.SwapOperands(expr)) - + if(expr.operator in AssociativeOperators) { + if(expr.left is IdentifierReference || expr.left is NumericLiteral || expr.left is DirectMemoryRead || (expr.left as? ArrayIndexedExpression)?.indexer?.constIndex()!=null) { + if(expr.right !is IdentifierReference && expr.right !is NumericLiteral && expr.right !is DirectMemoryRead) { + if(maySwapOperandOrder(expr)) { + return listOf(IAstModification.SwapOperands(expr)) + } + } + } + } return noModifications } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 6ffbecbd8..22a71f7e8 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,27 +4,41 @@ TODO ===== ====== ======= VM 6502 what ===== ====== ======= -ok . boolean const -ok . boolean variables value, boolean subroutine param -ok . static bool var (block scope) with initializer value (staticVariable2asm) -ok . boolean arrays value, list and single value -ok . return boolean value from sub -ok . logical not, and, or, xor work correctly, also inplace -ok . make sure that and,or,xor,not aren't getting replaced by the bitwise versions in the Ast -ok . and, or, xor, not should work in expressions: print_ub((bb and true) as ubyte) -ok . bitwise logical ops on bools give type error, including invert -ok . arithmetic ops on bools give type error -ok . logical ops on ints give type error -ok . boolean values in ubyte array should give type error -ok . type error for bool[3] derp = 99 and also for init value [1,0,1] and also for [true, false, 1, 0, 222] -ok . while booleanvar==42 and do..until booleanvar==42 should give type error -ok . while not should give type error -ok . while not should give type error -ok . while not cx16.mouse_pos() should give condition type error -ok . while boolean should produce identical code as while integer!=0 +ok ok boolean const +ok ok boolean variables value, boolean subroutine param +ok ok static bool var (block scope) with initializer value (staticVariable2asm) +ok ok boolean arrays value, list and single value +ok ok return boolean value from sub +ok ok logical not, and, or, xor work correctly, also inplace +ok ok make sure that and,or,xor,not aren't getting replaced by the bitwise versions in the Ast +ok ok and, or, xor, not should work in expressions: print_ub((bb and true) as ubyte) +ok ok swap operands around on evaluating staticboolvar xor ba[0] +ok ok bitwise logical ops on bools give type error, including invert +ok ok arithmetic ops on bools give type error +ok ok logical ops on ints give type error +ok ok boolean values in ubyte array should give type error +ok ok type error for bool[3] derp = 99 and also for init value [1,0,1] and also for [true, false, 1, 0, 222] +ok ok while booleanvar==42 and do..until booleanvar==42 should give type error +ok ok while not should give type error +ok ok while not should give type error +ok ok while not cx16.mouse_pos() should give condition type error +ok ok efficient code for manipulating bools in an array (normal and agumented assigns) +ok WIP efficient code for if with only a goto in it +ok . efficient code for if byte comparisons against 0 (== and !=) +ok . efficient code for if word comparisons against 0 (== and !=) +ok . efficient code for if float comparisons against 0 (== and !=) +ok . efficient code for if byte comparisons against a value +ok . efficient code for if word comparisons against a value +ok . efficient code for if float comparisons against a value +ok . efficient code for assignment byte comparisons against 0 (== and !=) +ok . efficient code for assignment word comparisons against 0 (== and !=) +ok . efficient code for assignment float comparisons against 0 (== and !=) +ok . efficient code for assignment byte comparisons against a value +ok . efficient code for assignment word comparisons against a value +ok . efficient code for assignment float comparisons against a value +ok ok efficient code for if_cc conditional expressions +ok FAIL while boolean should produce identical code as while integer!=0 and code should be efficient ok . while not boolvar -> can we get rid of the cmp? (6502 only?) -ok . if someint==0 / ==1 should stil produce good asm same as what it used to be with if not someint/if someint -ok . efficient code for manipulating bools in an array (normal and agumented assigns) ok . testmonogfx works ok . check program sizes vs. master branch ===== ====== ======= diff --git a/examples/test.p8 b/examples/test.p8 index 6ccf3a580..b992921ad 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,4 +1,5 @@ %import textio +%import floats %zeropage basicsafe %option no_sysinit @@ -7,22 +8,29 @@ main { bool @shared staticbool2 sub start() { - boolean_const_and_var(true) - staticbool1 = boolean_arrays_and_return() - txt.print_ub(staticbool1 as ubyte) - txt.nl() - and_or_xor_not() +; boolean_const_and_var(true) +; staticbool1 = boolean_arrays_and_return() +; txt.print_ub(staticbool1 as ubyte) +; txt.nl() +; and_or_xor_not() +; logical_operand_swap() ; bitwise_on_bools_errors() ; arith_on_bools_errors() ; logical_on_ints_errors() ; bools_in_intarray_errors() ; ints_in_boolarray_errors() ; while_until_int_errors() - bools_in_array_assigns() - bools_in_array_assigns_inplace() - if_code() +; bools_in_array_assigns() +; bools_in_array_assigns_inplace() +; while_bool_efficient() +; efficient_compare_0() +; efficient_compare_99() +; efficient_assign_cmp_0() +; efficient_assign_cmp_99() + if_gotos() +; if_code() ;;sys.exit(1) - while_equiv() +; while_equiv() ; bool[3] barr ; bool @shared bb @@ -42,6 +50,108 @@ main { ; bb = not bb } + sub while_bool_efficient() { + while staticbool1 { + cx16.r0++ + } + while not staticbool1 { + cx16.r0++ + } + while cx16.r0L==0 { + cx16.r0++ + } + while cx16.r0L!=0 { + cx16.r0++ + } + } + + + sub efficient_assign_cmp_0() { + ubyte @shared ub + uword @shared uw + float @shared fl + bool @shared bb1, bb2 + + bb1 = ub==0 + bb2 = ub!=0 + bb1 = uw==0 + bb2 = uw!=0 + bb1 = fl==0.0 + bb2 = fl!=0.0 + } + + + sub efficient_assign_cmp_99() { + ubyte @shared ub + uword @shared uw + float @shared fl + bool @shared bb1, bb2 + + bb1 = ub==99 + bb2 = ub!=99 + bb1 = uw==99 + bb2 = uw!=99 + bb1 = fl==99.0 + bb2 = fl!=99.0 + } + + sub efficient_compare_0() { + ubyte @shared ub + uword @shared uw + float @shared fl + + if ub==0 + cx16.r0++ + if uw==0 + cx16.r0++ + if fl==0 + cx16.r0++ + if ub!=0 + cx16.r0++ + else + cx16.r1++ + if uw!=0 + cx16.r0++ + else + cx16.r1++ + if fl!=0 + cx16.r0++ + else + cx16.r1++ + } + + sub efficient_compare_99() { + ubyte @shared ub + uword @shared uw + float @shared fl + + if ub==99 + cx16.r0++ + if uw==99 + cx16.r0++ + if fl==99.99 + cx16.r0++ + if ub!=99 + cx16.r0++ + else + cx16.r1++ + if uw!=99 + cx16.r0++ + else + cx16.r1++ + if fl!=99.99 + cx16.r0++ + else + cx16.r1++ + } + + sub logical_operand_swap() { + bool[] ba = [true, false, true] + bool @shared bb = staticbool1 xor ba[0] + ubyte @shared zz + cx16.r0L = 99+zz + } + sub boolean_const_and_var(bool barg) { const bool bconst1 = true const bool bconst2 = false @@ -212,6 +322,20 @@ main { ba[2] = ba[0] and ba[1] ba[1] = ba[0] or ba[2] ba[1] = not ba[2] + ba[1] = ba[0] xor ba[cx16.r0L] + ba[2] = ba[0] and ba[cx16.r0L] + ba[1] = ba[0] or ba[cx16.r0L] + ba[1] = not ba[cx16.r0L] + + ubyte[] uba = [11,22,33] + uba[1] = uba[0] ^ uba[2] + uba[2] = uba[0] & uba[1] + uba[1] = uba[0] | uba[2] + uba[1] = ~uba[2] + uba[1] = uba[0] ^ uba[cx16.r0L] + uba[2] = uba[0] & uba[cx16.r0L] + uba[1] = uba[0] | uba[cx16.r0L] + uba[1] = ~uba[cx16.r0L] } sub bools_in_array_assigns_inplace() { @@ -228,6 +352,55 @@ main { ba[1] = ba[1] and ba[2] ba[2] = ba[2] or ba[1] ba[2] = not ba[2] + ba[2] = ba[2] xor ba[cx16.r0L] + ba[1] = ba[1] and ba[cx16.r0L] + ba[2] = ba[2] or ba[cx16.r0L] + + ubyte[] uba = [11,22,33] + uba[2] = uba[2] ^ uba[1] + uba[1] = uba[1] & uba[2] + uba[2] = uba[2] | uba[1] + uba[2] = ~ uba[2] + uba[2] = uba[2] ^ uba[cx16.r0L] + uba[1] = uba[1] & uba[cx16.r0L] + uba[2] = uba[2] | uba[cx16.r0L] + } + + sub if_gotos() { + ubyte @shared ub + + if_cc + goto label + if_cc + goto label + else + cx16.r0++ + if_cs + goto label + else + cx16.r0++ + + + 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++ + label: } sub if_code() {