mirror of
https://github.com/irmen/prog8.git
synced 2025-01-14 17:31:01 +00:00
shortcutting part one
This commit is contained in:
parent
8f18b5b8a7
commit
1c55a6c6dc
@ -1,6 +1,6 @@
|
||||
package prog8.code.core
|
||||
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor")
|
||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||
|
@ -1041,39 +1041,60 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun optimizedLogicalAndOrExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||
if (expr.right.isSimple()) {
|
||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
|
||||
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" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
if(asmgen.options.shortCircuit && (!expr.left.isSimple() && !expr.right.isSimple())) {
|
||||
// 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)
|
||||
println("SHORTCUT AND ${expr.position}") // TODO weg
|
||||
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)
|
||||
return true
|
||||
}
|
||||
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) {
|
||||
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
if (expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandWord(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
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) {
|
||||
// normal evaluation, not worth to shortcircuit the simple right operand
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(right, RegisterOrPair.A, false)
|
||||
when (operator) {
|
||||
"and" -> TODO("logical and (with optional shortcircuit) ${left.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${left.position}")
|
||||
"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)
|
||||
}
|
||||
|
||||
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)
|
||||
"or=" -> inplaceModification(assign.target, "or", 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)
|
||||
@ -791,12 +793,42 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
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)
|
||||
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
|
||||
asmgen.out(" sta $name")
|
||||
}
|
||||
|
||||
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")
|
||||
inplacemodificationRegisterAwithVariable(operator, otherName, dt in SignedDatatypes)
|
||||
asmgen.out(" sta $name")
|
||||
@ -852,6 +884,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
"&" -> asmgen.out(" and $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("""
|
||||
@ -1130,6 +1163,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
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: no logical and/or shortcut here, not worth it due to simple right operand
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" lda $name | clc | adc #$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)
|
||||
"|" -> immediateOrInplace(name, value)
|
||||
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
"&", "and" -> immediateAndInplace(name, value)
|
||||
"|", "or" -> immediateOrInplace(name, value)
|
||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
"==" -> {
|
||||
asmgen.out("""
|
||||
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``,
|
||||
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)
|
||||
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.
|
||||
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'
|
||||
(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.
|
||||
Prog8 applies short-circuit aka McCarthy evaluation for ``and`` and ``or``.
|
||||
|
||||
.. note::
|
||||
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 ``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``
|
||||
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.
|
||||
|
@ -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 ....
|
||||
- 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)
|
||||
- test on word variables, if the TODO doesn't trigger, just replace it with a specific notification
|
||||
|
||||
...
|
||||
|
||||
|
@ -2,13 +2,22 @@
|
||||
%zeropage dontuse
|
||||
|
||||
main {
|
||||
sub start () {
|
||||
ubyte a1 = 10
|
||||
ubyte a2 = 20
|
||||
ubyte x1 = 30
|
||||
ubyte x2 = 40
|
||||
ubyte zero = 0
|
||||
/*
|
||||
sub start() {
|
||||
if bool_true() and bool_false() and bool_true()
|
||||
txt.print("all true")
|
||||
else
|
||||
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")
|
||||
if calc_a1()<calc_x1() and calc_a2()<=calc_x2()
|
||||
txt.print("* 1a and ok\n")
|
||||
@ -56,30 +65,47 @@ main {
|
||||
txt.print("\n5d:\n")
|
||||
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("bool_true\n")
|
||||
return true
|
||||
}
|
||||
sub bool_false() -> bool {
|
||||
txt.print("bool_false\n")
|
||||
return false
|
||||
}
|
||||
sub calc_a1() -> ubyte {
|
||||
txt.print("calc_a1\n")
|
||||
return a1+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
|
||||
}
|
||||
txt.print("augmented or shortcut:\n")
|
||||
b1 = true
|
||||
cx16.r0++
|
||||
b1 = b1 or bool_true()
|
||||
txt.print("augmented or no shortcut:\n")
|
||||
b1 = false
|
||||
cx16.r0++
|
||||
b1 = b1 or bool_true()
|
||||
}
|
||||
|
||||
sub bool_true() -> bool {
|
||||
txt.print("bool_true\n")
|
||||
return true
|
||||
}
|
||||
sub bool_false() -> bool {
|
||||
txt.print("bool_false\n")
|
||||
return false
|
||||
}
|
||||
sub calc_a1() -> ubyte {
|
||||
txt.print("calc_a1\n")
|
||||
return a1+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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user