From 295e199bfab3d732c08cf2d9b3a2950a5289d89e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 9 Apr 2020 00:12:50 +0200 Subject: [PATCH] optimized asm output for unneeded typecasts, fixed parent node linking issues with replaceChildNode, Assignment aug_op field is now mutable to avoid having to recreate many Assignment nodes --- compiler/src/prog8/ast/AstToplevel.kt | 2 + compiler/src/prog8/ast/base/Base.kt | 4 +- compiler/src/prog8/ast/base/Extensions.kt | 6 +- .../prog8/ast/expressions/AstExpressions.kt | 9 +++ .../src/prog8/ast/statements/AstStatements.kt | 33 ++++++-- compiler/src/prog8/compiler/Main.kt | 6 +- ...er.kt => BeforeAsmGenerationAstChanger.kt} | 76 +++++++++++-------- .../target/c64/codegen/AssignmentAsmGen.kt | 7 +- .../target/c64/codegen/ExpressionsAsmGen.kt | 6 +- .../target/c64/codegen/FunctionCallAsmGen.kt | 4 + examples/test.p8 | 12 +++ 11 files changed, 114 insertions(+), 51 deletions(-) rename compiler/src/prog8/compiler/target/{AsmVariableAndReturnsPreparer.kt => BeforeAsmGenerationAstChanger.kt} (51%) diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index d436e7f4e..46d8af973 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -233,6 +233,7 @@ class Program(val name: String, val modules: MutableList): Node { require(node is Module && replacement is Module) val idx = modules.indexOf(node) modules[idx] = replacement + replacement.parent = this } } @@ -259,6 +260,7 @@ class Module(override val name: String, require(node is Statement && replacement is Statement) val idx = statements.indexOf(node) statements[idx] = replacement + replacement.parent = this } override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)" diff --git a/compiler/src/prog8/ast/base/Base.kt b/compiler/src/prog8/ast/base/Base.kt index cfdb9e4af..fc8390c81 100644 --- a/compiler/src/prog8/ast/base/Base.kt +++ b/compiler/src/prog8/ast/base/Base.kt @@ -150,7 +150,9 @@ object ParentSentinel : Node { override val position = Position("<>", 0, 0, 0) override var parent: Node = this override fun linkParents(parent: Node) {} - override fun replaceChildNode(node: Node, replacement: Node) {} + override fun replaceChildNode(node: Node, replacement: Node) { + replacement.parent = this + } } data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) { diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index 1d85b77dd..6b61c56d1 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -4,7 +4,7 @@ import prog8.ast.Module import prog8.ast.Program import prog8.ast.processing.* import prog8.compiler.CompilationOptions -import prog8.compiler.target.AsmVariableAndReturnsPreparer +import prog8.compiler.target.BeforeAsmGenerationAstChanger import prog8.optimizer.FlattenAnonymousScopesAndNopRemover @@ -13,8 +13,8 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: Err checker.visit(this) } -internal fun Program.prepareAsmVariablesAndReturns(errors: ErrorReporter) { - val fixer = AsmVariableAndReturnsPreparer(this, errors) +internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter) { + val fixer = BeforeAsmGenerationAstChanger(this, errors) fixer.visit(this) fixer.applyModifications() } diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 261f2eb91..ee834b104 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -61,6 +61,7 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid override fun replaceChildNode(node: Node, replacement: Node) { require(node === expression && replacement is Expression) expression = replacement + replacement.parent = this } override fun constValue(program: Program): NumericLiteralValue? = null @@ -112,6 +113,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex node===right -> right = replacement else -> throw FatalAstException("invalid replace, no child $node") } + replacement.parent = this } override fun toString(): String { @@ -231,6 +233,7 @@ class ArrayIndexedExpression(var identifier: IdentifierReference, node===arrayspec.index -> arrayspec.index = replacement as Expression else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun constValue(program: Program): NumericLiteralValue? = null @@ -268,6 +271,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is Expression && node===expression) expression = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -299,6 +303,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position: override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is IdentifierReference && node===identifier) identifier = replacement + replacement.parent = this } override fun constValue(program: Program): NumericLiteralValue? = null @@ -320,6 +325,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position: override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is Expression && node===addressExpression) addressExpression = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -535,6 +541,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be require(replacement is Expression) val idx = value.indexOf(node) value[idx] = replacement + replacement.parent = this } override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) } @@ -629,6 +636,7 @@ class RangeExpr(var from: Expression, step===node -> step=replacement else -> throw FatalAstException("invalid replacement") } + replacement.parent = this } override fun constValue(program: Program): NumericLiteralValue? = null @@ -807,6 +815,7 @@ class FunctionCall(override var target: IdentifierReference, val idx = args.indexOf(node) args[idx] = replacement as Expression } + replacement.parent = this } override fun constValue(program: Program) = constValue(program, true) diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 4e512b7f0..fba7d10dc 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -48,7 +48,9 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder - override fun replaceChildNode(node: Node, replacement: Node) {} + override fun replaceChildNode(node: Node, replacement: Node) { + replacement.parent = this + } override val expensiveToInline = false } @@ -72,6 +74,7 @@ class Block(override val name: String, require(replacement is Statement) val idx = statements.indexOf(node) statements[idx] = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -139,6 +142,7 @@ open class Return(var value: Expression?, override val position: Position) : Sta override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is Expression) value = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -274,6 +278,7 @@ open class VarDecl(val type: VarDeclType, override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is Expression && node===value) value = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -324,6 +329,7 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is Expression && node===index) index = replacement + replacement.parent = this } companion object { @@ -345,7 +351,7 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node fun size() = (index as? NumericLiteralValue)?.number?.toInt() } -open class Assignment(var target: AssignTarget, val aug_op : String?, var value: Expression, override val position: Position) : Statement() { +open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() { override lateinit var parent: Node override val expensiveToInline get() = value !is NumericLiteralValue @@ -362,6 +368,7 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value: node===value -> value = replacement as Expression else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -373,8 +380,7 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value: } fun asDesugaredNonaugmented(): Assignment { - if(aug_op==null) - return this + val augmented = aug_op ?: return this val leftOperand: Expression = when { @@ -386,10 +392,10 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value: } val assignment = - if(aug_op=="setvalue") { + if(augmented=="setvalue") { Assignment(target, null, value, position) } else { - val expression = BinaryExpression(leftOperand, aug_op.substringBeforeLast('='), value, position) + val expression = BinaryExpression(leftOperand, augmented.substringBeforeLast('='), value, position) Assignment(target, null, expression, position) } assignment.linkParents(parent) @@ -418,6 +424,7 @@ data class AssignTarget(val register: Register?, node===arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression else -> throw FatalAstException("invalid replace") } + replacement.parent = this } fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -528,6 +535,7 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is AssignTarget && node===target) target = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -582,6 +590,7 @@ class FunctionCallStatement(override var target: IdentifierReference, val idx = args.indexOf(node) args[idx] = replacement as Expression } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -632,6 +641,7 @@ class AnonymousScope(override var statements: MutableList, require(replacement is Statement) val idx = statements.indexOf(node) statements[idx] = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -695,6 +705,7 @@ class Subroutine(override val name: String, require(replacement is Statement) val idx = statements.indexOf(node) statements[idx] = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -749,6 +760,7 @@ class IfStatement(var condition: Expression, node===elsepart -> elsepart = replacement as AnonymousScope else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -777,6 +789,7 @@ class BranchStatement(var condition: BranchCondition, node===elsepart -> elsepart = replacement as AnonymousScope else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -807,6 +820,7 @@ class ForLoop(val loopRegister: Register?, node===body -> body = replacement as AnonymousScope else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -842,6 +856,7 @@ class WhileLoop(var condition: Expression, node===body -> body = replacement as AnonymousScope else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -861,6 +876,7 @@ class ForeverLoop(var body: AnonymousScope, override val position: Position) : S override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is AnonymousScope && node===body) body = replacement + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -886,6 +902,7 @@ class RepeatLoop(var body: AnonymousScope, node===body -> body = replacement as AnonymousScope else -> throw FatalAstException("invalid replace") } + replacement.parent = this } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) @@ -912,6 +929,7 @@ class WhenStatement(var condition: Expression, val idx = choices.indexOf(node) choices[idx] = replacement as WhenChoice } + replacement.parent = this } fun choiceValues(program: Program): List?, WhenChoice>> { @@ -950,6 +968,7 @@ class WhenChoice(var values: List?, // if null, this is t override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is AnonymousScope && node===statements) statements = replacement + replacement.parent = this } override fun toString(): String { @@ -978,6 +997,7 @@ class StructDecl(override val name: String, require(replacement is Statement) val idx = statements.indexOf(node) statements[idx] = replacement + replacement.parent = this } val numberOfElements: Int @@ -1001,6 +1021,7 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position override fun replaceChildNode(node: Node, replacement: Node) { require(replacement is Expression && node===addressExpression) addressExpression = replacement + replacement.parent = this } override fun toString(): String { diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 24923e1ff..c363496ae 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -41,7 +41,7 @@ fun compileProgram(filepath: Path, optimizeAst(programAst, errors) postprocessAst(programAst, errors, compilationOptions) - printAst(programAst) // TODO + // printAst(programAst) // TODO if(writeAssembly) programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions) @@ -186,9 +186,11 @@ 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.prepareAsmVariablesAndReturns(errors) + programAst.processAstBeforeAsmGeneration(errors) errors.handle() + // printAst(programAst) // TODO + val assembly = CompilationTarget.asmGenerator( programAst, errors, diff --git a/compiler/src/prog8/compiler/target/AsmVariableAndReturnsPreparer.kt b/compiler/src/prog8/compiler/target/BeforeAsmGenerationAstChanger.kt similarity index 51% rename from compiler/src/prog8/compiler/target/AsmVariableAndReturnsPreparer.kt rename to compiler/src/prog8/compiler/target/BeforeAsmGenerationAstChanger.kt index 782da5eb2..c0113adf8 100644 --- a/compiler/src/prog8/compiler/target/AsmVariableAndReturnsPreparer.kt +++ b/compiler/src/prog8/compiler/target/BeforeAsmGenerationAstChanger.kt @@ -2,20 +2,20 @@ package prog8.compiler.target import prog8.ast.Node import prog8.ast.Program -import prog8.ast.base.ErrorReporter -import prog8.ast.base.NumericDatatypes -import prog8.ast.base.VarDeclType +import prog8.ast.base.* import prog8.ast.expressions.BinaryExpression +import prog8.ast.expressions.Expression import prog8.ast.expressions.IdentifierReference +import prog8.ast.expressions.TypecastExpression import prog8.ast.processing.AstWalker import prog8.ast.processing.IAstModification import prog8.ast.statements.* -class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorReporter): AstWalker() { +class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() { override fun after(decl: VarDecl, parent: Node): Iterable { - if(decl.value==null && decl.type==VarDeclType.VAR && decl.datatype in NumericDatatypes) { + if (decl.value == null && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) { // a numeric vardecl without an initial value is initialized with zero. decl.value = decl.zeroElementValue() } @@ -25,27 +25,27 @@ class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorRepor override fun after(scope: AnonymousScope, parent: Node): Iterable { val decls = scope.statements.filterIsInstance() val sub = scope.definingSubroutine() - if(sub!=null) { + if (sub != null) { val existingVariables = sub.statements.filterIsInstance().associateBy { it.name } var conflicts = false decls.forEach { val existing = existingVariables[it.name] - if (existing!=null) { + if (existing != null) { errors.err("variable ${it.name} already defined in subroutine ${sub.name} at ${existing.position}", it.position) conflicts = true } } - if(!conflicts) { - val numericVarsWithValue = decls.filter { it.value!=null && it.datatype in NumericDatatypes } + if (!conflicts) { + val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes } return numericVarsWithValue.map { - val initValue = it.value!! // assume here that value has always been set by now - it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous) - val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position) - val assign = Assignment(target, null, initValue, it.position) - IAstModification.InsertFirst(assign, scope) - } + - decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } + - decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine + val initValue = it.value!! // assume here that value has always been set by now + it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous) + val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position) + val assign = Assignment(target, null, initValue, it.position) + initValue.parent = assign + IAstModification.InsertFirst(assign, scope) + } + decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } + + decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine } } return emptyList() @@ -56,10 +56,10 @@ class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorRepor // and if an assembly block doesn't contain a rts/rti, and some other situations. val mods = mutableListOf() val returnStmt = Return(null, subroutine.position) - if(subroutine.asmAddress==null + if (subroutine.asmAddress == null && subroutine.statements.isNotEmpty() - && subroutine.amountOfRtsInAsm()==0 - && subroutine.statements.lastOrNull {it !is VarDecl } !is Return + && subroutine.amountOfRtsInAsm() == 0 + && subroutine.statements.lastOrNull { it !is VarDecl } !is Return && subroutine.statements.last() !is Subroutine) { mods += IAstModification.InsertLast(returnStmt, subroutine) } @@ -68,31 +68,45 @@ class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorRepor val outerScope = subroutine.definingScope() val outerStatements = outerScope.statements val subroutineStmtIdx = outerStatements.indexOf(subroutine) - if(subroutineStmtIdx>0 - && outerStatements[subroutineStmtIdx-1] !is Jump - && outerStatements[subroutineStmtIdx-1] !is Subroutine - && outerStatements[subroutineStmtIdx-1] !is Return + if (subroutineStmtIdx > 0 + && outerStatements[subroutineStmtIdx - 1] !is Jump + && outerStatements[subroutineStmtIdx - 1] !is Subroutine + && outerStatements[subroutineStmtIdx - 1] !is Return && outerScope !is Block) { - mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx-1], returnStmt, outerScope as Node) + mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node) } return mods } - override fun after(assignment: Assignment, parent: Node): Iterable { + override fun before(assignment: Assignment, parent: Node): Iterable { // modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments // also to put code generation stuff together, single value assignment (A = 5) is converted to a special // augmented form as wel (with the operator "setvalue") - if(assignment.aug_op==null) { + if (assignment.aug_op == null) { val binExpr = assignment.value as? BinaryExpression if (binExpr != null) { if (assignment.target.isSameAs(binExpr.left)) { - val augAssignment = Assignment(assignment.target, binExpr.operator + "=", binExpr.right, assignment.position) - return listOf(IAstModification.ReplaceNode(assignment, augAssignment, parent)) + assignment.value = binExpr.right + assignment.aug_op = binExpr.operator + "=" + assignment.value.parent = assignment + return emptyList() } } - val augAssignment = Assignment(assignment.target, "setvalue", assignment.value, assignment.position) - return listOf(IAstModification.ReplaceNode(assignment, augAssignment, parent)) + assignment.aug_op = "setvalue" + } + return emptyList() + } + + override fun after(typecast: TypecastExpression, parent: Node): Iterable { + // see if we can remove superfluous typecasts (outside of expressions) + // such as casting byte<->ubyte, word<->uword + val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.STRUCT) + if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes + || typecast.type in WordDatatypes && sourceDt in WordDatatypes) { + if(typecast.parent !is Expression) { + return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent)) + } } return emptyList() } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt index 221127058..32b2dcf6f 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AssignmentAsmGen.kt @@ -835,12 +835,6 @@ internal class AssignmentAsmGen(private val program: Program, private val errors assignFromEvalResult(normalAssign.target) } else { when (assign.value) { - is TypecastExpression -> { - // TODO optimize assignment of typecast value? - val normalAssign = assign.asDesugaredNonaugmented() - asmgen.translateExpression(normalAssign.value) - assignFromEvalResult(normalAssign.target) - } is FunctionCall -> { // TODO is there a way to avoid function return value being passed via the stack? // for instance, 1 byte return value always in A, etc @@ -860,6 +854,7 @@ internal class AssignmentAsmGen(private val program: Program, private val errors // old code-generation below: + // eventually, all of this should have been replaced by newer more optimized code above. private fun translateNormalAssignment(assign: Assignment) { require(assign.aug_op == null) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index 917e4a2bd..17d6c7fe5 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -320,8 +320,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge // the general, non-optimized cases translateExpression(expr.left) translateExpression(expr.right) - if(leftDt!=rightDt) - throw AssemblyError("binary operator ${expr.operator} left/right dt not identical") // is this strictly required always? + if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes) + || (leftDt in WordDatatypes && rightDt !in WordDatatypes)) + throw AssemblyError("binary operator ${expr.operator} left/right dt not identical") + when (leftDt) { in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt) in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index 5d756b5c7..ef03f4abd 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -225,6 +225,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { if(argType isAssignableTo paramType) return true + if(argType in ByteDatatypes && paramType in ByteDatatypes) + return true + if(argType in WordDatatypes && paramType in WordDatatypes) + return true // we have a special rule for some types. // strings are assignable to UWORD, for example, and vice versa diff --git a/examples/test.p8 b/examples/test.p8 index 45686d912..f728d4a14 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,10 +1,22 @@ %import c64lib %import c64utils +%import c64flt %zeropage basicsafe main { sub start() { + ubyte ubb = 44 + byte bbb=44 + uword uww = 4444 + word www = 4444 + float flt = 4.4 + + A = ubb + A = ubb as byte + A = bbb + A = bbb as ubyte + str foo = "foo" } }