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.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()
}

View File

@ -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<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()
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,

View File

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

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

View File

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