mirror of
https://github.com/irmen/prog8.git
synced 2025-01-12 19:29:50 +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
|
float[len(zcoor)] rotatedz
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
if irq.time_changed {
|
while(1) {
|
||||||
irq.time_changed = 0
|
if irq.time_changed {
|
||||||
_vm_gfx_clearscr(0)
|
irq.time_changed = 0
|
||||||
_vm_gfx_text(8, 6, 1, "Spin")
|
_vm_gfx_clearscr(0)
|
||||||
_vm_gfx_text(29, 11, 1, "to Win !")
|
_vm_gfx_text(8, 6, 1, "Spin")
|
||||||
|
_vm_gfx_text(29, 11, 1, "to Win !")
|
||||||
|
|
||||||
for byte i in 0 to width//10 {
|
for byte i in 0 to width//10 {
|
||||||
_vm_gfx_line(i*2+width//2-width//10, 130, i*10.w, 199, 6)
|
_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) {
|
sub rotate_vertices(t: float) {
|
||||||
|
@ -4,12 +4,17 @@
|
|||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
byte[10,5] barray = 0
|
|
||||||
|
|
||||||
sub start() {
|
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 WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||||
is BranchStatement -> {
|
is BranchStatement -> {
|
||||||
subscopes[stmt.truepart.name] = stmt.truepart
|
subscopes[stmt.truepart.name] = stmt.truepart
|
||||||
if(!stmt.elsepart.isEmpty())
|
if(stmt.elsepart.isNotEmpty())
|
||||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||||
}
|
}
|
||||||
is IfStatement -> {
|
is IfStatement -> {
|
||||||
subscopes[stmt.truepart.name] = stmt.truepart
|
subscopes[stmt.truepart.name] = stmt.truepart
|
||||||
if(!stmt.elsepart.isEmpty())
|
if(stmt.elsepart.isNotEmpty())
|
||||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -368,6 +368,13 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isEmpty() = statements.isEmpty()
|
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?,
|
class Jump(val address: Int?,
|
||||||
val identifier: IdentifierReference?,
|
val identifier: IdentifierReference?,
|
||||||
val generatedLabel: String?,
|
val generatedLabel: String?, // used in code generation scenarios
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : IStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
@ -83,7 +83,13 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
override fun process(returnStmt: Return): IStatement {
|
override fun process(returnStmt: Return): IStatement {
|
||||||
val expectedReturnValues = (returnStmt.definingScope() as? Subroutine)?.returnvalues ?: emptyList()
|
val expectedReturnValues = (returnStmt.definingScope() as? Subroutine)?.returnvalues ?: emptyList()
|
||||||
if(expectedReturnValues.size != returnStmt.values.size) {
|
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)) {
|
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) {
|
||||||
@ -176,8 +182,8 @@ class AstChecker(private val namespace: INameScope,
|
|||||||
|
|
||||||
override fun process(label: Label): IStatement {
|
override fun process(label: Label): IStatement {
|
||||||
// scope check
|
// scope check
|
||||||
if(label.parent !is Block && label.parent !is Subroutine) {
|
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 or within another subroutine", label.position))
|
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)
|
return super.process(label)
|
||||||
}
|
}
|
||||||
|
@ -135,9 +135,16 @@ class StackVmProgram(val name: String, val heap: HeapValues) {
|
|||||||
|
|
||||||
fun optimize() {
|
fun optimize() {
|
||||||
println("\nOptimizing stackVM code...")
|
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 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(
|
val typeConversionOpcodes = setOf(
|
||||||
Opcode.LSB,
|
Opcode.LSB,
|
||||||
@ -242,11 +249,6 @@ class StackVmProgram(val name: String, val heap: HeapValues) {
|
|||||||
for(rins in instructionsToReplace) {
|
for(rins in instructionsToReplace) {
|
||||||
instructions[rins.key] = rins.value
|
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) {
|
fun blockvar(scopedname: String, decl: VarDecl) {
|
||||||
@ -329,7 +331,7 @@ class Compiler(private val options: CompilationOptions) {
|
|||||||
|
|
||||||
val translator = StatementTranslator(intermediate, namespace, heap)
|
val translator = StatementTranslator(intermediate, namespace, heap)
|
||||||
translator.process(module)
|
translator.process(module)
|
||||||
println(" ${translator.stmtUniqueSequenceNr} source statements, ${intermediate.numInstructions} resulting instructions")
|
println(" ${intermediate.numInstructions} vm instructions")
|
||||||
|
|
||||||
return intermediate
|
return intermediate
|
||||||
}
|
}
|
||||||
@ -350,7 +352,7 @@ class Compiler(private val options: CompilationOptions) {
|
|||||||
private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
||||||
private val namespace: INameScope,
|
private val namespace: INameScope,
|
||||||
private val heap: HeapValues): IAstProcessor {
|
private val heap: HeapValues): IAstProcessor {
|
||||||
var stmtUniqueSequenceNr = 0
|
var generatedLabelSequenceNumber = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val breakStmtLabelStack : Stack<String> = Stack()
|
val breakStmtLabelStack : Stack<String> = Stack()
|
||||||
@ -387,7 +389,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
|
|
||||||
private fun translate(statements: List<IStatement>) {
|
private fun translate(statements: List<IStatement>) {
|
||||||
for (stmt: IStatement in statements) {
|
for (stmt: IStatement in statements) {
|
||||||
stmtUniqueSequenceNr++
|
generatedLabelSequenceNumber++
|
||||||
when (stmt) {
|
when (stmt) {
|
||||||
is Label -> translate(stmt)
|
is Label -> translate(stmt)
|
||||||
is Return -> translate(stmt)
|
is Return -> translate(stmt)
|
||||||
@ -592,7 +594,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
stackvmProg.instr(Opcode.NOP)
|
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) {
|
private fun translate(stmt: IfStatement) {
|
||||||
/*
|
/*
|
||||||
|
@ -33,9 +33,14 @@ fun Module.constantFold(globalNamespace: INameScope, heap: HeapValues) {
|
|||||||
fun Module.optimizeStatements(globalNamespace: INameScope, heap: HeapValues): Int {
|
fun Module.optimizeStatements(globalNamespace: INameScope, heap: HeapValues): Int {
|
||||||
val optimizer = StatementOptimizer(globalNamespace, heap)
|
val optimizer = StatementOptimizer(globalNamespace, heap)
|
||||||
this.process(optimizer)
|
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)
|
if(optimizer.optimizationsDone > 0)
|
||||||
println("[${this.name}] Debug: ${optimizer.optimizationsDone} statement optimizations performed")
|
println("[${this.name}] Debug: ${optimizer.optimizationsDone} statement optimizations performed")
|
||||||
this.linkParents() // re-link in final configuration
|
|
||||||
return optimizer.optimizationsDone
|
return optimizer.optimizationsDone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,11 @@ import prog8.functions.BuiltinFunctions
|
|||||||
todo inline subroutines that are "sufficiently small"
|
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
|
var optimizationsDone: Int = 0
|
||||||
private set
|
private set
|
||||||
|
var statementsToRemove = mutableListOf<IStatement>()
|
||||||
private var statementsToRemove = mutableListOf<IStatement>()
|
private set
|
||||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||||
|
|
||||||
override fun process(functionCall: FunctionCallStatement): IStatement {
|
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)
|
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 {
|
override fun process(ifStatement: IfStatement): IStatement {
|
||||||
super.process(ifStatement)
|
super.process(ifStatement)
|
||||||
val constvalue = ifStatement.condition.constValue(globalNamespace, heap)
|
val constvalue = ifStatement.condition.constValue(namespace, heap)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only if-part
|
// always true -> keep only if-part
|
||||||
printWarning("condition is always true", ifStatement.position)
|
printWarning("condition is always true", ifStatement.position)
|
||||||
|
optimizationsDone++
|
||||||
ifStatement.truepart
|
ifStatement.truepart
|
||||||
} else {
|
} else {
|
||||||
// always false -> keep only else-part
|
// always false -> keep only else-part
|
||||||
printWarning("condition is always false", ifStatement.position)
|
printWarning("condition is always false", ifStatement.position)
|
||||||
|
optimizationsDone++
|
||||||
ifStatement.elsepart
|
ifStatement.elsepart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,6 +108,7 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
|||||||
// loopvar/reg = range value , follow by block
|
// loopvar/reg = range value , follow by block
|
||||||
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position), null, range.from, forLoop.position)
|
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, forLoop.position), null, range.from, forLoop.position)
|
||||||
forLoop.body.statements.add(0, assignment)
|
forLoop.body.statements.add(0, assignment)
|
||||||
|
optimizationsDone++
|
||||||
return forLoop.body
|
return forLoop.body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,15 +117,22 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
|||||||
|
|
||||||
override fun process(whileLoop: WhileLoop): IStatement {
|
override fun process(whileLoop: WhileLoop): IStatement {
|
||||||
super.process(whileLoop)
|
super.process(whileLoop)
|
||||||
val constvalue = whileLoop.condition.constValue(globalNamespace, heap)
|
val constvalue = whileLoop.condition.constValue(namespace, heap)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true
|
// always true -> print a warning, and optimize into body + jump
|
||||||
printWarning("condition is always true", whileLoop.position)
|
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 {
|
} else {
|
||||||
// always false -> ditch whole statement
|
// always false -> ditch whole statement
|
||||||
printWarning("condition is always false", whileLoop.position)
|
printWarning("condition is always false", whileLoop.position)
|
||||||
|
optimizationsDone++
|
||||||
AnonymousScope(mutableListOf(), whileLoop.position)
|
AnonymousScope(mutableListOf(), whileLoop.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,18 +141,38 @@ class StatementOptimizer(private val globalNamespace: INameScope, private val he
|
|||||||
|
|
||||||
override fun process(repeatLoop: RepeatLoop): IStatement {
|
override fun process(repeatLoop: RepeatLoop): IStatement {
|
||||||
super.process(repeatLoop)
|
super.process(repeatLoop)
|
||||||
val constvalue = repeatLoop.untilCondition.constValue(globalNamespace, heap)
|
val constvalue = repeatLoop.untilCondition.constValue(namespace, heap)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only the statement block
|
// always true -> keep only the statement block
|
||||||
printWarning("condition is always true", repeatLoop.position)
|
printWarning("condition is always true", repeatLoop.position)
|
||||||
|
optimizationsDone++
|
||||||
repeatLoop.body
|
repeatLoop.body
|
||||||
} else {
|
} else {
|
||||||
// always false
|
// always false -> print a warning, and optimize into body + jump
|
||||||
printWarning("condition is always false", repeatLoop.position)
|
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
|
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…
x
Reference in New Issue
Block a user