restructure

This commit is contained in:
Irmen de Jong 2018-09-15 11:56:17 +02:00
parent 6da048ba4c
commit f3532e9014
10 changed files with 345 additions and 318 deletions

View File

@ -4,8 +4,9 @@ import java.nio.file.Paths
import il65.ast.*
import il65.parser.*
import il65.compiler.*
import il65.optimizing.optimizeExpressions
import il65.optimizing.constantFold
import il65.optimizing.optimizeStatements
import il65.optimizing.simplifyExpressions
import java.io.File
import kotlin.system.exitProcess
@ -23,9 +24,10 @@ fun main(args: Array<String>) {
try {
// import main module and process additional imports
println("Parsing...")
val moduleAst = importModule(filepath)
moduleAst.linkParents()
val globalNameSpaceBeforeOptimization = moduleAst.definingScope()
var namespace = moduleAst.definingScope()
// determine special compiler options
@ -46,16 +48,25 @@ fun main(args: Array<String>) {
// perform syntax checks and optimizations
moduleAst.checkIdentifiers()
moduleAst.optimizeExpressions(globalNameSpaceBeforeOptimization)
moduleAst.checkValid(globalNameSpaceBeforeOptimization, compilerOptions) // check if tree is valid
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers()
moduleAst.optimizeStatements(globalNameSpaceBeforeOptimization, allScopedSymbolDefinitions)
StatementReorderer().process(moduleAst) // reorder statements to please the compiler later
val globalNamespaceAfterOptimize = moduleAst.definingScope() // create it again, it could have changed in the meantime
moduleAst.checkValid(globalNamespaceAfterOptimize, compilerOptions) // check if final tree is valid
moduleAst.checkRecursion(globalNamespaceAfterOptimize) // check if there are recursive subroutine calls
// globalNamespaceAfterOptimize.debugPrint()
println("Optimizing...")
moduleAst.constantFold(namespace)
moduleAst.checkValid(namespace, compilerOptions) // check if tree is valid
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers()
while(true) {
// keep optimizing expressions and statements until no more steps remain
val optsDone1 = moduleAst.simplifyExpressions(namespace)
val optsDone2 = moduleAst.optimizeStatements(namespace, allScopedSymbolDefinitions)
if(optsDone1 + optsDone2 == 0)
break
}
StatementReorderer().process(moduleAst) // reorder statements to please the compiler later
namespace = moduleAst.definingScope() // create it again, it could have changed in the meantime
moduleAst.checkValid(namespace, compilerOptions) // check if final tree is valid
moduleAst.checkRecursion(namespace) // check if there are recursive subroutine calls
// namespace.debugPrint()
// compile the syntax tree into stackvmProg form, and optimize that
val compiler = Compiler(compilerOptions)

View File

@ -225,19 +225,6 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
return super.process(decl)
}
/**
* check if condition
*/
override fun process(ifStatement: IfStatement): IStatement {
val constvalue = ifStatement.condition.constValue(namespace)
if(constvalue!=null) {
val msg = if (constvalue.asBooleanValue) "condition is always true" else "condition is always false"
println("${ifStatement.position} Warning: $msg")
}
return super.process(ifStatement)
}
/**
* check the arguments of the directive
*/
@ -452,7 +439,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
if(av.register!=Register.A && av.register!=Register.X && av.register!=Register.Y)
return err("register '$av' in byte array is not a single register")
} else {
TODO("array value $av")
TODO("check array value $av")
}
}

View File

