Merge branch 'master' into next_compositetypes

# Conflicts:
#	codeCore/src/prog8/code/optimize/Optimizer.kt
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
This commit is contained in:
Irmen de Jong 2024-10-23 21:17:22 +02:00
commit 6bc1fc4464
9 changed files with 295 additions and 82 deletions

View File

@ -78,38 +78,65 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
if(options.compTarget.machine.cpu == CpuType.VIRTUAL)
return 0 // the special bittest optimization is not yet valid for the IR
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
require(bitmask==128 || bitmask==64)
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.forDt(BaseDataType.BOOL), condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(BaseDataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(BaseDataType.UBYTE, 6.0, and.right.position))
return bittestCall
}
fun isAndByteCondition(condition: PtBinaryExpression?): Triple<PtBinaryExpression, PtIdentifier, Int>? {
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
val variable = and.left as? PtIdentifier
val bitmask = and.right.asConstInteger()
if(variable!=null && variable.type.isByte && (bitmask==128 || bitmask==64)) {
return Triple(and, variable, bitmask)
}
}
}
}
return null
}
var changes = 0
var recurse = true
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtIfElse) {
val condition = node.condition as? PtBinaryExpression
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
if(condition.right.asConstInteger()==0) {
val and = condition.left as? PtBinaryExpression
if(and != null && and.operator=="&" && and.type.isUnsignedByte) {
val variable = and.left as? PtIdentifier
val bitmask = and.right.asConstInteger()
if(variable!=null && variable.type.isByte && (bitmask==128 || bitmask==64)) {
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val index = node.parent.children.indexOf(node)
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.forDt(BaseDataType.BOOL), node.condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(BaseDataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(BaseDataType.UBYTE, 6.0, and.right.position))
val ifElse = PtIfElse(node.position)
ifElse.add(bittestCall)
ifElse.add(node.ifScope)
if(node.hasElse())
ifElse.add(node.elseScope)
node.parent.children[index] = ifElse
ifElse.parent = node.parent
changes++
recurse = false
}
}
}
val check = isAndByteCondition(condition)
if(check!=null) {
val (and, variable, bitmask) = check
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
val ifElse = PtIfElse(node.position)
ifElse.add(bittestCall)
ifElse.add(node.ifScope)
if (node.hasElse())
ifElse.add(node.elseScope)
val index = node.parent.children.indexOf(node)
node.parent.children[index] = ifElse
ifElse.parent = node.parent
changes++
recurse = false
}
}
if (node is PtIfExpression) {
val condition = node.condition as? PtBinaryExpression
val check = isAndByteCondition(condition)
if(check!=null) {
val (and, variable, bitmask) = check
val bittestCall = makeBittestCall(condition!!, and, variable, bitmask)
node.children[0] = bittestCall
bittestCall.parent = node
changes++
recurse = false
}
}
recurse

View File

