This commit is contained in:
Irmen de Jong 2020-07-03 22:09:44 +02:00
parent a6d789cfbc
commit b6e2b36692
9 changed files with 136 additions and 126 deletions

View File

@ -6,7 +6,6 @@ import prog8.ast.processing.*
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.BeforeAsmGenerationAstChanger import prog8.compiler.BeforeAsmGenerationAstChanger
import prog8.optimizer.AssignmentTransformer import prog8.optimizer.AssignmentTransformer
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) { internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
@ -38,10 +37,9 @@ internal fun Program.addTypecasts(errors: ErrorReporter) {
caster.applyModifications() caster.applyModifications()
} }
internal fun Program.simplifyNumericCasts() { internal fun Program.verifyFunctionArgTypes() {
val fixer = TypecastsSimplifier(this) val fixer = VerifyFunctionArgTypes(this)
fixer.visit(this) fixer.visit(this)
fixer.applyModifications()
} }
internal fun Program.transformAssignments(errors: ErrorReporter) { internal fun Program.transformAssignments(errors: ErrorReporter) {
@ -83,7 +81,8 @@ internal fun Program.checkIdentifiers(errors: ErrorReporter) {
} }
} }
internal fun Program.removeNopsFlattenAnonScopes() { internal fun Program.variousCleanups() {
val flattener = FlattenAnonymousScopesAndNopRemover() val process = VariousCleanups()
flattener.visit(this) process.visit(this)
process.applyModifications()
} }

View File

@ -7,7 +7,6 @@ import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.CompilerException
import prog8.functions.BuiltinFunctions import prog8.functions.BuiltinFunctions
@ -247,52 +246,3 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
return noModifications 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<IAstModification>()
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
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<IAstModification>
= checkCallArgTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification>
= checkCallArgTypes(functionCall as IFunctionCall, functionCall.definingScope())
private fun checkCallArgTypes(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
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
}
}

View File

@ -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<IAstModification>()
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.Remove(nopStatement, parent))
}
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
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<IAstModification> {
if(typecast.expression is NumericLiteralValue) {
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
}
return noModifications
}
}

View File

@ -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 -> {}
}
}
}

View File

@ -146,10 +146,10 @@ private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptio
errors.handle() errors.handle()
programAst.constantFold(errors) programAst.constantFold(errors)
errors.handle() errors.handle()
programAst.removeNopsFlattenAnonScopes()
programAst.reorderStatements() programAst.reorderStatements()
programAst.addTypecasts(errors) programAst.addTypecasts(errors)
errors.handle() errors.handle()
programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors) programAst.checkValid(compilerOptions, errors)
errors.handle() errors.handle()
programAst.checkIdentifiers(errors) programAst.checkIdentifiers(errors)
@ -180,12 +180,12 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
errors.handle() errors.handle()
programAst.addTypecasts(errors) programAst.addTypecasts(errors)
errors.handle() errors.handle()
programAst.simplifyNumericCasts() programAst.variousCleanups()
programAst.removeNopsFlattenAnonScopes()
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
errors.handle() errors.handle()
programAst.checkRecursion(errors) // check if there are recursive subroutine calls programAst.checkRecursion(errors) // check if there are recursive subroutine calls
errors.handle() errors.handle()
programAst.verifyFunctionArgTypes()
} }
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path, private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,

View File

@ -364,11 +364,18 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
} }
is IdentifierReference -> { is IdentifierReference -> {
val sourceName = asmgen.asmIdentifierName(assign.value as 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 -> { is DirectMemoryRead -> {
val memory = (assign.value as DirectMemoryRead).addressExpression.constValue(program)!!.number.toHex() 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 return true
} }
is ArrayIndexedExpression -> { is ArrayIndexedExpression -> {

View File

@ -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<INameScope>()
private val nopStatements = mutableListOf<NopStatement>()
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)
}
}

View File

@ -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 { main {
@ -112,6 +112,7 @@ waitkey:
break break
} }
} }
if dropypos>ypos { if dropypos>ypos {
ypos = dropypos ypos = dropypos
sound.blockdrop() sound.blockdrop()
@ -548,6 +549,7 @@ blocklogic {
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte { sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
ubyte i ubyte i
for i in 15 downto 0 { 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 if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
return false return false
} }

View File

@ -5,30 +5,43 @@
%option enable_floats %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 { main {
sub start() { sub start() {
ubyte xx = 10 function(20, calculate())
float ff = 4 asmfunction(20, calculate())
float ff2 = 4
; xx /= 2 c64.CHROUT('\n')
;
; xx /= 3
;
; xx *= 2
; xx *= 3
;ff **= 2.0 if @($0400)==@($0402) and @($0401) == @($0403) {
;ff **= 3.0 c64scr.print("ok: results are same\n")
} else {
ff = ff2 ** 2.0 c64scr.print("error: result differ; arg got clobbered\n")
ff = ff2 ** 3.0 }
; xx = xx % 5
; xx %= 5
} }
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
}
} }