From b6e2b3669283ee463a475ecbea792cbe05a67b3a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 3 Jul 2020 22:09:44 +0200 Subject: [PATCH] refactor --- compiler/src/prog8/ast/base/Extensions.kt | 13 +++-- .../prog8/ast/processing/TypecastsAdder.kt | 50 ------------------- .../prog8/ast/processing/VariousCleanups.kt | 43 ++++++++++++++++ .../ast/processing/VerifyFunctionArgTypes.kt | 42 ++++++++++++++++ compiler/src/prog8/compiler/Main.kt | 6 +-- .../target/c64/codegen/AssignmentAsmGen.kt | 11 +++- .../FlattenAnonymousScopesAndNopRemover.kt | 46 ----------------- examples/tehtriz.p8 | 4 +- examples/test.p8 | 47 ++++++++++------- 9 files changed, 136 insertions(+), 126 deletions(-) create mode 100644 compiler/src/prog8/ast/processing/VariousCleanups.kt create mode 100644 compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt delete mode 100644 compiler/src/prog8/optimizer/FlattenAnonymousScopesAndNopRemover.kt diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index f1285926c..4a864e14c 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -6,7 +6,6 @@ import prog8.ast.processing.* import prog8.compiler.CompilationOptions import prog8.compiler.BeforeAsmGenerationAstChanger import prog8.optimizer.AssignmentTransformer -import prog8.optimizer.FlattenAnonymousScopesAndNopRemover internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) { @@ -38,10 +37,9 @@ internal fun Program.addTypecasts(errors: ErrorReporter) { caster.applyModifications() } -internal fun Program.simplifyNumericCasts() { - val fixer = TypecastsSimplifier(this) +internal fun Program.verifyFunctionArgTypes() { + val fixer = VerifyFunctionArgTypes(this) fixer.visit(this) - fixer.applyModifications() } internal fun Program.transformAssignments(errors: ErrorReporter) { @@ -83,7 +81,8 @@ internal fun Program.checkIdentifiers(errors: ErrorReporter) { } } -internal fun Program.removeNopsFlattenAnonScopes() { - val flattener = FlattenAnonymousScopesAndNopRemover() - flattener.visit(this) +internal fun Program.variousCleanups() { + val process = VariousCleanups() + process.visit(this) + process.applyModifications() } diff --git a/compiler/src/prog8/ast/processing/TypecastsAdder.kt b/compiler/src/prog8/ast/processing/TypecastsAdder.kt index 3f5fe465f..dba9d750b 100644 --- a/compiler/src/prog8/ast/processing/TypecastsAdder.kt +++ b/compiler/src/prog8/ast/processing/TypecastsAdder.kt @@ -7,7 +7,6 @@ import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.statements.* -import prog8.compiler.CompilerException import prog8.functions.BuiltinFunctions @@ -247,52 +246,3 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke return noModifications } } - - - -class TypecastsSimplifier(val program: Program) : AstWalker() { - /* - * Typecasts of a numeric literal value can be replaced by the numeric value of the type directly. - */ - - private val noModifications = emptyList() - - override fun before(typecast: TypecastExpression, parent: Node): Iterable { - if(typecast.expression is NumericLiteralValue) { - val value = (typecast.expression as NumericLiteralValue).cast(typecast.type) - return listOf(IAstModification.ReplaceNode(typecast, value, parent)) - } - - return noModifications - } - - override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable - = checkCallArgTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope()) - - override fun after(functionCall: FunctionCall, parent: Node): Iterable - = checkCallArgTypes(functionCall as IFunctionCall, functionCall.definingScope()) - - private fun checkCallArgTypes(call: IFunctionCall, scope: INameScope): Iterable { - val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) } - val target = call.target.targetStatement(scope) - when(target) { - is Subroutine -> { - val paramtypes = target.parameters.map { it.type } - if(argtypes!=paramtypes) - throw CompilerException("parameter type mismatch $call") - } - is BuiltinFunctionStatementPlaceholder -> { - val func = BuiltinFunctions.getValue(target.name) - val paramtypes = func.parameters.map { it.possibleDatatypes } - for(x in argtypes.zip(paramtypes)) { - if(x.first !in x.second) - throw CompilerException("parameter type mismatch $call") - } - } - else -> {} - } - println("**** $target") - return noModifications - } - -} diff --git a/compiler/src/prog8/ast/processing/VariousCleanups.kt b/compiler/src/prog8/ast/processing/VariousCleanups.kt new file mode 100644 index 000000000..46e82d896 --- /dev/null +++ b/compiler/src/prog8/ast/processing/VariousCleanups.kt @@ -0,0 +1,43 @@ +package prog8.ast.processing + +import prog8.ast.INameScope +import prog8.ast.Node +import prog8.ast.expressions.NumericLiteralValue +import prog8.ast.expressions.TypecastExpression +import prog8.ast.statements.AnonymousScope +import prog8.ast.statements.NopStatement + + +internal class VariousCleanups: AstWalker() { + private val noModifications = emptyList() + + override fun before(nopStatement: NopStatement, parent: Node): Iterable { + return listOf(IAstModification.Remove(nopStatement, parent)) + } + + override fun before(scope: AnonymousScope, parent: Node): Iterable { + return if(parent is INameScope) + listOf(ScopeFlatten(scope, parent as INameScope)) + else + noModifications + } + + class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification { + override fun perform() { + val idx = into.statements.indexOf(scope) + if(idx>=0) { + into.statements.addAll(idx+1, scope.statements) + into.statements.remove(scope) + } + } + } + + override fun before(typecast: TypecastExpression, parent: Node): Iterable { + if(typecast.expression is NumericLiteralValue) { + val value = (typecast.expression as NumericLiteralValue).cast(typecast.type) + return listOf(IAstModification.ReplaceNode(typecast, value, parent)) + } + + return noModifications + } +} diff --git a/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt new file mode 100644 index 000000000..cf6c09d2c --- /dev/null +++ b/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt @@ -0,0 +1,42 @@ +package prog8.ast.processing + +import prog8.ast.IFunctionCall +import prog8.ast.INameScope +import prog8.ast.Program +import prog8.ast.base.DataType +import prog8.ast.expressions.FunctionCall +import prog8.ast.statements.BuiltinFunctionStatementPlaceholder +import prog8.ast.statements.FunctionCallStatement +import prog8.ast.statements.Subroutine +import prog8.compiler.CompilerException +import prog8.functions.BuiltinFunctions + +class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { + + override fun visit(functionCall: FunctionCall) + = checkTypes(functionCall as IFunctionCall, functionCall.definingScope()) + + override fun visit(functionCallStatement: FunctionCallStatement) + = checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope()) + + private fun checkTypes(call: IFunctionCall, scope: INameScope) { + val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) } + val target = call.target.targetStatement(scope) + when(target) { + is Subroutine -> { + val paramtypes = target.parameters.map { it.type } + if(argtypes!=paramtypes) + throw CompilerException("parameter type mismatch $call") + } + is BuiltinFunctionStatementPlaceholder -> { + val func = BuiltinFunctions.getValue(target.name) + val paramtypes = func.parameters.map { it.possibleDatatypes } + for(x in argtypes.zip(paramtypes)) { + if(x.first !in x.second) + throw CompilerException("parameter type mismatch $call") + } + } + else -> {} + } + } +} diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index c39317dfa..510d7f513 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -146,10 +146,10 @@ private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptio errors.handle() programAst.constantFold(errors) errors.handle() - programAst.removeNopsFlattenAnonScopes() programAst.reorderStatements() programAst.addTypecasts(errors) errors.handle() + programAst.variousCleanups() programAst.checkValid(compilerOptions, errors) errors.handle() programAst.checkIdentifiers(errors) @@ -180,12 +180,12 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO errors.handle() programAst.addTypecasts(errors) errors.handle() - programAst.simplifyNumericCasts() - programAst.removeNopsFlattenAnonScopes() + programAst.variousCleanups() programAst.checkValid(compilerOptions, errors) // check if final tree is still valid errors.handle() programAst.checkRecursion(errors) // check if there are recursive subroutine calls errors.handle() + programAst.verifyFunctionArgTypes() } private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path, diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 630c9fb01..0e4e6636f 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -364,11 +364,18 @@ internal class AssignmentAsmGen(private val program: Program, private val errors } is IdentifierReference -> { val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference) - TODO("membyte = variable $assign") + when(assign.aug_op) { + "setvalue" -> asmgen.out(" lda $sourceName | sta $hexAddr") + else -> TODO("membyte aug.assign variable $assign") + } + return true } is DirectMemoryRead -> { val memory = (assign.value as DirectMemoryRead).addressExpression.constValue(program)!!.number.toHex() - asmgen.out(" lda $memory | sta $hexAddr") + when(assign.aug_op) { + "setvalue" -> asmgen.out(" lda $memory | sta $hexAddr") + else -> TODO("membyte aug.assign memread $assign") + } return true } is ArrayIndexedExpression -> { diff --git a/compiler/src/prog8/optimizer/FlattenAnonymousScopesAndNopRemover.kt b/compiler/src/prog8/optimizer/FlattenAnonymousScopesAndNopRemover.kt deleted file mode 100644 index ecdcb5ef7..000000000 --- a/compiler/src/prog8/optimizer/FlattenAnonymousScopesAndNopRemover.kt +++ /dev/null @@ -1,46 +0,0 @@ -package prog8.optimizer - -import prog8.ast.INameScope -import prog8.ast.Node -import prog8.ast.Program -import prog8.ast.processing.IAstVisitor -import prog8.ast.statements.AnonymousScope -import prog8.ast.statements.NopStatement -import prog8.ast.statements.Statement - -internal class FlattenAnonymousScopesAndNopRemover: IAstVisitor { - private var scopesToFlatten = mutableListOf() - private val nopStatements = mutableListOf() - - override fun visit(program: Program) { - super.visit(program) - for(scope in scopesToFlatten.reversed()) { - val namescope = scope.parent as INameScope - val idx = namescope.statements.indexOf(scope as Statement) - if(idx>=0) { - val nop = NopStatement.insteadOf(namescope.statements[idx]) - nop.parent = namescope as Node - namescope.statements[idx] = nop - namescope.statements.addAll(idx, scope.statements) - scope.statements.forEach { it.parent = namescope } - visit(nop) - } - } - - this.nopStatements.forEach { - it.definingScope().remove(it) - } - } - - override fun visit(scope: AnonymousScope) { - if(scope.parent is INameScope) { - scopesToFlatten.add(scope) // get rid of the anonymous scope - } - - return super.visit(scope) - } - - override fun visit(nopStatement: NopStatement) { - nopStatements.add(nopStatement) - } -} diff --git a/examples/tehtriz.p8 b/examples/tehtriz.p8 index e0538edb1..76f593856 100644 --- a/examples/tehtriz.p8 +++ b/examples/tehtriz.p8 @@ -9,7 +9,7 @@ -; TODO fix wrong block behavior at bottom when compiled without optimizations (codegen issue). +; TODO fix noCollision() at bottom when compiled without optimizations (codegen issue). main { @@ -112,6 +112,7 @@ waitkey: break } } + if dropypos>ypos { ypos = dropypos sound.blockdrop() @@ -548,6 +549,7 @@ blocklogic { sub noCollision(ubyte xpos, ubyte ypos) -> ubyte { ubyte i for i in 15 downto 0 { + ; TODO FIX THIS when compiling without optimizations (codegen problem: clobbering register arguments): if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32 return false } diff --git a/examples/test.p8 b/examples/test.p8 index c2dbf2a6c..e9fcfec45 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,30 +5,43 @@ %option enable_floats +; TODO: fix register argument clobbering when calling asmsubs. +; for instance if the first arg goes into Y, and the second in A, +; but when calculating the second argument clobbers Y, the first argument gets destroyed. + main { sub start() { - ubyte xx = 10 - float ff = 4 - float ff2 = 4 + function(20, calculate()) + asmfunction(20, calculate()) -; xx /= 2 -; -; xx /= 3 -; -; xx *= 2 -; xx *= 3 + c64.CHROUT('\n') - ;ff **= 2.0 - ;ff **= 3.0 - - ff = ff2 ** 2.0 - ff = ff2 ** 3.0 - -; xx = xx % 5 -; xx %= 5 + if @($0400)==@($0402) and @($0401) == @($0403) { + c64scr.print("ok: results are same\n") + } else { + c64scr.print("error: result differ; arg got clobbered\n") + } } + sub function(ubyte a1, ubyte a2) { + ; non-asm function passes via stack, this is ok + @($0400) = a1 + @($0401) = a2 + } + + asmsub asmfunction(ubyte a1 @ Y, ubyte a2 @ A) { + ; asm-function passes via registers, risk of clobbering + %asm {{ + sty $0402 + sta $0403 + }} + } + + sub calculate() -> ubyte { + Y = 99 + return Y + } }