cleaned up various ast checks/mutations

This commit is contained in:
Irmen de Jong 2020-03-24 19:37:54 +01:00
parent 131fe670a4
commit f2bb238e9b
11 changed files with 45 additions and 165 deletions

View File

@ -4,7 +4,7 @@ import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.processing.*
import prog8.compiler.CompilationOptions
import prog8.compiler.target.AsmVariablePreparer
import prog8.compiler.target.AsmVariableAndReturnsPreparer
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
@ -13,10 +13,10 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: Err
checker.visit(this)
}
internal fun Program.prepareAsmVariables(errors: ErrorReporter) {
val mover = AsmVariablePreparer(this, errors)
mover.visit(this)
mover.applyModifications()
internal fun Program.prepareAsmVariablesAndReturns(errors: ErrorReporter) {
val fixer = AsmVariableAndReturnsPreparer(this, errors)
fixer.visit(this)
fixer.applyModifications()
}
internal fun Program.reorderStatements() {

View File

@ -413,9 +413,11 @@ internal class AstChecker(private val program: Program,
}
}
}
val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
if(targetDt in IterableDatatypes)
errors.err("cannot assign to a string or array type", assignTarget.position)
// target type check is already done at the Assignment:
// val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
// if(targetDt in IterableDatatypes)
// errors.err("cannot assign to a string or array type", assignTarget.position)
if (assignment is Assignment) {
@ -1054,11 +1056,15 @@ internal class AstChecker(private val program: Program,
for((index, stmt) in statements.withIndex()) {
if(stmt is FunctionCallStatement
&& stmt.target.nameInSource.last()=="exit"
&& index < statements.lastIndex)
errors.warn("unreachable code, exit call above never returns", statements[index+1].position)
&& index < statements.lastIndex) {
println("STMT AFTER EXIT ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt
errors.warn("unreachable code, exit call above never returns", statements[index + 1].position)
}
if(stmt is Return && index < statements.lastIndex)
errors.warn("unreachable code, return statement above", statements[index+1].position)
if(stmt is Return && index < statements.lastIndex) {
println("STMT AFTER RETURN ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt
errors.warn("unreachable code, return statement above", statements[index + 1].position)
}
stmt.accept(this)
}

View File

@ -25,11 +25,9 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
private val addReturns = mutableListOf<Pair<INameScope, Int>>()
private val addVardecls = mutableMapOf<INameScope, MutableList<VarDecl>>()
override fun visit(module: Module) {
addReturns.clear()
addVardecls.clear()
super.visit(module)
@ -59,13 +57,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
module.statements.removeAll(directives)
module.statements.addAll(0, directives)
// add new statements
for(pos in addReturns) {
val returnStmt = Return(null, pos.first.position)
returnStmt.linkParents(pos.first as Node)
pos.first.statements.add(pos.second, returnStmt)
}
for((where, decls) in addVardecls) {
where.statements.addAll(0, decls)
decls.forEach { it.linkParents(where as Node) }
@ -93,23 +84,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
}
}
// make sure there is a 'return' in front of the first subroutine
// (if it isn't the first statement in the block itself, and isn't the program's entrypoint)
if(numSubroutinesAtEnd>0 && block.statements.size > (numSubroutinesAtEnd+1)) {
val firstSub = block.statements[block.statements.size - numSubroutinesAtEnd] as Subroutine
if(firstSub.name != "start" && block.name != "main") {
val stmtBeforeFirstSub = block.statements[block.statements.size - numSubroutinesAtEnd - 1]
if (stmtBeforeFirstSub !is Return
&& stmtBeforeFirstSub !is Jump
&& stmtBeforeFirstSub !is Subroutine
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
val ret = Return(null, stmtBeforeFirstSub.position)
ret.linkParents(block)
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret)
}
}
}
val varDecls = block.statements.filterIsInstance<VarDecl>()
block.statements.removeAll(varDecls)
block.statements.addAll(0, varDecls)
@ -124,19 +98,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
override fun visit(subroutine: Subroutine): Statement {
super.visit(subroutine)
val scope = subroutine.definingScope()
if(scope is Subroutine) {
for(stmt in scope.statements.withIndex()) {
if(stmt.index>0 && stmt.value===subroutine) {
val precedingStmt = scope.statements[stmt.index-1]
if(precedingStmt !is Jump && precedingStmt !is Subroutine) {
// insert a return statement before a nested subroutine, to avoid falling trough inside the subroutine
addReturns.add(Pair(scope, stmt.index))
}
}
}
}
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
subroutine.statements.removeAll(varDecls)
subroutine.statements.addAll(0, varDecls)
@ -144,18 +105,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
subroutine.statements.removeAll(directives)
subroutine.statements.addAll(0, directives)
if(subroutine.returntypes.isEmpty()) {
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
// and if an assembly block doesn't contain a rts/rti
if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) {
if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) {
val returnStmt = Return(null, subroutine.position)
returnStmt.linkParents(subroutine)
subroutine.statements.add(returnStmt)
}
}
}
return subroutine
}

View File

@ -45,11 +45,10 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
val targettype = targetItype.typeOrElse(DataType.STRUCT)
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
if (valuetype != targettype) {
if (valuetype isAssignableTo targettype)
return listOf(IAstModification.ReplaceNode(
assignment.value,
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
assignment))
return listOf(IAstModification.ReplaceNode(
assignment.value,
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
assignment))
}
}
return emptyList()

View File

