mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
optimize calls/jumps and loops with always known conditions.
This commit is contained in:
parent
7b51597fe9
commit
3ac2385d4b
@ -30,21 +30,21 @@
|
||||
float[len(zcoor)] rotatedz
|
||||
|
||||
sub start() {
|
||||
if irq.time_changed {
|
||||
irq.time_changed = 0
|
||||
_vm_gfx_clearscr(0)
|
||||
_vm_gfx_text(8, 6, 1, "Spin")
|
||||
_vm_gfx_text(29, 11, 1, "to Win !")
|
||||
while(1) {
|
||||
if irq.time_changed {
|
||||
irq.time_changed = 0
|
||||
_vm_gfx_clearscr(0)
|
||||
_vm_gfx_text(8, 6, 1, "Spin")
|
||||
_vm_gfx_text(29, 11, 1, "to Win !")
|
||||
|
||||
for byte i in 0 to width//10 {
|
||||
_vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6)
|
||||
for byte i in 0 to width//10 {
|
||||
_vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6)
|
||||
}
|
||||
|
||||
rotate_vertices(flt(irq.global_time) / 30.0)
|
||||
draw_edges()
|
||||
}
|
||||
|
||||
rotate_vertices(flt(irq.global_time) / 30.0)
|
||||
draw_edges()
|
||||
|
||||
}
|
||||
goto start
|
||||
}
|
||||
|
||||
sub rotate_vertices(t: float) {
|
||||
|
@ -4,12 +4,17 @@
|
||||
|
||||
~ main {
|
||||
|
||||
byte[10,5] barray = 0
|
||||
|
||||
sub start() {
|
||||
X=barray[2,3]
|
||||
barray[3,3]=X
|
||||
|
||||
repeat {
|
||||
_vm_write_str("333\n")
|
||||
} until(1)
|
||||
|
||||
repeat {
|
||||
_vm_write_str("444\n")
|
||||
} until (0)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
|
@ -307,12 +307,12 @@ interface INameScope {
|
||||
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is BranchStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(!stmt.elsepart.isEmpty())
|
||||
if(stmt.elsepart.isNotEmpty())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is IfStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(!stmt.elsepart.isEmpty())
|
||||
if(stmt.elsepart.isNotEmpty())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
}
|
||||
@ -368,6 +368,13 @@ interface INameScope {
|
||||
}
|
||||
|
||||
fun isEmpty() = statements.isEmpty()
|
||||
fun isNotEmpty() = statements.isNotEmpty()
|
||||
|
||||
fun remove(stmt: IStatement) {
|
||||
val removed = statements.remove(stmt)
|
||||
if(!removed)
|
||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1153,7 +1160,7 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
||||
|
||||
class Jump(val address: Int?,
|
||||
val identifier: IdentifierReference?,
|
||||
val generatedLabel: String?,
|
||||
val generatedLabel: String?, // used in code generation scenarios
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
|
@ -83,7 +83,13 @@ class AstChecker(private val namespace: INameScope,
|
||||
override fun process(returnStmt: Return): IStatement {
|
||||
val expectedReturnValues = (returnStmt.definingScope() as? Subroutine)?.returnvalues ?: emptyList()
|
||||
if(expectedReturnValues.size != returnStmt.values.size) {
|
||||
checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position))
|
||||
// if the return value is a function call, check the result of that call instead
|
||||
if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) {
|
||||
val dt = (returnStmt.values[0] as FunctionCall).resultingDatatype(namespace, heap)
|
||||
if(dt!=null && expectedReturnValues.isEmpty())
|
||||
checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position))
|
||||
} else
|
||||
checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position))
|
||||
}
|
||||
|
||||
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) {
|
||||
@ -176,8 +182,8 @@ class AstChecker(private val namespace: INameScope,
|
||||
|
||||
override fun process(label: Label): IStatement {
|
||||
// scope check
|
||||
if(label.parent !is Block && label.parent !is Subroutine) {
|
||||
checkResult.add(SyntaxError("Labels can only be defined in the scope of a block or within another subroutine", label.position))
|
||||
if(label.parent !is Block && label.parent !is Subroutine && label.parent !is AnonymousScope) {
|
||||
checkResult.add(SyntaxError("Labels can only be defined in the scope of a block, a loop body, or within another subroutine", label.position))
|
||||
}
|
||||
return super.process(label)
|
||||
}
|
||||
|
@ -135,9 +135,16 @@ class StackVmProgram(val name: String, val heap: HeapValues) {
|
||||
|
||||
fun optimize() {
|
||||
println("\nOptimizing stackVM code...")
|
||||
optimizeDataConversionAndUselessDiscards()
|
||||
// todo optimize stackvm code more
|
||||
|
||||
// remove nops (that are not a label)
|
||||
this.instructions.removeIf { it.opcode==Opcode.NOP && it !is LabelInstr }
|
||||
}
|
||||
|
||||
private fun optimizeDataConversionAndUselessDiscards() {
|
||||
// - push value followed by a data type conversion -> push the value in the correct type and remove the conversion
|
||||
// - push somthing followed by a discard -> remove both
|
||||
// - push something followed by a discard -> remove both
|
||||
|
||||
val typeConversionOpcodes = setOf(
|
||||
Opcode.LSB,
|
||||
@ -242,11 +249,6 @@ class StackVmProgram(val name: String, val heap: HeapValues) {
|
||||
for(rins in instructionsToReplace) {
|
||||
instructions[rins.key] = rins.value
|
||||
}
|
||||
|
||||
// remove nops (that are not a label)
|
||||
this.instructions.removeIf { it.opcode==Opcode.NOP && it !is LabelInstr }
|
||||
|
||||
// todo optimize stackvm code more
|
||||
}
|
||||
|
||||
fun blockvar(scopedname: String, decl: VarDecl) {
|
||||
@ -329,7 +331,7 @@ class Compiler(private val options: CompilationOptions) {
|
||||
|
||||
val translator = StatementTranslator(intermediate, namespace, heap)
|
||||
translator.process(module)
|
||||
println(" ${translator.stmtUniqueSequenceNr} source statements, ${intermediate.numInstructions} resulting instructions")
|
||||
println(" ${intermediate.numInstructions} vm instructions")
|
||||
|
||||
return intermediate
|
||||
}
|
||||
@ -350,7 +352,7 @@ class Compiler(private val options: CompilationOptions) {
|
||||
private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
private val namespace: INameScope,
|
||||
private val heap: HeapValues): IAstProcessor {
|
||||
var stmtUniqueSequenceNr = 0
|
||||
var generatedLabelSequenceNumber = 0
|
||||
private set
|
||||
|
||||
val breakStmtLabelStack : Stack<String> = Stack()
|
||||
@ -387,7 +389,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
|
||||
private fun translate(statements: List<IStatement>) {
|
||||
for (stmt: IStatement in statements) {
|
||||
stmtUniqueSequenceNr++
|
||||
generatedLabelSequenceNumber++
|
||||
when (stmt) {
|
||||
is Label -> translate(stmt)
|
||||
is Return -> translate(stmt)
|
||||
@ -592,7 +594,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||
stackvmProg.instr(Opcode.NOP)
|
||||
}
|
||||
|
||||
private fun makeLabel(postfix: String): String = "_prog8stmt_${stmtUniqueSequenceNr}_$postfix"
|
||||
private fun makeLabel(postfix: String): String {
|
||||
generatedLabelSequenceNumber++
|
||||
return "_prog8stmt_${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
private fun translate(stmt: IfStatement) {
|
||||
/*
|
||||
|
@ -33,9 +33,14 @@ fun Module.constantFold(globalNamespace: INameScope, heap: HeapValues) {
|
||||
fun Module.optimizeStatements(globalNamespace: INameScope, heap: HeapValues): Int {
|
||||
val optimizer = StatementOptimizer(globalNamespace, heap)
|
||||
this.process(optimizer)
|
||||
for(stmt in optimizer.statementsToRemove) {
|
||||
val scope=stmt.definingScope()
|
||||
scope.remove(stmt)
|
||||
}
|
||||
this.linkParents() // re-link in final configuration
|
||||
|
||||
if(optimizer.optimizationsDone > 0)
|
||||
println("[${this.name}] Debug: ${optimizer.optimizationsDone} statement optimizations performed")
|
||||
this.linkParents() // re-link in final configuration
|
||||
return optimizer.optimizationsDone
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,11 @@ import prog8.functions.BuiltinFunctions
|
||||
todo inline subroutines that are "sufficiently small"
|
||||
*/
|
||||
|
||||
class StatementOptimizer(private val globalNamespace: INameScope, private val heap: HeapValues) : IAstProcessor {
|
||||
class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
|
||||
var optimizationsDone: Int = 0
|
||||
private set
|
||||
|
||||
private var statementsToRemove = mutableListOf<IStatement>()
|
||||
var statementsToRemove = mutableListOf<IStatement>()
|
||||
private set
|
||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||
|
||||
override fun process(functionCall: FunctionCallStatement): IStatement {
|
||||
@ -40,20 +40,59 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
}
|
||||
}
|
||||
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Jump && first.identifier!=null) {
|
||||
optimizationsDone++
|
||||
return FunctionCallStatement(first.identifier, functionCall.arglist, functionCall.position)
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(functionCall)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Jump && first.identifier!=null) {
|
||||
optimizationsDone++
|
||||
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
|
||||
}
|
||||
}
|
||||
return super.process(functionCall)
|
||||
}
|
||||
|
||||
override fun process(returnStmt: Return): IStatement {
|
||||
// if the return value is a subroutine call, replace this with a jump to the subroutine
|
||||
if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) {
|
||||
val call = returnStmt.values[0] as FunctionCall
|
||||
if(call.target.targetStatement(namespace) is Subroutine) {
|
||||
optimizationsDone++
|
||||
return Jump(null, call.target, null, call.position)
|
||||
}
|
||||
}
|
||||
return super.process(returnStmt)
|
||||
}
|
||||
|
||||
override fun process(ifStatement: IfStatement): IStatement {
|
||||
super.process(ifStatement)
|
||||
val constvalue = ifStatement.condition.constValue(globalNamespace, heap)
|
||||
val constvalue = ifStatement.condition.constValue(namespace, heap)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only if-part
|
||||
printWarning("condition is always true", ifStatement.position)
|
||||
optimizationsDone++
|
||||
ifStatement.truepart
|
||||
} else {
|
||||
// always false -> keep only else-part
|
||||
printWarning("condition is always false", ifStatement.position)
|
||||
optimizationsDone++
|
||||
ifStatement.elsepart
|
||||
}
|
||||
}
|
||||
@ -69,6 +108,7 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
// loopvar/reg = range value , follow by block
|
||||
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position), null, range.from, forLoop.position)
|
||||
forLoop.body.statements.add(0, assignment)
|
||||
optimizationsDone++
|
||||
return forLoop.body
|
||||
}
|
||||
}
|
||||
@ -77,15 +117,22 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
|
||||
override fun process(whileLoop: WhileLoop): IStatement {
|
||||
super.process(whileLoop)
|
||||
val constvalue = whileLoop.condition.constValue(globalNamespace, heap)
|
||||
val constvalue = whileLoop.condition.constValue(namespace, heap)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true
|
||||
// always true -> print a warning, and optimize into body + jump
|
||||
printWarning("condition is always true", whileLoop.position)
|
||||
whileLoop
|
||||
val label = Label("__back", whileLoop.condition.position)
|
||||
whileLoop.body.statements.add(0, label)
|
||||
whileLoop.body.statements.add(Jump(null,
|
||||
IdentifierReference(listOf("__back"), whileLoop.condition.position),
|
||||
null, whileLoop.condition.position))
|
||||
optimizationsDone++
|
||||
return whileLoop.body
|
||||
} else {
|
||||
// always false -> ditch whole statement
|
||||
printWarning("condition is always false", whileLoop.position)
|
||||
optimizationsDone++
|
||||
AnonymousScope(mutableListOf(), whileLoop.position)
|
||||
}
|
||||
}
|
||||
@ -94,18 +141,38 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
||||
|
||||
override fun process(repeatLoop: RepeatLoop): IStatement {
|
||||
super.process(repeatLoop)
|
||||
val constvalue = repeatLoop.untilCondition.constValue(globalNamespace, heap)
|
||||
val constvalue = repeatLoop.untilCondition.constValue(namespace, heap)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only the statement block
|
||||
printWarning("condition is always true", repeatLoop.position)
|
||||
optimizationsDone++
|
||||
repeatLoop.body
|
||||
} else {
|
||||
// always false
|
||||
// always false -> print a warning, and optimize into body + jump
|
||||
printWarning("condition is always false", repeatLoop.position)
|
||||
repeatLoop
|
||||
val label = Label("__back", repeatLoop.untilCondition.position)
|
||||
repeatLoop.body.statements.add(0, label)
|
||||
repeatLoop.body.statements.add(Jump(null,
|
||||
IdentifierReference(listOf("__back"), repeatLoop.untilCondition.position),
|
||||
null, repeatLoop.untilCondition.position))
|
||||
optimizationsDone++
|
||||
return repeatLoop.body
|
||||
}
|
||||
}
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
val subroutine = jump.identifier?.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Jump) {
|
||||
optimizationsDone++
|
||||
return first
|
||||
}
|
||||
}
|
||||
return jump
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user