more bool fixes and optimizations in codegen

This commit is contained in:
Irmen de Jong 2024-02-13 21:56:22 +01:00
parent c5c4c6f111
commit 7d8cdcbfea
15 changed files with 589 additions and 889 deletions

View File

@ -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()
}

View File

@ -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<String, Boolean> {
internal fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
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<leftConstVal)
jmp(jumpIfFalseLabel)
return
} else {
if (left is PtIdentifier) {
if(rightConstVal.number.toInt()!=0) {
assignExpressionToRegister(left, RegisterOrPair.AY)
code("#>${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<leftConstVal)
jmp(jumpIfFalseLabel)
return
} else {
if (left is PtIdentifier) {
return if(rightConstVal.number.toInt()!=0) {
assignExpressionToRegister(left, RegisterOrPair.AY)
code("#>${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) {

View File

@ -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)

View File

@ -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()
}
}
}
}

View File

@ -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 ?")

View File

@ -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 {

View File

@ -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 -> {

View File

@ -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
}
}
}

View File

@ -247,8 +247,8 @@ class IRCodeGen(
val result = mutableListOf<IRCodeChunkBase>()
val goto = branch.trueScope.children.firstOrNull() as? PtJump
if(goto is PtJump && branch.falseScope.children.isEmpty()) {
// special case the form: if_cc <condition> goto <place>
if (goto is PtJump) {
// special case the form: if_cc goto <place> (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<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
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()

View File

@ -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 <associativeoperator> X --> X <associativeoperator> ConstValue
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> 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))

View File

@ -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)
)
}
}

View File

@ -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)

View File

@ -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<IAstModification> {
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
// simplething <associative> X -> X <associative> 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
}

View File

@ -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 <integervar> should give type error
ok . while not <integer functioncall> 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 <integervar> should give type error
ok ok while not <integer functioncall> 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
===== ====== =======

View File

@ -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() {