@ -40,7 +40,7 @@ fun compileProgram(filepath: Path,
if (optimize)
optimizeAst(programAst, errors)
postprocessAst(programAst, errors, compilationOptions)
// printAst(programAst)
printAst(programAst) // TODO
if(writeAssembly)
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
}
@ -179,7 +179,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir:
optimize: Boolean, compilerOptions: CompilationOptions): String {
// asm generation directly from the Ast,
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
programAst.prepareAsmVariables(errors)
programAst.prepareAsmVariablesAndReturns(errors)
errors.handle()
val assembly = CompilationTarget.asmGenerator(
programAst,

View File

@ -11,7 +11,7 @@ import prog8.ast.processing.IAstModification
import prog8.ast.statements.*
class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstWalker() {
class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorReporter): AstWalker() {
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.value==null && decl.type==VarDeclType.VAR && decl.datatype in NumericDatatypes) {
@ -49,4 +49,9 @@ class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstW
}
return emptyList()
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
TODO("insert Return statements at the required places such as at the end of a subroutine if they're missing")
return emptyList()
}
}

View File

@ -199,7 +199,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
when(expr.operator) {
">>" -> {
// bit-shifts are always by a constant number (for now)
// TODO for everything except UBYTE, if shifting > 2 bits, use a subroutine
translateExpression(expr.left)
val amount = expr.right.constValue(program)!!.number.toInt()
when (leftDt) {
@ -239,7 +238,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
"<<" -> {
// bit-shifts are always by a constant number (for now)
// TODO for the word types, if shifting > 3 bits, use a subroutine
translateExpression(expr.left)
val amount = expr.right.constValue(program)!!.number.toInt()
if (leftDt in ByteDatatypes) {

View File

@ -8,7 +8,6 @@ import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.statements.*
import prog8.compiler.target.CompilationTarget
import prog8.functions.BuiltinFunctions
import kotlin.math.floor
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor {
@ -620,74 +619,4 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
}
return array
}
// TODO: type casts are already done elsewhere, remove all this?:
override fun visit(assignment: Assignment): Statement {
super.visit(assignment)
val lv = assignment.value as? NumericLiteralValue
if(lv!=null) {
// see if we can promote/convert a literal value to the required datatype
val idt = assignment.target.inferType(program, assignment)
if(!idt.isKnown)
return assignment
when(idt.typeOrElse(DataType.STRUCT)) {
DataType.UWORD -> {
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
if(lv.type== DataType.UBYTE)
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.BYTE && lv.number.toInt()>=0)
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.WORD && lv.number.toInt()>=0)
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.number.toDouble()
if(floor(d)==d && d>=0 && d<=65535)
assignment.value = NumericLiteralValue(DataType.UWORD, floor(d).toInt(), lv.position)
}
}
DataType.UBYTE -> {
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
if(lv.type== DataType.UWORD && lv.number.toInt() <= 255)
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.BYTE && lv.number.toInt() >=0)
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.number.toDouble()
if(floor(d)==d && d >=0 && d<=255)
assignment.value = NumericLiteralValue(DataType.UBYTE, floor(d).toShort(), lv.position)
}
}
DataType.BYTE -> {
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
if(lv.type== DataType.UWORD && lv.number.toInt() <= 127)
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.UBYTE && lv.number.toInt() <= 127)
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.number.toDouble()
if(floor(d)==d && d>=0 && d<=127)
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
}
}
DataType.WORD -> {
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE)
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.UWORD && lv.number.toInt() <= 32767)
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
else if(lv.type== DataType.FLOAT) {
val d = lv.number.toDouble()
if(floor(d)==d && d>=-32768 && d<=32767)
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
}
}
DataType.FLOAT -> {
assignment.value = NumericLiteralValue(DataType.FLOAT, lv.number.toDouble(), lv.position)
}
else -> {}
}
}
return assignment
}
}

View File

@ -115,7 +115,6 @@ internal class StatementOptimizer(private val program: Program,
return NopStatement.insteadOf(subroutine)
}
visitStatements(subroutine.statements)
return subroutine
}
@ -548,7 +547,6 @@ internal class StatementOptimizer(private val program: Program,
if(linesToRemove.isNotEmpty()) {
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
}
visitStatements(scope.statements)
return super.visit(scope)
}
@ -561,21 +559,6 @@ internal class StatementOptimizer(private val program: Program,
return super.visit(label)
}
private fun visitStatements(statements: MutableList<Statement>) {
// TODO remove all unreachable code statements after call to exit() or a return
// this is not yet correct because we still have nested subroutines
// val exitCallIndex = statements.indexOfFirst { it is FunctionCallStatement && it.target.nameInSource.last()=="exit" }
// if(exitCallIndex>=0) {
// while(exitCallIndex < statements.lastIndex) {
// val stmt = statements[exitCallIndex+1]
// println("after exit() removing: $stmt")
// statements.removeAt(exitCallIndex+1)
// }
// }
}
}

View File

@ -26,9 +26,6 @@ Memory Block Operations integrated in language?
array/string memory block operations?
- vector inc/dec/add/sub/mul/div...? (on array or string):
``arrayvar++ / arrayvar-- / arrayvar += 2 / arrayvar -= 2 / arrayvar *= 3 / arrayvar /= 3``
- array operations
copy (from another array with the same length), shift-N(left,right), rotate-N(left,right)
clear (set whole array to the given value, default 0)

View File

@ -6,6 +6,20 @@
main {
sub start() {
c64scr.print("ubyte shift left\n")
byte v1
byte v2
bla()
exit(4)
v1 = 100
v2 = 127
A=5
return
sub bla () {
A=99
}
}
}