From 2f18a8f6d0892d3c8a1902bc99e69213db3eab55 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 20 Feb 2022 02:01:48 +0100 Subject: [PATCH] introduced BuiltinFunctionCall (expression) node for codegen --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 8 +- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 6 +- .../codegen/cpu6502/ExpressionsAsmGen.kt | 168 +++++++++--------- .../codegen/cpu6502/FunctionCallAsmGen.kt | 2 +- .../cpu6502/assignment/AsmAssignment.kt | 26 ++- .../cpu6502/assignment/AssignmentAsmGen.kt | 148 ++++++++------- .../prog8/codegen/target/cbm/AsmsubHelpers.kt | 8 +- .../astprocessing/BeforeAsmAstChanger.kt | 37 ---- .../astprocessing/BeforeAsmTypecastCleaner.kt | 62 ++++++- .../src/prog8/ast/AstToSourceTextConverter.kt | 4 + .../prog8/ast/expressions/AstExpressions.kt | 40 ++++- compilerAst/src/prog8/ast/walk/AstWalker.kt | 9 + compilerAst/src/prog8/ast/walk/IAstVisitor.kt | 5 + .../compilerinterface/BuiltinFunctions.kt | 3 + docs/source/todo.rst | 1 - 15 files changed, 289 insertions(+), 238 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 7486a0506..358a3e53d 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -443,8 +443,8 @@ class AsmGen(internal val program: Program, internal fun translateExpression(expression: Expression) = expressionsAsmGen.translateExpression(expression) - internal fun translateBuiltinFunctionCallExpression(functionCallExpr: FunctionCallExpression, resultToStack: Boolean, resultRegister: RegisterOrPair?) = - builtinFunctionsAsmGen.translateFunctioncallExpression(functionCallExpr, resultToStack, resultRegister) + internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) = + builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister) internal fun translateBuiltinFunctionCallExpression(name: String, args: List, scope: Subroutine): DataType = builtinFunctionsAsmGen.translateFunctioncall(name, args, false, scope) @@ -1200,7 +1200,7 @@ $repeatLabel lda $counterVar } if(dt==DataType.UBYTE) { assignExpressionToRegister(left, RegisterOrPair.A, false) - if (left is FunctionCallExpression && !left.isSimple) + if (left is IFunctionCall && !left.isSimple) out(" cmp #0") } else { assignExpressionToRegister(left, RegisterOrPair.AY, false) @@ -1216,7 +1216,7 @@ $repeatLabel lda $counterVar } DataType.BYTE -> { assignExpressionToRegister(left, RegisterOrPair.A, true) - if (left is FunctionCallExpression && !left.isSimple) + if (left is IFunctionCall && !left.isSimple) out(" cmp #0") when (operator) { "==" -> out(" bne $jumpIfFalseLabel") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 307da81ba..57468e5b8 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -22,7 +22,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val assignAsmGen: AssignmentAsmGen, private val allocations: VariableAllocator) { - internal fun translateFunctioncallExpression(fcall: FunctionCallExpression, resultToStack: Boolean, resultRegister: RegisterOrPair?) { + internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single()) translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) } @@ -57,7 +57,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } } }.toMutableList() - val fcall = FunctionCallExpression(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY) + val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY) fcall.linkParents(scope) translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null) return if(isStatement) DataType.UNDEFINED else func.known_returntype!! @@ -352,7 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, } private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { - if(discardResult || fcall !is FunctionCallExpression) + if(discardResult || fcall !is BuiltinFunctionCall) throw AssemblyError("should not discard result of memory allocation at $fcall") val name = (fcall.args[0] as StringLiteral).value require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"} diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index a724ec863..c67e8529d 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -3,8 +3,6 @@ package prog8.codegen.cpu6502 import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.statements.BuiltinFunctionPlaceholder -import prog8.ast.statements.Subroutine import prog8.ast.toHex import prog8.compilerinterface.AssemblyError import prog8.compilerinterface.CpuType @@ -38,6 +36,7 @@ internal class ExpressionsAsmGen(private val program: Program, is NumericLiteral -> translateExpression(expression) is IdentifierReference -> translateExpression(expression) is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression) + is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null) is PipeExpression -> asmgen.translatePipeExpression(expression.expressions, expression,false, true) is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported") is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable") @@ -50,95 +49,90 @@ internal class ExpressionsAsmGen(private val program: Program, private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) { // only for use in nested expression evaluation - val sub = call.target.targetStatement(program) - if(sub is BuiltinFunctionPlaceholder) { - asmgen.translateBuiltinFunctionCallExpression(call, true, null) - } else { - sub as Subroutine - asmgen.saveXbeforeCall(call) - asmgen.translateFunctionCall(call, true) - if(sub.regXasResult()) { - // store the return value in X somewhere that we can acces again below - asmgen.out(" stx P8ZP_SCRATCH_REG") - } - asmgen.restoreXafterCall(call) + val sub = call.target.targetSubroutine(program)!! + asmgen.saveXbeforeCall(call) + asmgen.translateFunctionCall(call, true) + if(sub.regXasResult()) { + // store the return value in X somewhere that we can access again below + asmgen.out(" stx P8ZP_SCRATCH_REG") + } + asmgen.restoreXafterCall(call) - val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) - for ((_, reg) in returns) { - // result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree) - if (reg.registerOrPair != null) { - when (reg.registerOrPair!!) { - RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex") - RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex") - RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") - RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex") - RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex") - RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex") - RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1") - RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2") - RegisterOrPair.R0, - RegisterOrPair.R1, - RegisterOrPair.R2, - RegisterOrPair.R3, - RegisterOrPair.R4, - RegisterOrPair.R5, - RegisterOrPair.R6, - RegisterOrPair.R7, - RegisterOrPair.R8, - RegisterOrPair.R9, - RegisterOrPair.R10, - RegisterOrPair.R11, - RegisterOrPair.R12, - RegisterOrPair.R13, - RegisterOrPair.R14, - RegisterOrPair.R15 -> { - asmgen.out( - """ - lda cx16.${reg.registerOrPair.toString().lowercase()} - sta P8ESTACK_LO,x - lda cx16.${reg.registerOrPair.toString().lowercase()}+1 - sta P8ESTACK_HI,x - dex - """) - } - } - } else when(reg.statusflag) { - Statusflag.Pc -> { - asmgen.out(""" - lda #0 - rol a + val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) + for ((_, reg) in returns) { + // result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree) + if (reg.registerOrPair != null) { + when (reg.registerOrPair!!) { + RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex") + RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex") + RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") + RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex") + RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex") + RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex") + RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1") + RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2") + RegisterOrPair.R0, + RegisterOrPair.R1, + RegisterOrPair.R2, + RegisterOrPair.R3, + RegisterOrPair.R4, + RegisterOrPair.R5, + RegisterOrPair.R6, + RegisterOrPair.R7, + RegisterOrPair.R8, + RegisterOrPair.R9, + RegisterOrPair.R10, + RegisterOrPair.R11, + RegisterOrPair.R12, + RegisterOrPair.R13, + RegisterOrPair.R14, + RegisterOrPair.R15 -> { + asmgen.out( + """ + lda cx16.${reg.registerOrPair.toString().lowercase()} sta P8ESTACK_LO,x - dex""") + lda cx16.${reg.registerOrPair.toString().lowercase()}+1 + sta P8ESTACK_HI,x + dex + """) } - Statusflag.Pz -> { - asmgen.out(""" - beq + - lda #0 - beq ++ -+ lda #1 -+ sta P8ESTACK_LO,x - dex""") - } - Statusflag.Pv -> { - asmgen.out(""" - bvs + - lda #0 - beq ++ -+ lda #1 -+ sta P8ESTACK_LO,x - dex""") - } - Statusflag.Pn -> { - asmgen.out(""" - bmi + - lda #0 - beq ++ -+ lda #1 -+ sta P8ESTACK_LO,x - dex""") - } - null -> {} } + } else when(reg.statusflag) { + Statusflag.Pc -> { + asmgen.out(""" + lda #0 + rol a + sta P8ESTACK_LO,x + dex""") + } + Statusflag.Pz -> { + asmgen.out(""" + beq + + lda #0 + beq ++ ++ lda #1 ++ sta P8ESTACK_LO,x + dex""") + } + Statusflag.Pv -> { + asmgen.out(""" + bvs + + lda #0 + beq ++ ++ lda #1 ++ sta P8ESTACK_LO,x + dex""") + } + Statusflag.Pn -> { + asmgen.out(""" + bmi + + lda #0 + beq ++ ++ lda #1 ++ sta P8ESTACK_LO,x + dex""") + } + null -> {} } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index a37ccc1fa..028b2a602 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -18,7 +18,7 @@ import prog8.compilerinterface.AssemblyError internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { - internal fun translateFunctionCallStatement(stmt: IFunctionCall) { + internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) { saveXbeforeCall(stmt) translateFunctionCall(stmt, false) restoreXafterCall(stmt) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index 1e654f911..4c4c3598d 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -167,26 +167,20 @@ internal class AsmAssignSource(val kind: SourceStorageKind, val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") } AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value) } + is BuiltinFunctionCall -> { + val returnType = value.inferType(program) + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value) + } is FunctionCallExpression -> { - when (val sub = value.target.targetStatement(program)) { - is Subroutine -> { - val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first - ?: throw AssemblyError("can't translate zero return values in assignment") + val sub = value.target.targetSubroutine(program)!! + val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first + ?: throw AssemblyError("can't translate zero return values in assignment") - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) - } - is BuiltinFunctionPlaceholder -> { - val returnType = value.inferType(program) - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value) - } - else -> { - throw AssemblyError("weird call") - } - } + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) } else -> { - val dt = value.inferType(program) - AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.getOrElse { throw AssemblyError("unknown dt") }, expression = value) + val returnType = value.inferType(program) + AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value) } } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 67dc878ad..c03673be9 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -161,75 +161,74 @@ internal class AssignmentAsmGen(private val program: Program, is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value) is FunctionCallExpression -> { - when (val sub = value.target.targetStatement(program)) { - is Subroutine -> { - asmgen.saveXbeforeCall(value) - asmgen.translateFunctionCall(value, true) - val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?: - sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null } - when (returnValue.first) { - DataType.STR -> { - asmgen.restoreXafterCall(value) - when(assign.target.datatype) { - DataType.UWORD -> { - // assign the address of the string result value - assignRegisterpairWord(assign.target, RegisterOrPair.AY) - } - DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> { - // copy the actual string result into the target string variable - asmgen.out(""" - pha - lda #<${assign.target.asmVarname} - sta P8ZP_SCRATCH_W1 - lda #>${assign.target.asmVarname} - sta P8ZP_SCRATCH_W1+1 - pla - jsr prog8_lib.strcpy""") - } - else -> throw AssemblyError("weird target dt") - } + val sub = value.target.targetSubroutine(program)!! + asmgen.saveXbeforeCall(value) + asmgen.translateFunctionCall(value, true) + val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?: + sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null } + when (returnValue.first) { + DataType.STR -> { + asmgen.restoreXafterCall(value) + when(assign.target.datatype) { + DataType.UWORD -> { + // assign the address of the string result value + assignRegisterpairWord(assign.target, RegisterOrPair.AY) } - DataType.FLOAT -> { - // float result from function sits in FAC1 - asmgen.restoreXafterCall(value) - assignFAC1float(assign.target) - } - else -> { - // do NOT restore X register before assigning the result values first - when (returnValue.second.registerOrPair) { - RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A) - RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X) - RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y) - RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX) - RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) - RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY) - else -> { - val sflag = returnValue.second.statusflag - if(sflag!=null) - assignStatusFlagByte(assign.target, sflag) - else - throw AssemblyError("should be just one register byte result value") - } - } - // we've processed the result value in the X register by now, so it's now finally safe to restore it - asmgen.restoreXafterCall(value) + DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> { + // copy the actual string result into the target string variable + asmgen.out(""" + pha + lda #<${assign.target.asmVarname} + sta P8ZP_SCRATCH_W1 + lda #>${assign.target.asmVarname} + sta P8ZP_SCRATCH_W1+1 + pla + jsr prog8_lib.strcpy""") } + else -> throw AssemblyError("weird target dt") } } - is BuiltinFunctionPlaceholder -> { - asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) - if(assign.target.register==null) { - // still need to assign the result to the target variable/etc. - val returntype = builtinFunctionReturnType(sub.name, value.args, program) - if(!returntype.isKnown) - throw AssemblyError("unknown dt") - when(returntype.getOr(DataType.UNDEFINED)) { - in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A - in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY + DataType.FLOAT -> { + // float result from function sits in FAC1 + asmgen.restoreXafterCall(value) + assignFAC1float(assign.target) + } + else -> { + // do NOT restore X register before assigning the result values first + when (returnValue.second.registerOrPair) { + RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A) + RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X) + RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y) + RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX) + RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) + RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY) + else -> { + val sflag = returnValue.second.statusflag + if(sflag!=null) + assignStatusFlagByte(assign.target, sflag) + else + throw AssemblyError("should be just one register byte result value") + } + } + // we've processed the result value in the X register by now, so it's now finally safe to restore it + asmgen.restoreXafterCall(value) + } + } + } + is BuiltinFunctionCall -> { + asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) + if(assign.target.register==null) { + // still need to assign the result to the target variable/etc. + val returntype = builtinFunctionReturnType(value.name, value.args, program) + if(!returntype.isKnown) + throw AssemblyError("unknown dt") + when(returntype.getOr(DataType.UNDEFINED)) { + in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A + in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY + DataType.STR -> { + when (assign.target.datatype) { DataType.STR -> { - when (assign.target.datatype) { - DataType.STR -> { - asmgen.out(""" + asmgen.out(""" pha lda #<${assign.target.asmVarname} sta P8ZP_SCRATCH_W1 @@ -237,21 +236,16 @@ internal class AssignmentAsmGen(private val program: Program, sta P8ZP_SCRATCH_W1+1 pla jsr prog8_lib.strcpy""") - } - DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) - else -> throw AssemblyError("str return value type mismatch with target") - } } - DataType.FLOAT -> { - // float result from function sits in FAC1 - assignFAC1float(assign.target) - } - else -> throw AssemblyError("weird result type") + DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) + else -> throw AssemblyError("str return value type mismatch with target") } } - } - else -> { - throw AssemblyError("weird func call") + DataType.FLOAT -> { + // float result from function sits in FAC1 + assignFAC1float(assign.target) + } + else -> throw AssemblyError("weird result type") } } } @@ -682,7 +676,7 @@ $containsLabel lda #1 } private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) { - val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position) + val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position) lsb.linkParents(value.parent) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val assign = AsmAssignment(src, target, false, program.memsizer, value.position) diff --git a/codeGenTargets/src/prog8/codegen/target/cbm/AsmsubHelpers.kt b/codeGenTargets/src/prog8/codegen/target/cbm/AsmsubHelpers.kt index f00d4ff56..9a8ca5377 100644 --- a/codeGenTargets/src/prog8/codegen/target/cbm/AsmsubHelpers.kt +++ b/codeGenTargets/src/prog8/codegen/target/cbm/AsmsubHelpers.kt @@ -3,8 +3,8 @@ package prog8.codegen.target.cbm import prog8.ast.base.Cx16VirtualRegisters import prog8.ast.base.RegisterOrPair import prog8.ast.expressions.ArrayIndexedExpression +import prog8.ast.expressions.BuiltinFunctionCall import prog8.ast.expressions.Expression -import prog8.ast.expressions.FunctionCallExpression import prog8.ast.statements.RegisterOrStatusflag import prog8.ast.statements.Subroutine @@ -46,10 +46,10 @@ internal fun asmsub6502ArgsHaveRegisterClobberRisk(args: List, it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY) } } - is FunctionCallExpression -> { - if (expr.target.nameInSource == listOf("lsb") || expr.target.nameInSource == listOf("msb")) + is BuiltinFunctionCall -> { + if (expr.name == "lsb" || expr.name == "msb") return isClobberRisk(expr.args[0]) - if (expr.target.nameInSource == listOf("mkword")) + if (expr.name == "mkword") return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1]) return !expr.isSimple } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index c10d680fb..5c1335c12 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -267,43 +267,6 @@ internal class BeforeAsmAstChanger(val program: Program, ) } - override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { - if(functionCallStatement.target.nameInSource.size==1 - && functionCallStatement.target.nameInSource[0] in program.builtinFunctions.names) { - return listOf(IAstModification.ReplaceNode( - functionCallStatement, - BuiltinFunctionCallStatement(functionCallStatement.target, functionCallStatement.args, functionCallStatement.position), - parent - )) - } - - return noModifications - } - - override fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable { - if(bfcs.name=="cmp") { - // if the datatype of the arguments of cmp() are different, cast the byte one to word. - val arg1 = bfcs.args[0] - val arg2 = bfcs.args[1] - val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED) - val dt2 = arg2.inferType(program).getOr(DataType.UNDEFINED) - if(dt1 in ByteDatatypes) { - if(dt2 in ByteDatatypes) - return noModifications - val (replaced, cast) = arg1.typecastTo(if(dt1== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt1, true) - if(replaced) - return listOf(IAstModification.ReplaceNode(arg1, cast, bfcs)) - } else { - if(dt2 in WordDatatypes) - return noModifications - val (replaced, cast) = arg2.typecastTo(if(dt2== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt2, true) - if(replaced) - return listOf(IAstModification.ReplaceNode(arg2, cast, bfcs)) - } - } - return noModifications - } - override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable { val containingStatement = getContainingStatement(arrayIndexedExpression) diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt index 9fa4b5fcf..34ac37af5 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmTypecastCleaner.kt @@ -7,10 +7,9 @@ import prog8.ast.base.ByteDatatypes import prog8.ast.base.DataType import prog8.ast.base.PassByReferenceDatatypes import prog8.ast.base.WordDatatypes -import prog8.ast.expressions.AddressOf -import prog8.ast.expressions.Expression -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.TypecastExpression +import prog8.ast.expressions.* +import prog8.ast.statements.BuiltinFunctionCallStatement +import prog8.ast.statements.FunctionCallStatement import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.compilerinterface.IErrorReporter @@ -73,4 +72,59 @@ internal class BeforeAsmTypecastCleaner(val program: Program, return noModifications } + + + // also convert calls to builtin functions to BuiltinFunctionCall nodes to make things easier for codegen + + override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { + if(functionCallStatement.target.nameInSource.size==1 + && functionCallStatement.target.nameInSource[0] in program.builtinFunctions.names) { + return listOf(IAstModification.ReplaceNode( + functionCallStatement, + BuiltinFunctionCallStatement(functionCallStatement.target, functionCallStatement.args, functionCallStatement.position), + parent + )) + } + + return noModifications + } + + + override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable { + if(functionCallExpr.target.nameInSource.size==1 + && functionCallExpr.target.nameInSource[0] in program.builtinFunctions.names) { + return listOf(IAstModification.ReplaceNode( + functionCallExpr, + BuiltinFunctionCall(functionCallExpr.target, functionCallExpr.args, functionCallExpr.position), + parent + )) + } + + return noModifications + } + + override fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable { + if(bfcs.name=="cmp") { + // if the datatype of the arguments of cmp() are different, cast the byte one to word. + val arg1 = bfcs.args[0] + val arg2 = bfcs.args[1] + val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED) + val dt2 = arg2.inferType(program).getOr(DataType.UNDEFINED) + if(dt1 in ByteDatatypes) { + if(dt2 in ByteDatatypes) + return noModifications + val (replaced, cast) = arg1.typecastTo(if(dt1== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt1, true) + if(replaced) + return listOf(IAstModification.ReplaceNode(arg1, cast, bfcs)) + } else { + if(dt2 in WordDatatypes) + return noModifications + val (replaced, cast) = arg2.typecastTo(if(dt2== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt2, true) + if(replaced) + return listOf(IAstModification.ReplaceNode(arg2, cast, bfcs)) + } + } + return noModifications + } + } \ No newline at end of file diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 943fb621c..4f84b26b2 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -206,6 +206,10 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: printout(functionCallExpr as IFunctionCall) } + override fun visit(bfc: BuiltinFunctionCall) { + printout(bfc as IFunctionCall) + } + override fun visit(functionCallStatement: FunctionCallStatement) { printout(functionCallStatement as IFunctionCall) } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index f000f0ae1..f18a10b34 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -965,10 +965,6 @@ class FunctionCallExpression(override var target: IdentifierReference, val stmt = target.targetStatement(program) ?: return InferredTypes.unknown() when (stmt) { is BuiltinFunctionPlaceholder -> { - if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" || - target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") { - return InferredTypes.void() // these have no return value - } return program.builtinFunctions.returnType(target.nameInSource[0], this.args) } is Subroutine -> { @@ -1132,3 +1128,39 @@ fun invertCondition(cond: Expression): BinaryExpression? { } return null } + + +class BuiltinFunctionCall(override var target: IdentifierReference, + override var args: MutableList, + override val position: Position) : Expression(), IFunctionCall { + + val name = target.nameInSource.single() + + override lateinit var parent: Node + + override fun linkParents(parent: Node) { + this.parent = parent + target.linkParents(this) + args.forEach { it.linkParents(this) } + } + + override fun copy() = BuiltinFunctionCall(target.copy(), args.map { it.copy() }.toMutableList(), position) + override val isSimple = name in arrayOf("msb", "lsb", "peek", "peekw", "set_carry", "set_irqd", "clear_carry", "clear_irqd") + + override fun replaceChildNode(node: Node, replacement: Node) { + if(node===target) + target=replacement as IdentifierReference + else { + val idx = args.indexOfFirst { it===node } + args[idx] = replacement as Expression + } + replacement.parent = this + } + + override fun constValue(program: Program): NumericLiteral? = null + override fun toString() = "BuiltinFunctionCall(name=$name, pos=$position)" + override fun accept(visitor: IAstVisitor) = visitor.visit(this) + override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) + override fun referencesIdentifier(nameInSource: List): Boolean = target.referencesIdentifier(nameInSource) || args.any{it.referencesIdentifier(nameInSource)} + override fun inferType(program: Program) = program.builtinFunctions.returnType(name, this.args) +} diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index 4be233a87..f5cb8e416 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -95,6 +95,7 @@ abstract class AstWalker { open fun before(forLoop: ForLoop, parent: Node): Iterable = noModifications open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable = noModifications open fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable = noModifications + open fun before(bfc: BuiltinFunctionCall, parent: Node): Iterable = noModifications open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable = noModifications open fun before(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable = noModifications open fun before(identifier: IdentifierReference, parent: Node): Iterable = noModifications @@ -139,6 +140,7 @@ abstract class AstWalker { open fun after(forLoop: ForLoop, parent: Node): Iterable = noModifications open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable = noModifications open fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable = noModifications + open fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable = noModifications open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable = noModifications open fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable = noModifications open fun after(identifier: IdentifierReference, parent: Node): Iterable = noModifications @@ -266,6 +268,13 @@ abstract class AstWalker { track(after(functionCallExpr, parent), functionCallExpr, parent) } + fun visit(bfc: BuiltinFunctionCall, parent: Node) { + track(before(bfc, parent), bfc, parent) + bfc.target.accept(this, bfc) + bfc.args.forEach { it.accept(this, bfc) } + track(after(bfc, parent), bfc, parent) + } + fun visit(functionCallStatement: FunctionCallStatement, parent: Node) { track(before(functionCallStatement, parent), functionCallStatement, parent) functionCallStatement.target.accept(this, functionCallStatement) diff --git a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt index 1f3db562c..e6d5414a4 100644 --- a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt +++ b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt @@ -49,6 +49,11 @@ interface IAstVisitor { functionCallExpr.args.forEach { it.accept(this) } } + fun visit(bfc: BuiltinFunctionCall) { + bfc.target.accept(this) + bfc.args.forEach { it.accept(this) } + } + fun visit(functionCallStatement: FunctionCallStatement) { functionCallStatement.target.accept(this) functionCallStatement.args.forEach { it.accept(this) } diff --git a/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt b/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt index 0846a4d44..5ea7f93aa 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/BuiltinFunctions.kt @@ -176,6 +176,9 @@ private fun builtinAll(array: List): Double = if(array.all { it!=0.0 }) fun builtinFunctionReturnType(function: String, args: List, program: Program): InferredTypes.InferredType { + if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd")) + return InferredTypes.InferredType.void() + fun datatypeFromIterableArg(arglist: Expression): DataType { if(arglist is ArrayLiteral) { val dt = arglist.value.map {it.inferType(program).getOr(DataType.UNDEFINED)}.toSet() diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 514f0c06b..85816030f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- introduce BuiltinFunctionCallExpression as well? - see if we can get rid of storing the origAstTarget in AsmAssignTarget