@ -88,7 +88,7 @@ data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, va
class Compiler(private val options: CompilationOptions) {
fun compile(module: Module) : StackVmProgram {
println("\nCompiling parsed source code to stackvmProg code...")
println("\nCreating stackVM code...")
val namespace = module.definingScope()
@ -423,7 +423,7 @@ class StackVmProgram(val name: String) {
}
fun optimize() {
println("\nOptimizing stackvmProg code...")
println("\nOptimizing stackVM code...")
// todo optimize stackvm code
}

View File

@ -1,269 +1,8 @@
package il65.optimizing
import il65.parser.ParsingFailedError
import il65.ast.*
import il65.compiler.Petscii
import kotlin.math.pow
fun Module.optimizeExpressions(globalNamespace: INameScope) {
val optimizer = ExpressionOptimizer(globalNamespace)
try {
this.process(optimizer)
} catch (ax: AstException) {
optimizer.addError(ax)
}
if(optimizer.optimizationsDone==0)
println("[${this.name}] 0 expression optimizations performed")
while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) {
println("[${this.name}] ${optimizer.optimizationsDone} expression optimizations performed")
optimizer.optimizationsDone = 0
this.process(optimizer)
}
if(optimizer.errors.isNotEmpty()) {
optimizer.errors.forEach { System.err.println(it) }
throw ParsingFailedError("There are ${optimizer.errors.size} errors.")
} else {
this.linkParents() // re-link in final configuration
}
}
/*
todo eliminate useless terms:
*0 -> constant 0
X*1, X/1, X//1 -> just X
X*-1 -> unary prefix -X
X**0 -> 1
X**1 -> X
X**-1 -> 1.0/X
X << 0 -> X
X | 0 -> X
x & 0 -> 0
X ^ 0 -> X
todo expression optimization: remove redundant builtin function calls
todo expression optimization: reduce expression nesting / flattening of parenthesis
todo expression optimization: simplify logical expression when a term makes it always true or false (1 or 0)
todo expression optimization: optimize some simple multiplications into shifts (A*8 -> A<<3, A/4 -> A>>2)
todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call)
*/
class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcessor {
var optimizationsDone: Int = 0
var errors : MutableList<AstException> = mutableListOf()
private val reportedErrorMessages = mutableSetOf<String>()
fun addError(x: AstException) {
// check that we don't add the same error more than once
if(!reportedErrorMessages.contains(x.toString())) {
reportedErrorMessages.add(x.toString())
errors.add(x)
}
}
override fun process(decl: VarDecl): IStatement {
// the initializer value can't refer to the variable itself (recursive definition)
if(decl.value?.referencesIdentifier(decl.name) == true||
decl.arrayspec?.x?.referencesIdentifier(decl.name) == true ||
decl.arrayspec?.y?.referencesIdentifier(decl.name) == true) {
errors.add(ExpressionError("recursive var declaration", decl.position))
return decl
}
val result = super.process(decl)
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
when {
decl.datatype == DataType.FLOAT -> {
// vardecl: for float vars, promote constant integer initialization values to floats
val literal = decl.value as? LiteralValue
if (literal != null && (literal.type == DataType.BYTE || literal.type==DataType.WORD)) {
val newValue = LiteralValue(DataType.FLOAT, floatvalue = literal.asNumericValue!!.toDouble(), position = literal.position)
decl.value = newValue
}
}
decl.datatype == DataType.BYTE || decl.datatype == DataType.WORD -> {
// vardecl: for byte/word vars, convert char/string of length 1 initialization values to integer
val literal = decl.value as? LiteralValue
if (literal != null && literal.isString && literal.strvalue?.length == 1) {
val petscii = Petscii.encodePetscii(literal.strvalue)[0]
val newValue = LiteralValue(DataType.BYTE, bytevalue = petscii, position = literal.position)
decl.value = newValue
}
}
}
}
return result
}
/**
* replace identifiers that refer to const value, with the value itself
*/
override fun process(identifier: IdentifierReference): IExpression {
return try {
identifier.constValue(globalNamespace) ?: identifier
} catch (ax: AstException) {
addError(ax)
identifier
}
}
override fun process(functionCall: FunctionCall): IExpression {
return try {
super.process(functionCall)
functionCall.constValue(globalNamespace) ?: functionCall
} catch (ax: AstException) {
addError(ax)
functionCall
}
}
/**
* Try to process a unary prefix expression.
* Compile-time constant sub expressions will be evaluated on the spot.
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
*/
override fun process(expr: PrefixExpression): IExpression {
return try {
super.process(expr)
val subexpr = expr.expression
if (subexpr is LiteralValue) {
// process prefixed literal values (such as -3, not true)
return when {
expr.operator == "+" -> subexpr
expr.operator == "-" -> when {
subexpr.asIntegerValue!= null -> {
optimizationsDone++
LiteralValue.optimalNumeric(-subexpr.asIntegerValue, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue(DataType.FLOAT, floatvalue = -subexpr.floatvalue, position = subexpr.position)
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
expr.operator == "~" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue.optimalNumeric(subexpr.asIntegerValue.inv(), subexpr.position)
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
expr.operator == "not" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue.fromBoolean(subexpr.asIntegerValue == 0, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue.fromBoolean(subexpr.floatvalue == 0.0, subexpr.position)
}
else -> throw ExpressionError("can not take logical not of $subexpr", subexpr.position)
}
else -> throw ExpressionError(expr.operator, subexpr.position)
}
}
return expr
} catch (ax: AstException) {
addError(ax)
expr
}
}
/**
* Try to process a binary expression.
* Compile-time constant sub expressions will be evaluated on the spot.
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
*/
override fun process(expr: BinaryExpression): IExpression {
return try {
super.process(expr)
val evaluator = ConstExprEvaluator()
val leftconst = expr.left.constValue(globalNamespace)
val rightconst = expr.right.constValue(globalNamespace)
return when {
leftconst != null && rightconst != null -> {
optimizationsDone++
evaluator.evaluate(leftconst, expr.operator, rightconst)
}
else -> expr
}
} catch (ax: AstException) {
addError(ax)
expr
}
}
override fun process(range: RangeExpr): IExpression {
return try {
super.process(range)
val from = range.from.constValue(globalNamespace)
val to = range.to.constValue(globalNamespace)
if (from != null && to != null) {
when {
from.type==DataType.WORD || to.type==DataType.WORD -> {
// range on word value boundaries
val rangeValue = from.asIntegerValue!!.rangeTo(to.asIntegerValue!!)
if (rangeValue.last - rangeValue.first > 65535) {
throw ExpressionError("amount of values in range exceeds 65535", range.position)
}
return LiteralValue(DataType.ARRAY_W, arrayvalue = rangeValue.map {
val v = LiteralValue(DataType.WORD, wordvalue = it, position = range.position)
v.parent = range.parent
v
}.toMutableList(), position = from.position)
}
from.type==DataType.BYTE && to.type==DataType.BYTE -> {
// range on byte value boundaries
val rangeValue = from.bytevalue!!.rangeTo(to.bytevalue!!)
if (rangeValue.last - rangeValue.first > 65535) {
throw ExpressionError("amount of values in range exceeds 65535", range.position)
}
return LiteralValue(DataType.ARRAY, arrayvalue = rangeValue.map {
val v = LiteralValue(DataType.BYTE, bytevalue = it.toShort(), position = range.position)
v.parent = range.parent
v
}.toMutableList(), position = from.position)
}
from.strvalue != null && to.strvalue != null -> {
// char range
val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0])
if (rangevalue.last - rangevalue.first > 65535) {
throw ExpressionError("amount of characters in range exceeds 65535", range.position)
}
val newval = LiteralValue(DataType.STR, strvalue = rangevalue.toList().joinToString(""), position = range.position)
newval.parent = range.parent
return newval
}
else -> AstException("range on weird datatype")
}
}
return range
} catch (ax: AstException) {
addError(ax)
range
}
}
override fun process(literalValue: LiteralValue): LiteralValue {
if(literalValue.arrayvalue!=null) {
val newArray = literalValue.arrayvalue.map { it.process(this) }
literalValue.arrayvalue.clear()
literalValue.arrayvalue.addAll(newArray)
}
return super.process(literalValue)
}
}
class ConstExprEvaluator {
fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression {
@ -410,11 +149,11 @@ class ConstExprEvaluator {
}
private fun bitwisexor(left: LiteralValue, right: LiteralValue): LiteralValue {
if(left.type==DataType.BYTE) {
if(left.type== DataType.BYTE) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.BYTE, bytevalue = (left.bytevalue!!.toInt() xor (right.asIntegerValue and 255)).toShort(), position = left.position)
}
} else if(left.type==DataType.WORD) {
} else if(left.type== DataType.WORD) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.WORD, wordvalue = left.wordvalue!! xor right.asIntegerValue, position = left.position)
}
@ -423,11 +162,11 @@ class ConstExprEvaluator {
}
private fun bitwiseor(left: LiteralValue, right: LiteralValue): LiteralValue {
if(left.type==DataType.BYTE) {
if(left.type== DataType.BYTE) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.BYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
}
} else if(left.type==DataType.WORD) {
} else if(left.type== DataType.WORD) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.WORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
}
@ -436,11 +175,11 @@ class ConstExprEvaluator {
}
private fun bitwiseand(left: LiteralValue, right: LiteralValue): LiteralValue {
if(left.type==DataType.BYTE) {
if(left.type== DataType.BYTE) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.BYTE, bytevalue = (left.bytevalue!!.toInt() or (right.asIntegerValue and 255)).toShort(), position = left.position)
}
} else if(left.type==DataType.WORD) {
} else if(left.type== DataType.WORD) {
if(right.asIntegerValue!=null) {
return LiteralValue(DataType.WORD, wordvalue = left.wordvalue!! or right.asIntegerValue, position = left.position)
}
@ -555,4 +294,4 @@ class ConstExprEvaluator {
else -> throw ExpressionError(error, left.position)
}
}
}
}