@ -1377,6 +1377,10 @@ $repeatLabel""")
pla
jsr floats.copy_float""")
}
internal fun assignIfExpression(target: AsmAssignTarget, value: PtIfExpression) {
ifElseAsmgen.assignIfExpression(target, value)
}
}
/**

View File

@ -55,6 +55,86 @@ internal class IfElseAsmGen(private val program: PtProgram,
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
}
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
// this is NOT for the if-else STATEMENT, but this is code for the IF-EXPRESSION.
require(target.datatype==expr.type)
val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end")
evalConditonAndBranchWhenFalse(expr.condition, falseLabel)
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isFloat -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
}
}
private fun evalConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
if (condition is PtBinaryExpression) {
val rightDt = condition.right.type
return when {
rightDt.isByteOrBool -> translateIfByteConditionBranch(condition, falseLabel)
rightDt.isWord -> translateIfWordConditionBranch(condition, falseLabel)
rightDt.isFloat -> translateFloatConditionBranch(condition, falseLabel)
else -> throw AssemblyError("weird dt")
}
}
else if(condition is PtPrefix && condition.operator=="not") {
assignConditionValueToRegisterAndTest(condition.value)
asmgen.out(" bne $falseLabel")
} else {
// 'simple' condition, check if it is a byte bittest
val bittest = condition as? PtBuiltinFunctionCall
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
val variable = bittest.args[0] as PtIdentifier
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
val testForBitSet = bittest.name.endsWith("_set")
when (bitnumber) {
7 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bpl $falseLabel")
else asmgen.out(" bmi $falseLabel")
return
}
6 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bvc $falseLabel")
else asmgen.out(" bvs $falseLabel")
return
}
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
}
}
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
private fun checkNotRomsubReturnsStatusReg(condition: PtExpression) {
val fcall = condition as? PtFunctionCall
if(fcall!=null && fcall.type.isBool) {
@ -74,6 +154,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
is PtIrRegister,
is PtArrayIndexer,
is PtPrefix,
is PtIfExpression,
is PtBinaryExpression -> { /* no cmp necessary the lda has been done just prior */ }
is PtTypeCast -> {
if(!condition.value.type.isByte && !condition.value.type.isWord)
@ -277,6 +358,103 @@ internal class IfElseAsmGen(private val program: PtProgram,
}
}
private fun translateIfByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger()
if(constValue==0) {
return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel)
}
when(condition.operator) {
"==" -> {
// if X==value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
cmpAwithByteValue(condition.right, false)
asmgen.out(" bne $falseLabel")
}
"!=" -> {
// if X!=value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
cmpAwithByteValue(condition.right, false)
asmgen.out(" beq $falseLabel")
}
in LogicalOperators -> {
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
asmgen.out(" beq $falseLabel")
} else {
errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-)
assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
else -> {
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
// TODO: special cases for <, <=, >, >= above.
assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
}
private fun translateIfWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger()
if(constValue==0) {
// TODO reuse more code from regular if statements. Need a shared routine like isWordExprZero() ?
when(condition.operator) {
"==" -> {
// if w==0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.AY, signed)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
return
}
"!=" -> {
// if w!=0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.AY, signed)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
return
}
}
}
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
// optimized code for byte comparisons with 0
assignConditionValueToRegisterAndTest(condition.left)
when (condition.operator) {
"==" -> asmgen.out(" bne $falseLabel")
"!=" -> asmgen.out(" beq $falseLabel")
">" -> {
if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel")
else asmgen.out(" beq $falseLabel")
}
">=" -> {
if(signed) asmgen.out(" bmi $falseLabel")
else { /* always true for unsigned */ }
}
"<" -> {
if(signed) asmgen.out(" bpl $falseLabel")
else asmgen.jmp(falseLabel)
}
"<=" -> {
if(signed) {
// inverted '>'
asmgen.out("""
beq +
bpl $falseLabel
+""")
} else asmgen.out(" bne $falseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
private fun translateIfCompareWithZeroByte(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
// optimized code for byte comparisons with 0
val condition = stmt.condition as PtBinaryExpression
@ -1742,6 +1920,51 @@ _jump jmp ($asmLabel)
}
}
private fun translateFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
val constValue = (condition.right as? PtNumber)?.number
if(constValue==0.0) {
if (condition.operator == "==") {
// if FL==0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel")
return
} else if(condition.operator=="!=") {
// if FL!=0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel")
return
}
}
when(condition.operator) {
"==" -> {
translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" beq $elseLabel")
}
"!=" -> {
translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" bne $elseLabel")
}
"<" -> {
translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" beq $elseLabel")
}
"<=" -> {
translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" beq $elseLabel")
}
">" -> {
translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" bne $elseLabel")
}
">=" -> {
translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" bne $elseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
private fun translateFloatsEqualsConditionIntoA(left: PtExpression, right: PtExpression) {
fun equalf(leftName: String, rightName: String) {
asmgen.out("""

View File

@ -644,47 +644,11 @@ internal class AssignmentAsmGen(
throw AssemblyError("Expression is too complex to translate into assembly. Split it up into several separate statements, introduce a temporary variable, or otherwise rewrite it. Location: $pos")
}
}
is PtIfExpression -> assignIfExpression(assign.target, value)
is PtIfExpression -> asmgen.assignIfExpression(assign.target, value)
else -> throw AssemblyError("weird assignment value type $value")
}
}
private fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
require(target.datatype==expr.type)
val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end")
assignExpressionToRegister(expr.condition, RegisterOrPair.A, false)
asmgen.out(" beq $falseLabel")
when {
expr.type.isByteOrBool -> {
assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
asmgen.out(endLabel)
assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord -> {
assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
asmgen.out(endLabel)
assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isFloat -> {
assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
}
}
private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment, scope: IPtSubroutine?) {
require(assign.source.expression is PtPrefix)
if(assign.source.datatype.isFloat) {

View File

@ -40,8 +40,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"ror" -> funcRolRor(call)
"rol2" -> funcRolRor(call)
"ror2" -> funcRolRor(call)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse/ifExpression statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse/ifExpression statement")
"prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)

View File

@ -101,6 +101,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val endLabel = codeGen.createLabelName()
addToResult(result, condTr, condTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=condTr.resultReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = falseLabel), null)
if (irDt != IRDataType.FLOAT) {

View File

@ -122,6 +122,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
is PtNumber,
is PtRange,
is PtString -> true
// note that unlike most other times, PtIdentifier IS "complex" this time (it's a variable that might change)
else -> false
}

View File

@ -1,9 +1,6 @@
TODO
====
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) (assignIfExpression / translate(ifExpr: PtIfExpression))
Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
@ -62,6 +59,7 @@ Libraries:
Optimizations:
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,

View File

@ -1,4 +1,3 @@
%import floats
%import textio
%option no_sysinit
%zeropage basicsafe
@ -6,19 +5,15 @@
main {
sub start() {
ubyte[4] values
uword[4] wvalues
float[4] fvalues
cx16.r0L = 0
cx16.r1L = 3
values[cx16.r0L+2] = if cx16.r1L>2 99 else 111
wvalues[cx16.r0L+2] = if cx16.r1L>2 9999 else 1111
fvalues[cx16.r0L+2] = if cx16.r1L>2 9.99 else 1.111
word @shared ww = 1234
txt.print_ub(values[2])
txt.print_ub(if ww==0 111 else 222)
txt.spc()
txt.print_ub(if ww!=0 111 else 222)
txt.spc()
txt.print_ub(if ww==1000 111 else 222)
txt.spc()
txt.print_ub(if ww!=1000 111 else 222)
txt.nl()
txt.print_uw(wvalues[2])
txt.nl()
floats.print(fvalues[2])
}
}