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 get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup val elseScope: PtNodeGroup
get() = children[2] as 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 anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator) private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val ifElseAsmgen = IfElseAsmGen(program, this)
fun compileToAssembly(): IAssemblyProgram? { fun compileToAssembly(): IAssemblyProgram? {
@@ -594,7 +595,7 @@ class AsmGen6502Internal (
} }
is PtLabel -> translate(stmt) is PtLabel -> translate(stmt)
is PtConditionalBranch -> translate(stmt) is PtConditionalBranch -> translate(stmt)
is PtIfElse -> translate(stmt) is PtIfElse -> ifElseAsmgen.translate(stmt)
is PtForLoop -> forloopsAsmGen.translate(stmt) is PtForLoop -> forloopsAsmGen.translate(stmt)
is PtRepeatLoop -> translate(stmt) is PtRepeatLoop -> translate(stmt)
is PtWhen -> 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) { private fun translate(stmt: PtRepeatLoop) {
val endLabel = makeLabel("repeatend") val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
@@ -880,7 +861,6 @@ $repeatLabel""")
out(endLabel) out(endLabel)
} }
private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) { private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) {
require(count in 2..256) { "invalid repeat count ${stmt.position}" } require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") 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 ident = jump.identifier
val addr = jump.address val addr = jump.address
return when { 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) { internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
fun assignViaExprEval() { 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) { internal fun popCpuStack(asmsub: PtAsmSub, parameter: PtSubroutineParameter, reg: RegisterOrStatusflag) {
val shouldKeepA = asmsub.parameters.any { it.first.registerOrPair==RegisterOrPair.AX || it.first.registerOrPair==RegisterOrPair.AY} val shouldKeepA = asmsub.parameters.any { it.first.registerOrPair==RegisterOrPair.AX || it.first.registerOrPair==RegisterOrPair.AY}
if(reg.statusflag!=null) { 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) { else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) { if(optimizeIntArgsViaRegisters(sub)) {
if(sub.parameters.size==1) { 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) argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
} else { } else {
// 2 byte params, second in Y, first in A // 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 { private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType) if(argType isAssignableTo paramType)
return true return true
if(argType==DataType.BOOL && paramType==DataType.BOOL)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes) if(argType in ByteDatatypes && paramType in ByteDatatypes)
return true return true
if(argType in WordDatatypes && paramType in WordDatatypes) 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) { for(num in 1..count) {
val name = asmgen.buildTempVarName(dt, num) val name = asmgen.buildTempVarName(dt, num)
when (dt) { when (dt) {
DataType.BOOL -> asmgen.out("$name .byte ?")
DataType.BYTE -> asmgen.out("$name .char ?") DataType.BYTE -> asmgen.out("$name .char ?")
DataType.UBYTE -> asmgen.out("$name .byte ?") DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.WORD -> asmgen.out("$name .sint ?") DataType.WORD -> asmgen.out("$name .sint ?")

View File

@@ -44,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
init { init {
if(register!=null && datatype !in NumericDatatypes) if(register!=null && datatype !in NumericDatatypesWithBoolean)
throw AssemblyError("register must be integer or float type") throw AssemblyError("must be numeric type")
} }
companion object { companion object {

View File

@@ -340,7 +340,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} else { } else {
// use a temporary variable // 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) assignExpressionToVariable(value.value, tempvar, value.type)
when (value.operator) { when (value.operator) {
"+" -> {} "+" -> {}
@@ -394,16 +394,15 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true) assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true)
assignFAC1float(assign.target) assignFAC1float(assign.target)
} else { } else {
// array[x] = -value ... use a tempvar then store that back into the array. val register = if(assign.source.datatype in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
val tempvar = asmgen.getTempVarName(assign.target.datatype) val assignToRegister = AsmAssignment(assign.source,
val assignToTempvar = AsmAssignment(assign.source, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position, register = register, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position) asmgen.translateNormalAssignment(assignToRegister, scope)
asmgen.translateNormalAssignment(assignToTempvar, scope) val signed = assign.target.datatype in SignedDatatypes
when(assign.target.datatype) { when(assign.target.datatype) {
in ByteDatatypesWithBoolean -> assignVariableByte(assign.target, tempvar) in ByteDatatypesWithBoolean -> assignRegisterByte(assign.target, CpuRegister.A, signed, false)
in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype) in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
else -> throw AssemblyError("weird dt") else -> throw AssemblyError("weird dt")
} }
} }
@@ -1149,6 +1148,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true 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) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
if(directIntoY(expr.right)) { if(directIntoY(expr.right)) {
@@ -1162,7 +1177,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when (expr.operator) { when (expr.operator) {
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1") "&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|" -> asmgen.out(" ora 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") else -> throw AssemblyError("invalid bitwise operator")
} }
assignRegisterByte(target, CpuRegister.A, false, true) assignRegisterByte(target, CpuRegister.A, false, true)
@@ -1183,7 +1198,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
when (expr.operator) { when (expr.operator) {
"&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa") "&" -> 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") "|" -> 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") else -> throw AssemblyError("invalid bitwise operator")
} }
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
@@ -1193,61 +1208,103 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
private fun optimizedLogicalExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean { 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") { fun swapOperands(): Boolean =
// shortcircuit evaluation into A if(expr.right is PtIdentifier || expr.right is PtMemoryByte)
val shortcutLabel = asmgen.makeLabel("shortcut") false
when (expr.operator) { else
"and" -> { expr.left is PtIdentifier || expr.left is PtMemoryByte
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) fun assignResultIntoA(left: PtExpression, operator: String, right: PtExpression) {
asmgen.out(" beq $shortcutLabel") // non short-circuit evaluation it is *likely* shorter and faster because of the simple operands.
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
asmgen.out(shortcutLabel) fun assignViaScratch() {
} if(directIntoY(right)) {
"or" -> { assignExpressionToRegister(right, RegisterOrPair.Y, false)
// 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)
asmgen.out(" sty P8ZP_SCRATCH_B1") asmgen.out(" sty P8ZP_SCRATCH_B1")
} else { } else {
asmgen.out(" pha") asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE) assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla") asmgen.out(" pla")
} }
when (expr.operator) { when (operator) {
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1") "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid logical operator") 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) { 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) 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) { private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
assignExpressionToRegister(left, RegisterOrPair.AY, false) assignExpressionToRegister(left, RegisterOrPair.AY, false)
when(right) { when(right) {
@@ -1555,7 +1592,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
if(valueDt in ByteDatatypes) { if(valueDt in ByteDatatypesWithBoolean) {
when(target.register) { when(target.register) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
@@ -2763,7 +2800,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(indexVar!=null) { if(indexVar!=null) {
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y") asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
} else { } else {
require(target.array.index.type in ByteDatatypes) require(target.array.index.type in ByteDatatypesWithBoolean)
asmgen.saveRegisterStack(register, false) asmgen.saveRegisterStack(register, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false) asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
asmgen.out(" pla | sta ${target.asmVarname},y") 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") 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 -> { DataType.UWORD -> {

View File

@@ -63,9 +63,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}" fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}"
if(value.kind==SourceStorageKind.LITERALBOOLEAN)
TODO("inplace modification literalboolean")
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
when (target.datatype) { when (target.datatype) {
@@ -240,7 +237,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
// normal array // normal array
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}" val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) { when (target.datatype) {
in ByteDatatypes -> { in ByteDatatypesWithBoolean -> {
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt()) SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt()) SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt())
@@ -309,7 +306,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return return
when (target.datatype) { when (target.datatype) {
in ByteDatatypes -> { in ByteDatatypesWithBoolean -> {
if(value.kind==SourceStorageKind.EXPRESSION if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast && value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) && 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) { private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
val shortcutLabel = asmgen.makeLabel("shortcut") if(!value.isSimple()) {
when (operator) { // attempt short-circuit (McCarthy) evaluation
"and" -> { when (operator) {
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) "and" -> {
asmgen.out(" lda $name | beq $shortcutLabel") // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes) val shortcutLabel = asmgen.makeLabel("shortcut")
asmgen.out(""" asmgen.out(" lda $name | beq $shortcutLabel")
and $name asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
sta $name asmgen.out("""
and $name
sta $name
$shortcutLabel:""") $shortcutLabel:""")
return return
} }
"or" -> { "or" -> {
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
asmgen.out(" lda $name | bne $shortcutLabel") val shortcutLabel = asmgen.makeLabel("shortcut")
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes) asmgen.out(" lda $name | bne $shortcutLabel")
asmgen.out(""" asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
ora $name asmgen.out("""
sta $name ora $name
sta $name
$shortcutLabel:""") $shortcutLabel:""")
return return
}
} }
} }

View File

@@ -247,8 +247,8 @@ class IRCodeGen(
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val goto = branch.trueScope.children.firstOrNull() as? PtJump val goto = branch.trueScope.children.firstOrNull() as? PtJump
if(goto is PtJump && branch.falseScope.children.isEmpty()) { if (goto is PtJump) {
// special case the form: if_cc <condition> goto <place> // special case the form: if_cc goto <place> (with optional else)
val address = goto.address?.toInt() val address = goto.address?.toInt()
if(address!=null) { if(address!=null) {
val branchIns = when(branch.condition) { val branchIns = when(branch.condition) {
@@ -276,10 +276,11 @@ class IRCodeGen(
} }
addInstr(result, branchIns, null) addInstr(result, branchIns, null)
} }
if(branch.falseScope.children.isNotEmpty())
result += translateNode(branch.falseScope)
return result return result
} }
val elseLabel = createLabelName() 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 // 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) { val branchIns = when(branch.condition) {
@@ -1093,7 +1094,7 @@ class IRCodeGen(
else -> throw AssemblyError("weird operator") else -> throw AssemblyError("weird operator")
} }
if (ifElse.elseScope.children.isNotEmpty()) { if (ifElse.hasElse()) {
// if and else parts // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
@@ -1126,12 +1127,11 @@ class IRCodeGen(
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> { private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val hasElse = ifElse.elseScope.children.isNotEmpty()
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) { fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) {
val tr = expressionEval.translateExpression(condition) val tr = expressionEval.translateExpression(condition)
result += tr.chunks result += tr.chunks
if(hasElse) { if(ifElse.hasElse()) {
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(jumpFalseOpcode, labelSymbol = elseLabel), null) addInstr(result, IRInstruction(jumpFalseOpcode, labelSymbol = elseLabel), null)
@@ -1178,7 +1178,7 @@ class IRCodeGen(
else -> throw AssemblyError("invalid comparison operator") else -> throw AssemblyError("invalid comparison operator")
} }
if (hasElse) { if (ifElse.hasElse()) {
// if and else parts // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
@@ -1252,7 +1252,7 @@ class IRCodeGen(
else -> throw AssemblyError("invalid comparison operator") else -> throw AssemblyError("invalid comparison operator")
} }
if (hasElse) { if (ifElse.hasElse()) {
// if and else parts // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = 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) if (!leftIDt.isKnown || !rightIDt.isKnown)
throw FatalAstException("can't determine datatype of both expression operands $expr") 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 // NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) { if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr)) 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()) {
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) { // remove obvious dangling elses (else after a return)
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) if(ifElse.truepart.statements.singleOrNull() is Return) {
return listOf( val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse), return listOf(
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) 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 // switch if/else around if the else is just a jump or branch
if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) { if(ifElse.elsepart.statements.size==1) {
val jump = ifElse.elsepart.statements[0] val jump = ifElse.elsepart.statements[0]
if(jump is Jump) { if(jump is Jump) {
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position) 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) val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse), IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, 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 arg2 = bfcs.args[1]
val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED) val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED)
val dt2 = arg2.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) if(dt2 in ByteDatatypes)
return noModifications return noModifications
val (replaced, cast) = arg1.typecastTo(if(dt1== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt1, true) 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 package prog8.compiler.astprocessing
import prog8.ast.* import prog8.ast.*
import prog8.ast.expressions.ArrayLiteral import prog8.ast.expressions.*
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
@@ -173,15 +171,18 @@ internal class StatementReorderer(
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> { 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, // (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.) // 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 if(expr.operator in AssociativeOperators) {
&& expr.operator in AssociativeOperators if(expr.left is IdentifierReference || expr.left is NumericLiteral || expr.left is DirectMemoryRead || (expr.left as? ArrayIndexedExpression)?.indexer?.constIndex()!=null) {
&& expr.right.constValue(program) == null if(expr.right !is IdentifierReference && expr.right !is NumericLiteral && expr.right !is DirectMemoryRead) {
&& maySwapOperandOrder(expr)) if(maySwapOperandOrder(expr)) {
return listOf(IAstModification.SwapOperands(expr)) return listOf(IAstModification.SwapOperands(expr))
}
}
}
}
return noModifications return noModifications
} }

View File

@@ -4,27 +4,41 @@ TODO
===== ====== ======= ===== ====== =======
VM 6502 what VM 6502 what
===== ====== ======= ===== ====== =======
ok . boolean const ok ok boolean const
ok . boolean variables value, boolean subroutine param ok ok boolean variables value, boolean subroutine param
ok . static bool var (block scope) with initializer value (staticVariable2asm) ok ok static bool var (block scope) with initializer value (staticVariable2asm)
ok . boolean arrays value, list and single value ok ok boolean arrays value, list and single value
ok . return boolean value from sub ok ok return boolean value from sub
ok . logical not, and, or, xor work correctly, also inplace ok 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 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 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 ok swap operands around on evaluating staticboolvar xor ba[0]
ok . arithmetic ops on bools give type error ok ok bitwise logical ops on bools give type error, including invert
ok . logical ops on ints give type error ok ok arithmetic ops on bools give type error
ok . boolean values in ubyte array should give type error ok ok logical ops on ints 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 ok boolean values in ubyte array should give type error
ok . while booleanvar==42 and do..until booleanvar==42 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 . while not <integervar> should give type error ok ok while booleanvar==42 and do..until booleanvar==42 should give type error
ok . while not <integer functioncall> should give type error ok ok while not <integervar> should give type error
ok . while not cx16.mouse_pos() should give condition type error ok ok while not <integer functioncall> should give type error
ok . while boolean should produce identical code as while integer!=0 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 . 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 . testmonogfx works
ok . check program sizes vs. master branch ok . check program sizes vs. master branch
===== ====== ======= ===== ====== =======

View File

@@ -1,4 +1,5 @@
%import textio %import textio
%import floats
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit %option no_sysinit
@@ -7,22 +8,29 @@ main {
bool @shared staticbool2 bool @shared staticbool2
sub start() { sub start() {
boolean_const_and_var(true) ; boolean_const_and_var(true)
staticbool1 = boolean_arrays_and_return() ; staticbool1 = boolean_arrays_and_return()
txt.print_ub(staticbool1 as ubyte) ; txt.print_ub(staticbool1 as ubyte)
txt.nl() ; txt.nl()
and_or_xor_not() ; and_or_xor_not()
; logical_operand_swap()
; bitwise_on_bools_errors() ; bitwise_on_bools_errors()
; arith_on_bools_errors() ; arith_on_bools_errors()
; logical_on_ints_errors() ; logical_on_ints_errors()
; bools_in_intarray_errors() ; bools_in_intarray_errors()
; ints_in_boolarray_errors() ; ints_in_boolarray_errors()
; while_until_int_errors() ; while_until_int_errors()
bools_in_array_assigns() ; bools_in_array_assigns()
bools_in_array_assigns_inplace() ; bools_in_array_assigns_inplace()
if_code() ; while_bool_efficient()
; efficient_compare_0()
; efficient_compare_99()
; efficient_assign_cmp_0()
; efficient_assign_cmp_99()
if_gotos()
; if_code()
;;sys.exit(1) ;;sys.exit(1)
while_equiv() ; while_equiv()
; bool[3] barr ; bool[3] barr
; bool @shared bb ; bool @shared bb
@@ -42,6 +50,108 @@ main {
; bb = not bb ; 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) { sub boolean_const_and_var(bool barg) {
const bool bconst1 = true const bool bconst1 = true
const bool bconst2 = false const bool bconst2 = false
@@ -212,6 +322,20 @@ main {
ba[2] = ba[0] and ba[1] ba[2] = ba[0] and ba[1]
ba[1] = ba[0] or ba[2] ba[1] = ba[0] or ba[2]
ba[1] = not 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() { sub bools_in_array_assigns_inplace() {
@@ -228,6 +352,55 @@ main {
ba[1] = ba[1] and ba[2] ba[1] = ba[1] and ba[2]
ba[2] = ba[2] or ba[1] ba[2] = ba[2] or ba[1]
ba[2] = not ba[2] 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() { sub if_code() {