View File

@ -0,0 +1,217 @@
package il65.optimizing
import il65.ast.*
import il65.compiler.Petscii
class ConstantFolding(private val globalNamespace: INameScope) : IAstProcessor {
var optimizationsDone: Int = 0
var errors : MutableList<AstException> = mutableListOf()
private val reportedErrorMessages = mutableSetOf<String>()
fun addError(x: AstException) {
// check that we don't add the same error more than once
if(!reportedErrorMessages.contains(x.toString())) {
reportedErrorMessages.add(x.toString())
errors.add(x)
}
}
override fun process(decl: VarDecl): IStatement {
// the initializer value can't refer to the variable itself (recursive definition)
if(decl.value?.referencesIdentifier(decl.name) == true||
decl.arrayspec?.x?.referencesIdentifier(decl.name) == true ||
decl.arrayspec?.y?.referencesIdentifier(decl.name) == true) {
errors.add(ExpressionError("recursive var declaration", decl.position))
return decl
}
val result = super.process(decl)
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
when {
decl.datatype == DataType.FLOAT -> {
// vardecl: for float vars, promote constant integer initialization values to floats
val literal = decl.value as? LiteralValue
if (literal != null && (literal.type == DataType.BYTE || literal.type==DataType.WORD)) {
val newValue = LiteralValue(DataType.FLOAT, floatvalue = literal.asNumericValue!!.toDouble(), position = literal.position)
decl.value = newValue
}
}
decl.datatype == DataType.BYTE || decl.datatype == DataType.WORD -> {
// vardecl: for byte/word vars, convert char/string of length 1 initialization values to integer
val literal = decl.value as? LiteralValue
if (literal != null && literal.isString && literal.strvalue?.length == 1) {
val petscii = Petscii.encodePetscii(literal.strvalue)[0]
val newValue = LiteralValue(DataType.BYTE, bytevalue = petscii, position = literal.position)
decl.value = newValue
}
}
}
}
return result
}
/**
* replace identifiers that refer to const value, with the value itself
*/
override fun process(identifier: IdentifierReference): IExpression {
return try {
identifier.constValue(globalNamespace) ?: identifier
} catch (ax: AstException) {
addError(ax)
identifier
}
}
override fun process(functionCall: FunctionCall): IExpression {
return try {
super.process(functionCall)
functionCall.constValue(globalNamespace) ?: functionCall
} catch (ax: AstException) {
addError(ax)
functionCall
}
}
/**
* Try to process a unary prefix expression.
* Compile-time constant sub expressions will be evaluated on the spot.
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
*/
override fun process(expr: PrefixExpression): IExpression {
return try {
super.process(expr)
val subexpr = expr.expression
if (subexpr is LiteralValue) {
// process prefixed literal values (such as -3, not true)
return when {
expr.operator == "+" -> subexpr
expr.operator == "-" -> when {
subexpr.asIntegerValue!= null -> {
optimizationsDone++
LiteralValue.optimalNumeric(-subexpr.asIntegerValue, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue(DataType.FLOAT, floatvalue = -subexpr.floatvalue, position = subexpr.position)
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
expr.operator == "~" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue.optimalNumeric(subexpr.asIntegerValue.inv(), subexpr.position)
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
expr.operator == "not" -> when {
subexpr.asIntegerValue != null -> {
optimizationsDone++
LiteralValue.fromBoolean(subexpr.asIntegerValue == 0, subexpr.position)
}
subexpr.floatvalue != null -> {
optimizationsDone++
LiteralValue.fromBoolean(subexpr.floatvalue == 0.0, subexpr.position)
}
else -> throw ExpressionError("can not take logical not of $subexpr", subexpr.position)
}
else -> throw ExpressionError(expr.operator, subexpr.position)
}
}
return expr
} catch (ax: AstException) {
addError(ax)
expr
}
}
/**
* Try to process a binary expression.
* Compile-time constant sub expressions will be evaluated on the spot.
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
*/
override fun process(expr: BinaryExpression): IExpression {
return try {
super.process(expr)
val evaluator = ConstExprEvaluator()
val leftconst = expr.left.constValue(globalNamespace)
val rightconst = expr.right.constValue(globalNamespace)
return when {
leftconst != null && rightconst != null -> {
optimizationsDone++
evaluator.evaluate(leftconst, expr.operator, rightconst)
}
else -> expr
}
} catch (ax: AstException) {
addError(ax)
expr
}
}
override fun process(range: RangeExpr): IExpression {
return try {
super.process(range)
val from = range.from.constValue(globalNamespace)
val to = range.to.constValue(globalNamespace)
if (from != null && to != null) {
when {
from.type==DataType.WORD || to.type==DataType.WORD -> {
// range on word value boundaries
val rangeValue = from.asIntegerValue!!.rangeTo(to.asIntegerValue!!)
if (rangeValue.last - rangeValue.first > 65535) {
throw ExpressionError("amount of values in range exceeds 65535", range.position)
}
return LiteralValue(DataType.ARRAY_W, arrayvalue = rangeValue.map {
val v = LiteralValue(DataType.WORD, wordvalue = it, position = range.position)
v.parent = range.parent
v
}.toMutableList(), position = from.position)
}
from.type==DataType.BYTE && to.type==DataType.BYTE -> {
// range on byte value boundaries
val rangeValue = from.bytevalue!!.rangeTo(to.bytevalue!!)
if (rangeValue.last - rangeValue.first > 65535) {
throw ExpressionError("amount of values in range exceeds 65535", range.position)
}
return LiteralValue(DataType.ARRAY, arrayvalue = rangeValue.map {
val v = LiteralValue(DataType.BYTE, bytevalue = it.toShort(), position = range.position)
v.parent = range.parent
v
}.toMutableList(), position = from.position)
}
from.strvalue != null && to.strvalue != null -> {
// char range
val rangevalue = from.strvalue[0].rangeTo(to.strvalue[0])
if (rangevalue.last - rangevalue.first > 65535) {
throw ExpressionError("amount of characters in range exceeds 65535", range.position)
}
val newval = LiteralValue(DataType.STR, strvalue = rangevalue.toList().joinToString(""), position = range.position)
newval.parent = range.parent
return newval
}
else -> AstException("range on weird datatype")
}
}
return range
} catch (ax: AstException) {
addError(ax)
range
}
}
override fun process(literalValue: LiteralValue): LiteralValue {
if(literalValue.arrayvalue!=null) {
val newArray = literalValue.arrayvalue.map { it.process(this) }
literalValue.arrayvalue.clear()
literalValue.arrayvalue.addAll(newArray)
}
return super.process(literalValue)
}
}

View File

@ -0,0 +1,48 @@
package il65.optimizing
import il65.ast.AstException
import il65.ast.INameScope
import il65.ast.IStatement
import il65.ast.Module
import il65.parser.ParsingFailedError
fun Module.constantFold(globalNamespace: INameScope) {
val optimizer = ConstantFolding(globalNamespace)
try {
this.process(optimizer)
} catch (ax: AstException) {
optimizer.addError(ax)
}
while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) {
println("[${this.name}] ${optimizer.optimizationsDone} constant folds performed")
optimizer.optimizationsDone = 0
this.process(optimizer)
}
if(optimizer.errors.isNotEmpty()) {
optimizer.errors.forEach { System.err.println(it) }
throw ParsingFailedError("There are ${optimizer.errors.size} errors.")
} else {
this.linkParents() // re-link in final configuration
}
}
fun Module.optimizeStatements(globalNamespace: INameScope, allScopedSymbolDefinitions: MutableMap<String, IStatement>): Int {
val optimizer = StatementOptimizer(globalNamespace)
this.process(optimizer)
optimizer.removeUnusedNodes(globalNamespace.usedNames(), allScopedSymbolDefinitions)
if(optimizer.optimizationsDone > 0)
println("[${this.name}] ${optimizer.optimizationsDone} statement optimizations performed")
this.linkParents() // re-link in final configuration
return optimizer.optimizationsDone
}
fun Module.simplifyExpressions(namespace: INameScope) : Int {
val optimizer = SimplifyExpressions(namespace)
this.process(optimizer)
if(optimizer.optimizationsDone > 0)
println("[${this.name}] ${optimizer.optimizationsDone} expression optimizations performed")
return optimizer.optimizationsDone
}

View File

@ -0,0 +1,33 @@
package il65.optimizing
import il65.ast.IAstProcessor
import il65.ast.INameScope
/*
todo eliminate useless terms:
*0 -> constant 0
X*1, X/1, X//1 -> just X
X*-1 -> unary prefix -X
X**0 -> 1
X**1 -> X
X**-1 -> 1.0/X
X << 0 -> X
X | 0 -> X
x & 0 -> 0
X ^ 0 -> X
todo expression optimization: remove redundant builtin function calls
todo expression optimization: reduce expression nesting / flattening of parenthesis
todo expression optimization: simplify logical expression when a term makes it always true or false (1 or 0)
todo expression optimization: optimize some simple multiplications into shifts (A*8 -> A<<3, A/4 -> A>>2)
todo optimize addition with self into shift 1 (A+=A -> A<<=1)
todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call)
todo remove or simplify logical aug assigns like A |= 0, A |= true, A |= false (or perhaps turn them into byte values first?)
*/
class SimplifyExpressions(namespace: INameScope) : IAstProcessor {
var optimizationsDone: Int = 0
// @todo build this optimizer
}

View File

@ -5,22 +5,9 @@ import il65.functions.BuiltinFunctionNames
import il65.functions.BuiltinFunctionsWithoutSideEffects
fun Module.optimizeStatements(globalNamespace: INameScope, allScopedSymbolDefinitions: MutableMap<String, IStatement>) {
val optimizer = StatementOptimizer(globalNamespace)
this.process(optimizer)
optimizer.removeUnusedNodes(globalNamespace.usedNames(), allScopedSymbolDefinitions)
if(optimizer.optimizationsDone==0)
println("[${this.name}] 0 statement optimizations performed")
while(optimizer.optimizationsDone>0) {
println("[${this.name}] ${optimizer.optimizationsDone} statement optimizations performed")
optimizer.reset()
this.process(optimizer)
}
this.linkParents() // re-link in final configuration
}
/*
todo remove if statements with empty statement blocks
todo replace if statements with only else block
todo statement optimization: create augmented assignment from assignment that only refers to its lvalue (A=A+10, A=4*A, ...)
todo statement optimization: X+=1, X-=1 --> X++/X-- ,
todo remove statements that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
@ -37,10 +24,6 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
private var statementsToRemove = mutableListOf<IStatement>()
fun reset() {
optimizationsDone = 0
}
override fun process(functionCall: FunctionCall): IExpression {
val target = globalNamespace.lookup(functionCall.target.nameInSource, functionCall)
if(target!=null)
@ -100,6 +83,8 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
}
fun removeUnusedNodes(usedNames: Set<String>, allScopedSymbolDefinitions: MutableMap<String, IStatement>) {
val symbolsToRemove = mutableListOf<String>()
for ((name, value) in allScopedSymbolDefinitions) {
if(!usedNames.contains(name)) {
val parentScope = value.parent as INameScope
@ -109,10 +94,15 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
if(value is Block)
println("${value.position} Info: block '$localname' is never used")
parentScope.removeStatement(value)
symbolsToRemove.add(name)
optimizationsDone++
}
}
for(name in symbolsToRemove) {
allScopedSymbolDefinitions.remove(name)
}
for(stmt in statementsToRemove) {
stmt.definingScope().removeStatement(stmt)
}

View File

@ -15,7 +15,7 @@ private val importedModules : HashMap<String, Module> = hashMapOf()
fun importModule(filePath: Path) : Module {
println("importing '${filePath.fileName}' (from ${filePath.parent})...")
println("importing '${filePath.fileName}' (from '${filePath.parent}')")
if(!Files.isReadable(filePath))
throw ParsingFailedError("No such file: $filePath")

View File

@ -130,9 +130,10 @@ enum class Syscall(val callNr: Short) {
INPUT_VAR(15), // user input a string into a variable
GFX_PIXEL(16), // plot a pixel at (x,y,color) pushed on stack in that order
GFX_CLEARSCR(17), // clear the screen with color pushed on stack
GFX_TEXT(18), // write text on screen at (x,y,text,color) pushed on stack in that order
GFX_TEXT(18), // write text on screen at (x,y,color,text) pushed on stack in that order
RANDOM(19), // push a random byte on the stack
RANDOM_W(20), // push a random word on the stack
RANDOM_F(21), // push a random float on the stack (between 0.0 and 1.0)
FUNC_P_CARRY(100),
@ -598,7 +599,7 @@ class Program (prog: MutableList<Instruction>,
Instruction(opcode, Value(DataType.BYTE, call.callNr), callValues)
}
else -> {
println("INSTR $opcode at $lineNr args=$args") // TODO weg
// println("INSTR $opcode at $lineNr args=$args")
Instruction(opcode, getArgValue(args))
}
}
@ -1006,13 +1007,14 @@ class StackVm(val traceOutputFile: String?) {
canvas.clearScreen(color.integerValue())
}
Syscall.GFX_TEXT -> {
val color = evalstack.pop()
val text = evalstack.pop()
val color = evalstack.pop()
val (y, x) = evalstack.pop2()
canvas.writeText(x.integerValue(), y.integerValue(), text.stringvalue!!, color.integerValue())
}
Syscall.RANDOM -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255))
Syscall.RANDOM_W -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535))
Syscall.RANDOM_F -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble()))
else -> throw VmExecutionException("unimplemented syscall $syscall")
}
}