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.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
import prog8.ast.expressions.augmentAssignmentOperators
|
import prog8.ast.expressions.augmentAssignmentOperators
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
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() {
|
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> {
|
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
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
if (binExpr != null) {
|
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,
|
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.
|
// 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...
|
// 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
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,8 +124,10 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
it.isInRegularRAMof(compTarget.machine)
|
it.isInRegularRAMof(compTarget.machine)
|
||||||
}
|
}
|
||||||
if(assignTargets.size==usages.size) {
|
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)
|
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)
|
IAstModification.Remove(it.first, it.second)
|
||||||
} + listOf(
|
} + listOf(
|
||||||
IAstModification.Remove(decl, parent as IStatementContainer)
|
IAstModification.Remove(decl, parent as IStatementContainer)
|
||||||
|
@ -210,7 +210,7 @@ class TestOptimization: FunSpec({
|
|||||||
asm.valid shouldBe true
|
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 = """
|
val src = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
@ -219,28 +219,28 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target, true, src, writeAssembly = false).assertSuccess()
|
val result = compileText(C64Target, true, src, writeAssembly = true).assertSuccess()
|
||||||
|
/* turned into:
|
||||||
// bb = (cos8(r)/2 + 100) as ubyte
|
ubyte r
|
||||||
val bbAssign = result.program.entrypoint.statements.last() as Assignment
|
r = 0
|
||||||
val texpr = bbAssign.value as TypecastExpression
|
ubyte bb
|
||||||
texpr.type shouldBe DataType.UBYTE
|
cx16.r15sL = cos8(r)
|
||||||
texpr.expression shouldBe instanceOf<BinaryExpression>()
|
cx16.r15sL >>= 1
|
||||||
texpr.expression.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.BYTE
|
cx16.r15sL += 100
|
||||||
|
bb = cx16.r15sL
|
||||||
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target)
|
return
|
||||||
val changer = BeforeAsmGenerationAstChanger(result.program,
|
*/
|
||||||
options,
|
val st = result.program.entrypoint.statements
|
||||||
ErrorReporterForTests()
|
st.size shouldBe 8
|
||||||
)
|
st.last() shouldBe instanceOf<Return>()
|
||||||
|
var assign = st[3] as Assignment
|
||||||
changer.visit(result.program)
|
assign.target.identifier!!.nameInSource shouldBe listOf("cx16","r15sL")
|
||||||
while(changer.applyModifications()>0) {
|
assign = st[4] as Assignment
|
||||||
changer.visit(result.program)
|
assign.target.identifier!!.nameInSource shouldBe listOf("cx16","r15sL")
|
||||||
}
|
assign = st[5] as Assignment
|
||||||
|
assign.target.identifier!!.nameInSource shouldBe listOf("cx16","r15sL")
|
||||||
// printAst(result.program)
|
assign = st[6] as Assignment
|
||||||
// TODO finish this test
|
assign.target.identifier!!.nameInSource shouldBe listOf("bb")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("asmgen correctly deals with float typecasting in augmented assignment") {
|
test("asmgen correctly deals with float typecasting in augmented assignment") {
|
||||||
@ -293,4 +293,32 @@ class TestOptimization: FunSpec({
|
|||||||
(decl2 as VarDecl).name shouldBe "usedvar"
|
(decl2 as VarDecl).name shouldBe "usedvar"
|
||||||
assign2 shouldBe instanceOf<Assignment>()
|
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)
|
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.
|
- add expression simplification to while and until loops as well.
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user