mirror of
https://github.com/irmen/prog8.git
synced 2025-08-14 22:27:48 +00:00
shortcutting part one
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
package prog8.code.core
|
package prog8.code.core
|
||||||
|
|
||||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
|
||||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||||
|
@@ -1041,39 +1041,60 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
private fun optimizedLogicalAndOrExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
private fun optimizedLogicalAndOrExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||||
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||||
if (expr.right.isSimple()) {
|
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
return true
|
||||||
return true
|
}
|
||||||
}
|
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
if(asmgen.options.shortCircuit && (!expr.left.isSimple() && !expr.right.isSimple())) {
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
// shortcircuit evaluation into A
|
||||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
when (expr.operator) {
|
||||||
when (expr.operator) {
|
"and" -> {
|
||||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
println("SHORTCUT AND ${expr.position}") // TODO weg
|
||||||
else -> throw AssemblyError("invalid logical operator")
|
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
|
||||||
|
println("SHORTCUT OR ${expr.position}") // TODO weg
|
||||||
|
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
|
||||||
|
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||||
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
|
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
|
when (expr.operator) {
|
||||||
|
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
|
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||||
|
else -> throw AssemblyError("invalid logical operator")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||||
if (expr.right.isSimple()) {
|
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
||||||
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
return true
|
||||||
return true
|
}
|
||||||
}
|
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
||||||
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||||
when (expr.operator) {
|
when (expr.operator) {
|
||||||
@@ -1775,11 +1796,15 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||||
|
// normal evaluation, not worth to shortcircuit the simple right operand
|
||||||
|
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||||
|
assignExpressionToRegister(right, RegisterOrPair.A, false)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"and" -> TODO("logical and (with optional shortcircuit) ${left.position}")
|
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
"or" -> TODO("logical or (with optional shortcircuit) ${left.position}")
|
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||||
else -> throw AssemblyError("invalid logical operator")
|
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) {
|
||||||
|
@@ -37,8 +37,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
"*=" -> inplaceModification(assign.target, "*", assign.source)
|
"*=" -> inplaceModification(assign.target, "*", assign.source)
|
||||||
"/=" -> inplaceModification(assign.target, "/", assign.source)
|
"/=" -> inplaceModification(assign.target, "/", assign.source)
|
||||||
"|=" -> inplaceModification(assign.target, "|", assign.source)
|
"|=" -> inplaceModification(assign.target, "|", assign.source)
|
||||||
|
"or=" -> inplaceModification(assign.target, "or", assign.source)
|
||||||
"&=" -> inplaceModification(assign.target, "&", assign.source)
|
"&=" -> inplaceModification(assign.target, "&", assign.source)
|
||||||
"^=" -> inplaceModification(assign.target, "^", assign.source)
|
"and=" -> inplaceModification(assign.target, "and", assign.source)
|
||||||
|
"^=", "xor=" -> inplaceModification(assign.target, "^", assign.source)
|
||||||
"<<=" -> inplaceModification(assign.target, "<<", assign.source)
|
"<<=" -> inplaceModification(assign.target, "<<", assign.source)
|
||||||
">>=" -> inplaceModification(assign.target, ">>", assign.source)
|
">>=" -> inplaceModification(assign.target, ">>", assign.source)
|
||||||
"%=" -> inplaceModification(assign.target, "%", assign.source)
|
"%=" -> inplaceModification(assign.target, "%", assign.source)
|
||||||
@@ -791,12 +793,42 @@ 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) {
|
||||||
|
if(asmgen.options.shortCircuit) {
|
||||||
|
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)
|
||||||
|
println("SHORTCUT AND ${value.position}") // TODO weg
|
||||||
|
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
|
||||||
|
println("SHORTCUT OR ${value.position}") // TODO weg
|
||||||
|
asmgen.out(" lda $name | bne $shortcutLabel")
|
||||||
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||||
|
asmgen.out("""
|
||||||
|
ora $name
|
||||||
|
sta $name
|
||||||
|
$shortcutLabel:""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal evaluation
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||||
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
|
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
|
||||||
asmgen.out(" sta $name")
|
asmgen.out(" sta $name")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplacemodificationByteVariableWithVariable(name: String, dt: DataType, operator: String, otherName: String) {
|
private fun inplacemodificationByteVariableWithVariable(name: String, dt: DataType, operator: String, otherName: String) {
|
||||||
|
// note: no logical and/or shortcut here, not worth it due to simple right operand
|
||||||
asmgen.out(" lda $name")
|
asmgen.out(" lda $name")
|
||||||
inplacemodificationRegisterAwithVariable(operator, otherName, dt in SignedDatatypes)
|
inplacemodificationRegisterAwithVariable(operator, otherName, dt in SignedDatatypes)
|
||||||
asmgen.out(" sta $name")
|
asmgen.out(" sta $name")
|
||||||
@@ -852,6 +884,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
"&" -> asmgen.out(" and $variable")
|
"&" -> asmgen.out(" and $variable")
|
||||||
"|" -> asmgen.out(" ora $variable")
|
"|" -> asmgen.out(" ora $variable")
|
||||||
|
"and", "or" -> throw AssemblyError("logical and/or should have been handled earlier because of shortcircuit handling")
|
||||||
"^" -> asmgen.out(" eor $variable")
|
"^" -> asmgen.out(" eor $variable")
|
||||||
"==" -> {
|
"==" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@@ -1130,6 +1163,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
|
|
||||||
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
|
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
|
||||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||||
|
// note: no logical and/or shortcut here, not worth it due to simple right operand
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name")
|
"+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name")
|
||||||
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
|
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
|
||||||
@@ -1192,9 +1226,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> immediateAndInplace(name, value)
|
"&", "and" -> immediateAndInplace(name, value)
|
||||||
"|" -> immediateOrInplace(name, value)
|
"|", "or" -> immediateOrInplace(name, value)
|
||||||
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||||
"==" -> {
|
"==" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
|
@@ -717,6 +717,7 @@ Logical expressions are expressions that calculate a boolean result: true or fal
|
|||||||
(which in reality are just a 1 or 0 integer value). When using variables of the type ``bool``,
|
(which in reality are just a 1 or 0 integer value). When using variables of the type ``bool``,
|
||||||
logical expressions will compile more efficiently than when you're using regular integer type operands
|
logical expressions will compile more efficiently than when you're using regular integer type operands
|
||||||
(because these have to be converted to 0 or 1 every time)
|
(because these have to be converted to 0 or 1 every time)
|
||||||
|
Prog8 applies short-circuit aka McCarthy evaluation for ``and`` and ``or`` on boolean expressions.
|
||||||
|
|
||||||
You can use parentheses to group parts of an expression to change the precedence.
|
You can use parentheses to group parts of an expression to change the precedence.
|
||||||
Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions
|
Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions
|
||||||
|
@@ -599,18 +599,13 @@ logical: ``not`` ``and`` ``or`` ``xor``
|
|||||||
about truths (boolean values). The result of such an expression is a 'boolean' value 'true' or 'false'
|
about truths (boolean values). The result of such an expression is a 'boolean' value 'true' or 'false'
|
||||||
(which in reality is just a byte value of 1 or 0).
|
(which in reality is just a byte value of 1 or 0).
|
||||||
Notice that the expression ``not x`` is equivalent to ``x==0``, and the compiler will treat it as such.
|
Notice that the expression ``not x`` is equivalent to ``x==0``, and the compiler will treat it as such.
|
||||||
|
Prog8 applies short-circuit aka McCarthy evaluation for ``and`` and ``or``.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
You can use regular integers directly in logical expressions but these have to be converted to
|
You can use regular integers directly in logical expressions but these have to be converted to
|
||||||
the boolean value 0 or 1 every time, resulting in larger and slower code. Consider using
|
the boolean value 0 or 1 every time, resulting in larger and slower code. Consider using
|
||||||
the ``bool`` variable type instead, where this conversion doesn't need to occur.
|
the ``bool`` variable type instead, where this conversion doesn't need to occur.
|
||||||
|
|
||||||
.. note::
|
|
||||||
Unlike most other programming languages, there is no short-circuit or McCarthy evaluation
|
|
||||||
for the logical ``and`` and ``or`` operators. This means that prog8 currently always evaluates
|
|
||||||
all operands from these logical expressions, even when one of them already determines the outcome!
|
|
||||||
If you don't want this to happen, you have to split and nest the if-statements yourself.
|
|
||||||
|
|
||||||
range creation: ``to``, ``downto``
|
range creation: ``to``, ``downto``
|
||||||
Creates a range of values from the LHS value to the RHS value, inclusive.
|
Creates a range of values from the LHS value to the RHS value, inclusive.
|
||||||
These are mainly used in for loops to set the loop range.
|
These are mainly used in for loops to set the loop range.
|
||||||
|
@@ -4,8 +4,9 @@ TODO
|
|||||||
|
|
||||||
- [on branch: shortcircuit] complete McCarthy evaluation. This may also reduce code size perhaps for things like if a>4 or a<2 ....
|
- [on branch: shortcircuit] complete McCarthy evaluation. This may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||||
- note: shortcircuit only on logical boolean expressions (and,or) not on bitwise (&,|)
|
- note: shortcircuit only on logical boolean expressions (and,or) not on bitwise (&,|)
|
||||||
- vm ircodegen (DONE!)
|
- vm ircodegen (PARTIALLY DONE!)
|
||||||
- in 6502 codegen (see vm's ExpressionGen operatorAnd / operatorOr)
|
- in 6502 codegen (see vm's ExpressionGen operatorAnd / operatorOr)
|
||||||
|
- test on word variables, if the TODO doesn't trigger, just replace it with a specific notification
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@@ -2,13 +2,22 @@
|
|||||||
%zeropage dontuse
|
%zeropage dontuse
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start () {
|
/*
|
||||||
ubyte a1 = 10
|
sub start() {
|
||||||
ubyte a2 = 20
|
if bool_true() and bool_false() and bool_true()
|
||||||
ubyte x1 = 30
|
txt.print("all true")
|
||||||
ubyte x2 = 40
|
else
|
||||||
ubyte zero = 0
|
txt.print("not all true")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
ubyte @shared a1 = 10
|
||||||
|
ubyte @shared a2 = 20
|
||||||
|
ubyte @shared x1 = 30
|
||||||
|
ubyte @shared x2 = 40
|
||||||
|
ubyte @shared zero = 0
|
||||||
|
|
||||||
|
sub start () {
|
||||||
txt.print("1a:\n")
|
txt.print("1a:\n")
|
||||||
if calc_a1()<calc_x1() and calc_a2()<=calc_x2()
|
if calc_a1()<calc_x1() and calc_a2()<=calc_x2()
|
||||||
txt.print("* 1a and ok\n")
|
txt.print("* 1a and ok\n")
|
||||||
@@ -56,30 +65,47 @@ main {
|
|||||||
txt.print("\n5d:\n")
|
txt.print("\n5d:\n")
|
||||||
result = bool_false() xor bool_true()
|
result = bool_false() xor bool_true()
|
||||||
|
|
||||||
|
txt.print("augmented and shortcut:\n")
|
||||||
|
bool @shared b1 = false
|
||||||
|
cx16.r0++
|
||||||
|
b1 = b1 and bool_true()
|
||||||
|
txt.print("augmented and no shortcut:\n")
|
||||||
|
b1 = true
|
||||||
|
cx16.r0++
|
||||||
|
b1 = b1 and bool_true()
|
||||||
|
|
||||||
sub bool_true() -> bool {
|
txt.print("augmented or shortcut:\n")
|
||||||
txt.print("bool_true\n")
|
b1 = true
|
||||||
return true
|
cx16.r0++
|
||||||
}
|
b1 = b1 or bool_true()
|
||||||
sub bool_false() -> bool {
|
txt.print("augmented or no shortcut:\n")
|
||||||
txt.print("bool_false\n")
|
b1 = false
|
||||||
return false
|
cx16.r0++
|
||||||
}
|
b1 = b1 or bool_true()
|
||||||
sub calc_a1() -> ubyte {
|
}
|
||||||
txt.print("calc_a1\n")
|
|
||||||
return a1+zero
|
sub bool_true() -> bool {
|
||||||
}
|
txt.print("bool_true\n")
|
||||||
sub calc_a2() -> ubyte {
|
return true
|
||||||
txt.print("calc_a2\n")
|
}
|
||||||
return a2+zero
|
sub bool_false() -> bool {
|
||||||
}
|
txt.print("bool_false\n")
|
||||||
sub calc_x1() -> ubyte {
|
return false
|
||||||
txt.print("calc_x1\n")
|
}
|
||||||
return x1+zero
|
sub calc_a1() -> ubyte {
|
||||||
}
|
txt.print("calc_a1\n")
|
||||||
sub calc_x2() -> ubyte {
|
return a1+zero
|
||||||
txt.print("calc_x2\n")
|
}
|
||||||
return x2+zero
|
sub calc_a2() -> ubyte {
|
||||||
}
|
txt.print("calc_a2\n")
|
||||||
|
return a2+zero
|
||||||
|
}
|
||||||
|
sub calc_x1() -> ubyte {
|
||||||
|
txt.print("calc_x1\n")
|
||||||
|
return x1+zero
|
||||||
|
}
|
||||||
|
sub calc_x2() -> ubyte {
|
||||||
|
txt.print("calc_x2\n")
|
||||||
|
return x2+zero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user