mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 08:29:25 +00:00
automatically convert multi-compare expression (if X==1 or X==2..) to contaiment check if X in [1,2,..]
This commit is contained in:
parent
8c4765b386
commit
fef52c0112
@ -91,13 +91,47 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
override fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
// try to replace a multi-comparison expression (if x==1 or x==2 or x==3 ... ) by a simple containment check.
|
||||
// but only if the containment check is the top-level expression.
|
||||
if(parent is BinaryExpression)
|
||||
return noModifications
|
||||
if(expr.operator == "or") {
|
||||
val leftBinExpr = expr.left as? BinaryExpression
|
||||
val rightBinExpr = expr.right as? BinaryExpression
|
||||
if(leftBinExpr!=null && leftBinExpr.operator=="==" && rightBinExpr!=null && rightBinExpr.operator=="==") {
|
||||
if(leftBinExpr.right is NumericLiteral && rightBinExpr.right is NumericLiteral) {
|
||||
if(leftBinExpr.left isSameAs rightBinExpr.left)
|
||||
errors.warn("consider using 'in' or 'when' to test for multiple values", expr.position)
|
||||
val leftBinExpr1 = expr.left as? BinaryExpression
|
||||
val rightBinExpr1 = expr.right as? BinaryExpression
|
||||
|
||||
if(rightBinExpr1?.operator=="==" && rightBinExpr1.right is NumericLiteral && leftBinExpr1!=null) {
|
||||
val needle = rightBinExpr1.left
|
||||
val values = mutableListOf(rightBinExpr1.right as NumericLiteral)
|
||||
|
||||
fun isMultiComparisonRecurse(expr: BinaryExpression): Boolean {
|
||||
if(expr.operator=="==") {
|
||||
if(expr.right is NumericLiteral && expr.left isSameAs needle) {
|
||||
values.add(expr.right as NumericLiteral)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if(expr.operator!="or")
|
||||
return false
|
||||
val leftBinExpr = expr.left as? BinaryExpression
|
||||
val rightBinExpr = expr.right as? BinaryExpression
|
||||
if(leftBinExpr==null || rightBinExpr==null || rightBinExpr.right !is NumericLiteral || !rightBinExpr.left.isSameAs(needle))
|
||||
return false
|
||||
if(rightBinExpr.operator=="==")
|
||||
values.add(rightBinExpr.right as NumericLiteral)
|
||||
else
|
||||
return false
|
||||
return isMultiComparisonRecurse(leftBinExpr)
|
||||
}
|
||||
|
||||
if(isMultiComparisonRecurse(leftBinExpr1)) {
|
||||
// replace it!
|
||||
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
|
||||
val elementType = needle.inferType(program).getOrElse { throw FatalAstException("invalid needle dt") }
|
||||
val arrayType = ElementToArrayTypes.getValue(elementType)
|
||||
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
|
||||
val containment = ContainmentCheck(needle, valuesArray, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,12 @@ package prog8tests
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldStartWith
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.string.shouldNotBeBlank
|
||||
import io.kotest.matchers.string.shouldStartWith
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||
import prog8.ast.ParentSentinel
|
||||
@ -169,7 +171,6 @@ class TestOptimization: FunSpec({
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), true, source, writeAssembly = false)!!
|
||||
printProgram(result.program)
|
||||
// expected:
|
||||
// word llw
|
||||
// llw = 300
|
||||
@ -424,7 +425,6 @@ class TestOptimization: FunSpec({
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
printProgram(result.program)
|
||||
result.program.entrypoint.statements.size shouldBe 3
|
||||
val ifstmt = result.program.entrypoint.statements[0] as IfElse
|
||||
ifstmt.truepart.statements.size shouldBe 1
|
||||
@ -609,7 +609,6 @@ class TestOptimization: FunSpec({
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
printProgram(result.program)
|
||||
/* expected result:
|
||||
uword yy
|
||||
yy = 20
|
||||
@ -649,8 +648,6 @@ class TestOptimization: FunSpec({
|
||||
yy = 0
|
||||
xx += 10
|
||||
*/
|
||||
printProgram(result.program)
|
||||
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
stmts.filterIsInstance<VarDecl>().size shouldBe 2
|
||||
@ -665,4 +662,101 @@ class TestOptimization: FunSpec({
|
||||
(xxValue.left as? IdentifierReference)?.nameInSource shouldBe listOf("xx")
|
||||
xxValue.right shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("multi-comparison replaced by containment check") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte source=99
|
||||
ubyte thingy=42
|
||||
|
||||
if source==3 or source==4 or source==99 or source==1
|
||||
thingy++
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
/*
|
||||
expected result:
|
||||
ubyte[] auto_heap_var = [1,4,99,3]
|
||||
ubyte source
|
||||
source = 99
|
||||
ubyte thingy
|
||||
thingy = 42
|
||||
if source in auto_heap_var
|
||||
thingy++
|
||||
*/
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
val ifStmt = stmts[5] as IfElse
|
||||
val containment = ifStmt.condition as ContainmentCheck
|
||||
(containment.element as IdentifierReference).nameInSource shouldBe listOf("source")
|
||||
(containment.iterable as IdentifierReference).nameInSource.single() shouldStartWith "auto_heap_value"
|
||||
val arrayDecl = stmts[0] as VarDecl
|
||||
arrayDecl.isArray shouldBe true
|
||||
arrayDecl.arraysize?.constIndex() shouldBe 4
|
||||
val arrayValue = arrayDecl.value as ArrayLiteral
|
||||
arrayValue.type shouldBe InferredTypes.InferredType.known(DataType.ARRAY_UB)
|
||||
arrayValue.value shouldBe listOf(
|
||||
NumericLiteral.optimalInteger(1, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(3, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(4, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(99, Position.DUMMY))
|
||||
}
|
||||
|
||||
test("invalid multi-comparison (not all equals) not replaced") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte source=99
|
||||
ubyte thingy=42
|
||||
|
||||
if source==3 or source==4 or source!=99 or source==1
|
||||
thingy++
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
printProgram(result.program)
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
|
||||
test("invalid multi-comparison (not all same needle) not replaced") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte source=99
|
||||
ubyte thingy=42
|
||||
|
||||
if source==3 or source==4 or thingy==99 or source==1
|
||||
thingy++
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
printProgram(result.program)
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
|
||||
test("invalid multi-comparison (not all or) not replaced") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte source=99
|
||||
ubyte thingy=42
|
||||
|
||||
if source==3 or source==4 and source==99 or source==1
|
||||
thingy++
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
printProgram(result.program)
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
})
|
||||
|
@ -3,12 +3,15 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls.
|
||||
- fix compiler crash when comparing strings
|
||||
- make it possible to inline non-asmsub routines that just contain a single statement (return, functioncall, assignment)
|
||||
but this requires all identifiers in the inlined expression to be changed to fully scoped names.
|
||||
If we can do that why not perhaps also able to inline multi-line subroutines? Why would it be limited to just 1 line? Maybe to protect against code size bloat.
|
||||
Inlined subroutines cannot contain further nested subroutines!
|
||||
Only if the arguments are simple expressions, and the inlined subroutine cannot contain further nested subroutines!
|
||||
This requires all identifiers in the inlined expression to be changed to fully scoped names (because their scope changes).
|
||||
If we can do that why not perhaps also able to inline multi-line subroutines?
|
||||
Why would it be limited to just 1 line? Maybe to protect against code size bloat.
|
||||
Once this works, look for library subroutines that should be inlined.
|
||||
- vm: add way more instructions operating directly on memory instead of only registers
|
||||
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||
|
||||
...
|
||||
|
||||
@ -24,7 +27,6 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- vm: add way more instructions operating directly on memory instead of only registers
|
||||
- vm: codeGen: various TODOs to tweak code
|
||||
- vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8
|
||||
- vm: make registers typed? so that it's immediately obvious what type they represent. Much like regular variables in memory.
|
||||
@ -40,7 +42,6 @@ Compiler:
|
||||
- make everything an expression? (get rid of Statements. Statements are expressions with void return types?).
|
||||
- simplifyConditionalExpression() should not split expression if it still results in stack-based evaluation, but how does it know?
|
||||
- simplifyConditionalExpression() sometimes introduces needless assignment to r9 tempvar (what scenarios?)
|
||||
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
|
||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway)
|
||||
then we can get rid of the instruction lists in the machinedefinitions as well?
|
||||
- [problematic due to using 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||
@ -58,12 +59,12 @@ Libraries:
|
||||
- optimize several inner loops in gfx2 even further?
|
||||
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?
|
||||
- add a flood fill routine to gfx2?
|
||||
- diskio: use cx16 MACPTR() to load stuff faster? (see it's use in X16edit to fast load blocks)
|
||||
- diskio: use cx16 MACPTR() to load stuff faster? (see its use in X16edit to fast load blocks)
|
||||
note that it might fail on non sdcard files so have to make graceful degradation
|
||||
- add a diskio.f_seek() routine for the Cx16 that uses its seek dos api? (only if that's stable)
|
||||
|
||||
Expressions:
|
||||
|
||||
- pipe operator: (targets other than 'Virtual'): allow non-unary function calls in the pipe that specify the other argument(s) in the calls.
|
||||
- rethink the whole "isAugmentable" business. Because the way this is determined, should always also be exactly mirrorred in the AugmentableAssignmentAsmGen or you'll get a crash at code gen time.
|
||||
note: new ast PtAssignment already has no knowledge about this anymore.
|
||||
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
|
||||
@ -84,5 +85,4 @@ Optimizations:
|
||||
- AssignmentAsmGen.assignExpression() -> better code gen for assigning boolean comparison expressions
|
||||
- when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop
|
||||
but we have no efficient way right now to see if the body references a variable.
|
||||
- automatically convert if statements that test for multiple values (if X==1 or X==2..) to if X in [1,2,..] statements, instead of just a warning.
|
||||
- introduce byte-index operator to avoid index multiplications in loops over arrays? see github issue #4
|
||||
|
@ -7,23 +7,20 @@
|
||||
|
||||
main {
|
||||
|
||||
sub add(ubyte first, ubyte second) -> ubyte {
|
||||
return first+second
|
||||
}
|
||||
|
||||
sub mul(ubyte first, ubyte second) -> ubyte {
|
||||
return first*second
|
||||
}
|
||||
|
||||
sub start() {
|
||||
str thing = "????"
|
||||
|
||||
if thing=="bmap" {
|
||||
txt.print("gottem")
|
||||
}
|
||||
|
||||
; TODO: test with builtin function using multiple args (such as mkword)
|
||||
ubyte source=99
|
||||
ubyte value = add(3,4) |> add(10) |> mul(2) |> math.sin8u() ; TODO should not work yet on vm codegen, but it compiles.... :/
|
||||
txt.print_ub(value)
|
||||
txt.nl()
|
||||
uword wvalue = add(3,4) |> add($30) |> mkword($ea)
|
||||
txt.print_uwhex(wvalue, true)
|
||||
txt.nl()
|
||||
; ubyte value = add(3,4) |> add(10) |> mul(2) |> math.sin8u() ; TODO should not work yet on vm codegen, but it compiles.... :/
|
||||
; txt.print_ub(value)
|
||||
; txt.nl()
|
||||
; uword wvalue = add(3,4) |> add($30) |> mkword($ea)
|
||||
; txt.print_uwhex(wvalue, true)
|
||||
; txt.nl()
|
||||
|
||||
; expected output: aaabbb aaa bbb
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user