removal of unused nodes

This commit is contained in:
Irmen de Jong 2018-08-31 21:46:14 +02:00
parent cc73d90d6e
commit 3e11d45883
7 changed files with 159 additions and 53 deletions

View File

@ -11,12 +11,15 @@
~ extra233 {
; this is imported
X = 42
return 44
const byte snerp=33
const byte snerp2 = snerp+22
sub foo() -> () {
A=99
return
X = 42+snerp
return 44+snerp
sub foo234234() -> () {
A=99+snerp
return A+snerp2
}
}

View File

@ -14,20 +14,27 @@ fun main(args: Array<String>) {
println("\nIL65 compiler by Irmen de Jong (irmen@razorvine.net)")
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
// import main module and process additional imports
val filepath = Paths.get(args[0]).normalize()
val moduleAst = importModule(filepath)
moduleAst.linkParents()
val globalNamespace = moduleAst.namespace()
//globalNamespace.debugPrint()
// perform syntax checks and optimizations
moduleAst.checkIdentifiers(globalNamespace)
moduleAst.optimizeExpressions(globalNamespace)
moduleAst.optimizeStatements(globalNamespace)
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers(globalNamespace)
moduleAst.optimizeStatements(globalNamespace, allScopedSymbolDefinitions)
val globalNamespaceAfterOptimize = moduleAst.namespace() // it could have changed in the meantime
moduleAst.checkValid(globalNamespaceAfterOptimize) // check if final tree is valid
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers(globalNamespace)
// determine special compiler options
val options = moduleAst.statements.filter { it is Directive && it.directive=="%option" }.flatMap { (it as Directive).args }.toSet()
val outputType = (moduleAst.statements.singleOrNull { it is Directive && it.directive=="%output"}
as? Directive)?.args?.single()?.name?.toUpperCase()
@ -42,6 +49,10 @@ fun main(args: Array<String>) {
if(zpType==null) ZeropageType.COMPATIBLE else ZeropageType.valueOf(zpType),
options.contains(DirectiveArg(null, "enable_floats", null))
)
// compile the syntax tree into intermediate form, and optimize that
val compiler = Compiler(compilerOptions, globalNamespaceAfterOptimize)
val intermediate = compiler.compile(moduleAst)
intermediate.optimize()
@ -49,8 +60,8 @@ fun main(args: Array<String>) {
// val assembler = intermediate.compileToAssembly()
// assembler.assemble(compilerOptions, "input", "output")
// val monitorfile = assembler.generateBreakpointList()
// start the vice emulator
//
// // start the vice emulator
// val program = "foo"
// val cmdline = listOf("x64", "-moncommands", monitorfile,
// "-autostartprgmode", "1", "-autostart-warp", "-autostart", program)
@ -61,5 +72,3 @@ fun main(args: Array<String>) {
exitProcess(1)
}
}

View File

@ -61,50 +61,62 @@ class ExpressionException(message: String, val position: Position?) : AstExcepti
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col $startCol-$endCol]"
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
}
interface IAstProcessor {
fun process(module: Module) {
module.statements = module.statements.map { it.process(this) }
module.statements = module.statements.map { it.process(this) }.toMutableList()
}
fun process(expr: PrefixExpression): IExpression {
expr.expression = expr.expression.process(this)
return expr
}
fun process(expr: BinaryExpression): IExpression {
expr.left = expr.left.process(this)
expr.right = expr.right.process(this)
return expr
}
fun process(directive: Directive): IStatement {
return directive
}
fun process(block: Block): IStatement {
block.statements = block.statements.map { it.process(this) }
block.statements = block.statements.map { it.process(this) }.toMutableList()
return block
}
fun process(decl: VarDecl): IStatement {
decl.value = decl.value?.process(this)
decl.arrayspec?.process(this)
return decl
}
fun process(subroutine: Subroutine): IStatement {
subroutine.statements = subroutine.statements.map { it.process(this) }
subroutine.statements = subroutine.statements.map { it.process(this) }.toMutableList()
return subroutine
}
fun process(functionCall: FunctionCall): IExpression {
functionCall.arglist = functionCall.arglist.map { it.process(this) }
return functionCall
}
fun process(functionCall: FunctionCallStatement): IStatement {
functionCall.arglist = functionCall.arglist.map { it.process(this) }
return functionCall
}
fun process(identifier: IdentifierReference): IExpression {
// note: this is an identifier that is used in an expression.
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
return identifier
}
fun process(jump: Jump): IStatement {
return jump
}
@ -137,7 +149,7 @@ interface Node {
interface IStatement : Node {
fun process(processor: IAstProcessor) : IStatement
fun scopedName(name: String): String {
fun makeScopedName(name: String): List<String> {
val scope = mutableListOf<String>()
var statementScope = this.parent
while(statementScope!=null && statementScope !is Module) {
@ -147,7 +159,7 @@ interface IStatement : Node {
statementScope = statementScope.parent
}
scope.add(name)
return scope.joinToString(".")
return scope
}
}
@ -160,7 +172,11 @@ interface IFunctionCall {
interface INameScope {
val name: String
val position: Position?
var statements: List<IStatement>
var statements: MutableList<IStatement>
fun usedNames(): Set<String>
fun registerUsedName(name: String)
fun subScopes() = statements.filter { it is INameScope } .map { it as INameScope }.associate { it.name to it }
@ -188,6 +204,9 @@ interface INameScope {
statementScope = statementScope.parent
if (statementScope == null)
return null
if(statementScope.parent==null && statementScope !is Module)
throw AstException("non-Module node has no parent! node: $statementScope at ${statementScope.position}")
val localScope = statementScope as INameScope
val result = localScope.definedNames()[scopedName[0]]
if (result != null)
@ -213,6 +232,12 @@ interface INameScope {
}
printNames(0, this)
}
fun removeStatement(statement: IStatement) {
// remove a statement (most likely because it is never referenced such as a subroutine)
val removed = statements.remove(statement)
if(!removed) throw AstException("node to remove wasn't found")
}
}
@ -236,13 +261,14 @@ data class AnonymousStatementList(override var parent: Node?, var statements: Li
data class Module(override val name: String,
override var statements: List<IStatement>) : Node, INameScope {
override var statements: MutableList<IStatement>) : Node, INameScope {
override var position: Position? = null
override var parent: Node? = null
override fun linkParents(parent: Node) {
this.parent=parent
}
fun linkParents() {
parent = null
statements.forEach {it.linkParents(this)}
@ -254,17 +280,44 @@ data class Module(override val name: String,
fun namespace(): INameScope {
class GlobalNamespace(override val name: String,
override var statements: List<IStatement>,
override val position: Position?) : INameScope
override var statements: MutableList<IStatement>,
override val position: Position?) : INameScope {
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used
override fun usedNames(): Set<String> = scopedNamesUsed
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
val stmt = super.lookup(scopedName, statement)
if(stmt!=null) {
val targetScopedName = when(stmt) {
is Label -> stmt.makeScopedName(stmt.name)
is VarDecl -> stmt.makeScopedName(stmt.name)
is Block -> stmt.makeScopedName(stmt.name)
is Subroutine -> stmt.makeScopedName(stmt.name)
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
}
registerUsedName(targetScopedName.joinToString("."))
}
return stmt
}
override fun registerUsedName(name: String) {
scopedNamesUsed.add(name)
}
}
return GlobalNamespace("<<<global>>>", statements, position)
}
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
}
data class Block(override val name: String,
val address: Int?,
override var statements: List<IStatement>) : IStatement, INameScope {
override var statements: MutableList<IStatement>) : IStatement, INameScope {
override var position: Position? = null
override var parent: Node? = null
@ -278,6 +331,9 @@ data class Block(override val name: String,
override fun toString(): String {
return "Block(name=$name, address=$address, ${statements.size} statements)"
}
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
}
@ -533,7 +589,7 @@ data class RegisterExpr(val register: Register) : IExpression {
}
data class IdentifierReference(val scopedName: List<String>) : IExpression {
data class IdentifierReference(val nameInSource: List<String>) : IExpression {
override var position: Position? = null
override var parent: Node? = null
@ -542,9 +598,9 @@ data class IdentifierReference(val scopedName: List<String>) : IExpression {
}
override fun constValue(namespace: INameScope): LiteralValue? {
val node = namespace.lookup(scopedName, this)
val node = namespace.lookup(nameInSource, this)
?:
throw ExpressionException("undefined symbol: ${scopedName.joinToString(".")}", position)
throw ExpressionException("undefined symbol: ${nameInSource.joinToString(".")}", position)
val vardecl = node as? VarDecl
if(vardecl==null) {
throw ExpressionException("name should be a constant, instead of: ${node::class.simpleName}", position)
@ -555,7 +611,7 @@ data class IdentifierReference(val scopedName: List<String>) : IExpression {
}
override fun process(processor: IAstProcessor) = processor.process(this)
override fun referencesIdentifier(name: String): Boolean = scopedName.last() == name // @todo is this correct all the time?
override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
}
@ -600,8 +656,8 @@ data class FunctionCall(override var target: IdentifierReference, override var a
override fun constValue(namespace: INameScope): LiteralValue? {
// if the function is a built-in function and the args are consts, should evaluate!
if(target.scopedName.size>1) return null
return when(target.scopedName[0]){
if(target.nameInSource.size>1) return null
return when(target.nameInSource[0]){
"sin" -> builtin_sin(arglist, position, namespace)
"cos" -> builtin_cos(arglist, position, namespace)
"abs" -> builtin_abs(arglist, position, namespace)
@ -656,15 +712,15 @@ data class Subroutine(override val name: String,
val parameters: List<SubroutineParameter>,
val returnvalues: List<SubroutineReturnvalue>,
val address: Int?,
override var statements: List<IStatement>) : IStatement, INameScope {
override var statements: MutableList<IStatement>) : IStatement, INameScope {
override var position: Position? = null
override var parent: Node? = null
override fun linkParents(parent: Node) {
this.parent = parent
parameters.forEach { it.parent=this }
returnvalues.forEach { it.parent=this }
statements.forEach { it.parent=this }
parameters.forEach { it.linkParents(this) }
returnvalues.forEach { it.linkParents(this) }
statements.forEach { it.linkParents(this) }
}
override fun process(processor: IAstProcessor) = processor.process(this)
@ -672,6 +728,9 @@ data class Subroutine(override val name: String,
override fun toString(): String {
return "Subroutine(name=$name, address=$address, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements)"
}
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
}
@ -715,7 +774,7 @@ data class IfStatement(var condition: IExpression,
/***************** Antlr Extension methods to create AST ****************/
fun il65Parser.ModuleContext.toAst(name: String, withPosition: Boolean) : Module {
val module = Module(name, modulestatement().map { it.toAst(withPosition) })
val module = Module(name, modulestatement().map { it.toAst(withPosition) }.toMutableList())
module.position = toPosition(withPosition)
return module
}
@ -753,8 +812,8 @@ private fun il65Parser.BlockContext.toAst(withPosition: Boolean) : IStatement {
}
private fun il65Parser.Statement_blockContext.toAst(withPosition: Boolean): List<IStatement>
= statement().map { it.toAst(withPosition) }
private fun il65Parser.Statement_blockContext.toAst(withPosition: Boolean): MutableList<IStatement>
= statement().map { it.toAst(withPosition) }.toMutableList()
private fun il65Parser.StatementContext.toAst(withPosition: Boolean) : IStatement {
@ -918,7 +977,7 @@ private fun il65Parser.SubroutineContext.toAst(withPosition: Boolean) : Subrouti
if(sub_params()==null) emptyList() else sub_params().toAst(),
if(sub_returns()==null) emptyList() else sub_returns().toAst(),
sub_address()?.integerliteral()?.toAst(),
if(statement_block()==null) emptyList() else statement_block().toAst(withPosition))
if(statement_block()==null) mutableListOf() else statement_block().toAst(withPosition))
sub.position = toPosition(withPosition)
return sub
}

View File

@ -309,8 +309,8 @@ class AstChecker(private val globalNamespace: INameScope) : IAstProcessor {
}
private fun checkFunctionExists(target: IdentifierReference, statement: IStatement) {
if(globalNamespace.lookup(target.scopedName, statement)==null)
checkResult.add(SyntaxError("undefined function or subroutine: ${target.scopedName.joinToString(".")}", statement.position))
if(globalNamespace.lookup(target.nameInSource, statement)==null)
checkResult.add(SyntaxError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position))
}
private fun checkValueRange(datatype: DataType, value: LiteralValue, position: Position?) : Boolean {

View File

@ -35,50 +35,46 @@ class AstIdentifiersChecker(private val globalNamespace: INameScope) : IAstProce
}
override fun process(block: Block): IStatement {
val scopedName = block.scopedName(block.name)
val scopedName = block.makeScopedName(block.name).joinToString(".")
val existing = symbols[scopedName]
if(existing!=null) {
nameError(block.name, block.position, existing)
} else {
symbols[scopedName] = block
}
super.process(block)
return block
return super.process(block)
}
override fun process(decl: VarDecl): IStatement {
val scopedName = decl.scopedName(decl.name)
val scopedName = decl.makeScopedName(decl.name).joinToString(".")
val existing = symbols[scopedName]
if(existing!=null) {
nameError(decl.name, decl.position, existing)
} else {
symbols[scopedName] = decl
}
super.process(decl)
return decl
return super.process(decl)
}
override fun process(subroutine: Subroutine): IStatement {
val scopedName = subroutine.scopedName(subroutine.name)
val scopedName = subroutine.makeScopedName(subroutine.name).joinToString(".")
val existing = symbols[scopedName]
if(existing!=null) {
nameError(subroutine.name, subroutine.position, existing)
} else {
symbols[scopedName] = subroutine
}
super.process(subroutine)
return subroutine
return super.process(subroutine)
}
override fun process(label: Label): IStatement {
val scopedName = label.scopedName(label.name)
val scopedName = label.makeScopedName(label.name).joinToString(".")
val existing = symbols[scopedName]
if(existing!=null) {
nameError(label.name, label.position, existing)
} else {
symbols[scopedName] = label
}
super.process(label)
return label
return super.process(label)
}
}

View File

@ -161,7 +161,8 @@ class AssemblyResult(val name: String) {
}
}
fun genereateBreakpointList(): String {
fun generateBreakpointList(): String {
// todo build breakpoint list!
/*
def generate_breakpoint_list(self, program_filename: str) -> str:
breakpoints = []

View File

@ -3,9 +3,10 @@ package il65.optimizing
import il65.ast.*
fun Module.optimizeStatements(globalNamespace: INameScope) {
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 optimizations performed")
@ -18,7 +19,6 @@ fun Module.optimizeStatements(globalNamespace: INameScope) {
}
/*
todo remove unused blocks, subroutines and variable decls (replace with empty AnonymousStatementList)
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,6 +37,32 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
optimizationsDone = 0
}
override fun process(functionCall: FunctionCall): IExpression {
val function = globalNamespace.lookup(functionCall.target.nameInSource, functionCall)
if(function!=null) {
val scopedName = when(function) {
is Label -> function.makeScopedName(function.name)
is Subroutine -> function.makeScopedName(function.name)
else -> throw AstException("invalid function call target node type")
}
globalNamespace.registerUsedName(scopedName.joinToString("."))
}
return super.process(functionCall)
}
override fun process(functionCall: FunctionCallStatement): IStatement {
val function = globalNamespace.lookup(functionCall.target.nameInSource, functionCall)
if(function!=null) {
val scopedName = when(function) {
is Label -> function.makeScopedName(function.name)
is Subroutine -> function.makeScopedName(function.name)
else -> throw AstException("invalid function call target node type")
}
globalNamespace.registerUsedName(scopedName.joinToString("."))
}
return super.process(functionCall)
}
override fun process(ifStatement: IfStatement): IStatement {
super.process(ifStatement)
val constvalue = ifStatement.condition.constValue(globalNamespace)
@ -53,4 +79,16 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
}
return ifStatement
}
fun removeUnusedNodes(usedNames: Set<String>, allScopedSymbolDefinitions: MutableMap<String, IStatement>) {
for ((name, value) in allScopedSymbolDefinitions) {
if(!usedNames.contains(name)) {
val parentScope = value.parent as INameScope
val localname = name.substringAfterLast(".")
println("${value.position} Warning: ${value::class.simpleName} '$localname' is never used")
parentScope.removeStatement(value)
optimizationsDone++
}
}
}
}