mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
optimize typecasted binary expression to avoid even more estack use. also fix wrong parent crash in removal of unused variable's assignments.
This commit is contained in:
parent
00c6f74481
commit
f0f52b9166
@ -4,7 +4,10 @@ import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.expressions.augmentAssignmentOperators
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
@ -17,34 +20,14 @@ import prog8.compilerinterface.isInRegularRAMof
|
||||
|
||||
class BinExprSplitter(private val program: Program, private val options: CompilationOptions, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
|
||||
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...: [ IS THIS STILL TRUE AFTER ALL CHANGES? ]
|
||||
// if(decl.type==VarDeclType.VAR ) {
|
||||
// val binExpr = decl.value as? BinaryExpression
|
||||
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
||||
// // split into a vardecl with just the left expression, and an aug. assignment with the right expression.
|
||||
// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position)
|
||||
// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
// val assign = Assignment(target, augExpr, binExpr.position)
|
||||
// println("SPLIT VARDECL $decl")
|
||||
// return listOf(
|
||||
// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl),
|
||||
// IAstModification.InsertAfter(decl, assign, parent)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// return noModifications
|
||||
// }
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(assignment.value.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
||||
return noModifications
|
||||
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
|
||||
if(binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
||||
return noModifications
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
||||
@ -78,10 +61,39 @@ X = BinExpr X = LeftExpr
|
||||
}
|
||||
|
||||
// TODO further unraveling of binary expression trees into flat statements.
|
||||
// however this should probably be done in a more generic way to also service
|
||||
// however this should probably be done in a more generic way to also work on
|
||||
// the expressiontrees that are not used in an assignment statement...
|
||||
}
|
||||
|
||||
val typecast = assignment.value as? TypecastExpression
|
||||
if(typecast!=null) {
|
||||
val origExpr = typecast.expression as? BinaryExpression
|
||||
if(origExpr!=null) {
|
||||
// it's a typecast of a binary expression.
|
||||
// we can see if we can unwrap the binary expression by working on a new temporary variable
|
||||
// (that has the type of the expression), and then finally doing the typecast.
|
||||
// Once it's outside the typecast, the regular splitting can commence.
|
||||
val tempDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val tempVar = when(tempDt) {
|
||||
DataType.UBYTE -> listOf("cx16", "r15L")
|
||||
DataType.BYTE -> listOf("cx16", "r15sL")
|
||||
DataType.UWORD -> listOf("cx16", "r15")
|
||||
DataType.WORD -> listOf("cx16", "r15s")
|
||||
DataType.FLOAT -> listOf("floats", "tempvar_swap_float")
|
||||
else -> throw FatalAstException("invalid dt $tempDt")
|
||||
}
|
||||
val assignTempVar = Assignment(
|
||||
AssignTarget(IdentifierReference(tempVar, typecast.position), null, null, typecast.position),
|
||||
typecast.expression, typecast.position
|
||||
)
|
||||
println("UNWRAP TYPECAST: ${assignment.position}") // TODO DEBUG
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(assignment, assignTempVar, parent as IStatementContainer),
|
||||
IAstModification.ReplaceNode(typecast.expression, IdentifierReference(tempVar, typecast.position), typecast)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -124,8 +124,10 @@ class UnusedCodeRemover(private val program: Program,
|
||||
it.isInRegularRAMof(compTarget.machine)
|
||||
}
|
||||
if(assignTargets.size==usages.size) {
|
||||
// TODO FIX THAT A MEMREAD OF THE VARIABLE ISN'T RECOGNISED AS A USE (imageviewer iff_module.p8 pixptr)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
return assignTargets.map { it.parent to it.definingScope}.toSet().map {
|
||||
val assignmentsToRemove = assignTargets.map { it.parent to it.parent.parent as IStatementContainer}.toSet()
|
||||
return assignmentsToRemove.map {
|
||||
IAstModification.Remove(it.first, it.second)
|
||||
} + listOf(
|
||||
IAstModification.Remove(decl, parent as IStatementContainer)
|
||||
|
@ -210,7 +210,7 @@ class TestOptimization: FunSpec({
|
||||
asm.valid shouldBe true
|
||||
}
|
||||
|
||||
test("intermediate assignment steps 2 have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {
|
||||
test("intermediate assignment steps generated for typecasted expression") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
@ -219,28 +219,28 @@ class TestOptimization: FunSpec({
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, true, src, writeAssembly = false).assertSuccess()
|
||||
|
||||
// bb = (cos8(r)/2 + 100) as ubyte
|
||||
val bbAssign = result.program.entrypoint.statements.last() as Assignment
|
||||
val texpr = bbAssign.value as TypecastExpression
|
||||
texpr.type shouldBe DataType.UBYTE
|
||||
texpr.expression shouldBe instanceOf<BinaryExpression>()
|
||||
texpr.expression.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.BYTE
|
||||
|
||||
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target)
|
||||
val changer = BeforeAsmGenerationAstChanger(result.program,
|
||||
options,
|
||||
ErrorReporterForTests()
|
||||
)
|
||||
|
||||
changer.visit(result.program)
|
||||
while(changer.applyModifications()>0) {
|
||||
changer.visit(result.program)
|
||||
}
|
||||
|
||||
// printAst(result.program)
|
||||
// TODO finish this test
|
||||
val result = compileText(C64Target, true, src, writeAssembly = true).assertSuccess()
|
||||
/* turned into:
|
||||
ubyte r
|
||||
r = 0
|
||||
ubyte bb
|
||||
cx16.r15sL = cos8(r)
|
||||
cx16.r15sL >>= 1
|
||||
cx16.r15sL += 100
|
||||
bb = cx16.r15sL
|
||||
return
|
||||
*/
|
||||
val st = result.program.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
st.last() shouldBe instanceOf<Return>()
|
||||
var assign = st[3] as Assignment
|
||||
assign.target.identifier!!.nameInSource shouldBe listOf("cx16","r15sL")
|
||||
assign = st[4] as Assignment
|
||||
assign.target.identifier!!.nameInSource shouldBe listOf("cx16","r15sL")
|
||||
assign = st[5] as Assignment
|
||||
assign.target.identifier!!.nameInSource shouldBe listOf("cx16","r15sL")
|
||||
assign = st[6] as Assignment
|
||||
assign.target.identifier!!.nameInSource shouldBe listOf("bb")
|
||||
}
|
||||
|
||||
test("asmgen correctly deals with float typecasting in augmented assignment") {
|
||||
@ -293,4 +293,32 @@ class TestOptimization: FunSpec({
|
||||
(decl2 as VarDecl).name shouldBe "usedvar"
|
||||
assign2 shouldBe instanceOf<Assignment>()
|
||||
}
|
||||
|
||||
test("unused variable removal from subscope") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
if cx16.r0 {
|
||||
uword xx = 42 ; to be removed
|
||||
xx=99 ; to be removed
|
||||
cx16.r0 = 0
|
||||
}
|
||||
func2()
|
||||
|
||||
sub func2() {
|
||||
uword yy = 33 ; to be removed
|
||||
yy=99 ; to be removed
|
||||
cx16.r0 = 0
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
|
||||
result.program.entrypoint.statements.size shouldBe 3
|
||||
val ifstmt = result.program.entrypoint.statements[0] as IfStatement
|
||||
ifstmt.truepart.statements.size shouldBe 1
|
||||
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||
val func2 = result.program.entrypoint.statements[2] as Subroutine
|
||||
func2.statements.size shouldBe 1
|
||||
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||
}
|
||||
})
|
||||
|
@ -3,7 +3,7 @@ TODO
|
||||
|
||||
For next compiler release (7.3)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- fix compiler crashing on assembler and imageviewer (add unit tests)
|
||||
- fix compiler crashing on imageviewer (add unit tests)
|
||||
- add expression simplification to while and until loops as well.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user