introduced BuiltinFunctionCall (expression) node for codegen

This commit is contained in:
Irmen de Jong
2022-02-20 02:01:48 +01:00
parent 5ac784e18a
commit 2f18a8f6d0
15 changed files with 289 additions and 238 deletions

View File

@@ -443,8 +443,8 @@ class AsmGen(internal val program: Program,
internal fun translateExpression(expression: Expression) = internal fun translateExpression(expression: Expression) =
expressionsAsmGen.translateExpression(expression) expressionsAsmGen.translateExpression(expression)
internal fun translateBuiltinFunctionCallExpression(functionCallExpr: FunctionCallExpression, resultToStack: Boolean, resultRegister: RegisterOrPair?) = internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCallExpr, resultToStack, resultRegister) builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
internal fun translateBuiltinFunctionCallExpression(name: String, args: List<AsmAssignSource>, scope: Subroutine): DataType = internal fun translateBuiltinFunctionCallExpression(name: String, args: List<AsmAssignSource>, scope: Subroutine): DataType =
builtinFunctionsAsmGen.translateFunctioncall(name, args, false, scope) builtinFunctionsAsmGen.translateFunctioncall(name, args, false, scope)
@@ -1200,7 +1200,7 @@ $repeatLabel lda $counterVar
} }
if(dt==DataType.UBYTE) { if(dt==DataType.UBYTE) {
assignExpressionToRegister(left, RegisterOrPair.A, false) assignExpressionToRegister(left, RegisterOrPair.A, false)
if (left is FunctionCallExpression && !left.isSimple) if (left is IFunctionCall && !left.isSimple)
out(" cmp #0") out(" cmp #0")
} else { } else {
assignExpressionToRegister(left, RegisterOrPair.AY, false) assignExpressionToRegister(left, RegisterOrPair.AY, false)
@@ -1216,7 +1216,7 @@ $repeatLabel lda $counterVar
} }
DataType.BYTE -> { DataType.BYTE -> {
assignExpressionToRegister(left, RegisterOrPair.A, true) assignExpressionToRegister(left, RegisterOrPair.A, true)
if (left is FunctionCallExpression && !left.isSimple) if (left is IFunctionCall && !left.isSimple)
out(" cmp #0") out(" cmp #0")
when (operator) { when (operator) {
"==" -> out(" bne $jumpIfFalseLabel") "==" -> out(" bne $jumpIfFalseLabel")

View File

@@ -22,7 +22,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
private val assignAsmGen: AssignmentAsmGen, private val assignAsmGen: AssignmentAsmGen,
private val allocations: VariableAllocator) { 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()) val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
} }
@@ -57,7 +57,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
}.toMutableList() }.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) fcall.linkParents(scope)
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null) translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
return if(isStatement) DataType.UNDEFINED else func.known_returntype!! 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?) { 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") throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as StringLiteral).value val name = (fcall.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"} require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}

View File

@@ -3,8 +3,6 @@ package prog8.codegen.cpu6502
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.BuiltinFunctionPlaceholder
import prog8.ast.statements.Subroutine
import prog8.ast.toHex import prog8.ast.toHex
import prog8.compilerinterface.AssemblyError import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.CpuType import prog8.compilerinterface.CpuType
@@ -38,6 +36,7 @@ internal class ExpressionsAsmGen(private val program: Program,
is NumericLiteral -> translateExpression(expression) is NumericLiteral -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression) is IdentifierReference -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression) is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PipeExpression -> asmgen.translatePipeExpression(expression.expressions, expression,false, true) is PipeExpression -> asmgen.translatePipeExpression(expression.expressions, expression,false, true)
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported") 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") 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) { private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) {
// only for use in nested expression evaluation // only for use in nested expression evaluation
val sub = call.target.targetStatement(program) val sub = call.target.targetSubroutine(program)!!
if(sub is BuiltinFunctionPlaceholder) { asmgen.saveXbeforeCall(call)
asmgen.translateBuiltinFunctionCallExpression(call, true, null) asmgen.translateFunctionCall(call, true)
} else { if(sub.regXasResult()) {
sub as Subroutine // store the return value in X somewhere that we can access again below
asmgen.saveXbeforeCall(call) asmgen.out(" stx P8ZP_SCRATCH_REG")
asmgen.translateFunctionCall(call, true) }
if(sub.regXasResult()) { asmgen.restoreXafterCall(call)
// store the return value in X somewhere that we can acces again below
asmgen.out(" stx P8ZP_SCRATCH_REG")
}
asmgen.restoreXafterCall(call)
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for ((_, reg) in returns) { 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) // 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) { if (reg.registerOrPair != null) {
when (reg.registerOrPair!!) { when (reg.registerOrPair!!) {
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex") RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | 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.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.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.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.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.FAC1 -> asmgen.out(" jsr floats.push_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2") RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
RegisterOrPair.R0, RegisterOrPair.R0,
RegisterOrPair.R1, RegisterOrPair.R1,
RegisterOrPair.R2, RegisterOrPair.R2,
RegisterOrPair.R3, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R4,
RegisterOrPair.R5, RegisterOrPair.R5,
RegisterOrPair.R6, RegisterOrPair.R6,
RegisterOrPair.R7, RegisterOrPair.R7,
RegisterOrPair.R8, RegisterOrPair.R8,
RegisterOrPair.R9, RegisterOrPair.R9,
RegisterOrPair.R10, RegisterOrPair.R10,
RegisterOrPair.R11, RegisterOrPair.R11,
RegisterOrPair.R12, RegisterOrPair.R12,
RegisterOrPair.R13, RegisterOrPair.R13,
RegisterOrPair.R14, RegisterOrPair.R14,
RegisterOrPair.R15 -> { RegisterOrPair.R15 -> {
asmgen.out( asmgen.out(
""" """
lda cx16.${reg.registerOrPair.toString().lowercase()} 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
sta P8ESTACK_LO,x 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 -> {}
} }
} }
} }

View File

@@ -18,7 +18,7 @@ import prog8.compilerinterface.AssemblyError
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translateFunctionCallStatement(stmt: IFunctionCall) { internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) {
saveXbeforeCall(stmt) saveXbeforeCall(stmt)
translateFunctionCall(stmt, false) translateFunctionCall(stmt, false)
restoreXafterCall(stmt) restoreXafterCall(stmt)

View File

@@ -167,26 +167,20 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") } val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value) 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 -> { is FunctionCallExpression -> {
when (val sub = value.target.targetStatement(program)) { val sub = value.target.targetSubroutine(program)!!
is Subroutine -> { val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
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")
?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) 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")
}
}
} }
else -> { else -> {
val dt = value.inferType(program) val returnType = value.inferType(program)
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.getOrElse { throw AssemblyError("unknown dt") }, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
} }
} }
} }

View File

@@ -161,75 +161,74 @@ internal class AssignmentAsmGen(private val program: Program,
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value) is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
is FunctionCallExpression -> { is FunctionCallExpression -> {
when (val sub = value.target.targetStatement(program)) { val sub = value.target.targetSubroutine(program)!!
is Subroutine -> { asmgen.saveXbeforeCall(value)
asmgen.saveXbeforeCall(value) asmgen.translateFunctionCall(value, true)
asmgen.translateFunctionCall(value, true) val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?:
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).singleOrNull { it.second.registerOrPair!=null } ?: sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null }
sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.statusflag!=null } when (returnValue.first) {
when (returnValue.first) { DataType.STR -> {
DataType.STR -> { asmgen.restoreXafterCall(value)
asmgen.restoreXafterCall(value) when(assign.target.datatype) {
when(assign.target.datatype) { DataType.UWORD -> {
DataType.UWORD -> { // assign the address of the string result value
// assign the address of the string result value assignRegisterpairWord(assign.target, RegisterOrPair.AY)
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")
}
} }
DataType.FLOAT -> { DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
// float result from function sits in FAC1 // copy the actual string result into the target string variable
asmgen.restoreXafterCall(value) asmgen.out("""
assignFAC1float(assign.target) pha
} lda #<${assign.target.asmVarname}
else -> { sta P8ZP_SCRATCH_W1
// do NOT restore X register before assigning the result values first lda #>${assign.target.asmVarname}
when (returnValue.second.registerOrPair) { sta P8ZP_SCRATCH_W1+1
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A) pla
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X) jsr prog8_lib.strcpy""")
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)
} }
else -> throw AssemblyError("weird target dt")
} }
} }
is BuiltinFunctionPlaceholder -> { DataType.FLOAT -> {
asmgen.translateBuiltinFunctionCallExpression(value, false, assign.target.register) // float result from function sits in FAC1
if(assign.target.register==null) { asmgen.restoreXafterCall(value)
// still need to assign the result to the target variable/etc. assignFAC1float(assign.target)
val returntype = builtinFunctionReturnType(sub.name, value.args, program) }
if(!returntype.isKnown) else -> {
throw AssemblyError("unknown dt") // do NOT restore X register before assigning the result values first
when(returntype.getOr(DataType.UNDEFINED)) { when (returnValue.second.registerOrPair) {
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY 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 -> { DataType.STR -> {
when (assign.target.datatype) { asmgen.out("""
DataType.STR -> {
asmgen.out("""
pha pha
lda #<${assign.target.asmVarname} lda #<${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@@ -237,21 +236,16 @@ internal class AssignmentAsmGen(private val program: Program,
sta P8ZP_SCRATCH_W1+1 sta P8ZP_SCRATCH_W1+1
pla pla
jsr prog8_lib.strcpy""") jsr prog8_lib.strcpy""")
}
DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
else -> throw AssemblyError("str return value type mismatch with target")
}
} }
DataType.FLOAT -> { DataType.UWORD -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
// float result from function sits in FAC1 else -> throw AssemblyError("str return value type mismatch with target")
assignFAC1float(assign.target)
}
else -> throw AssemblyError("weird result type")
} }
} }
} DataType.FLOAT -> {
else -> { // float result from function sits in FAC1
throw AssemblyError("weird func call") assignFAC1float(assign.target)
}
else -> throw AssemblyError("weird result type")
} }
} }
} }
@@ -682,7 +676,7 @@ $containsLabel lda #1
} }
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) { 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) lsb.linkParents(value.parent)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
val assign = AsmAssignment(src, target, false, program.memsizer, value.position) val assign = AsmAssignment(src, target, false, program.memsizer, value.position)

View File

@@ -3,8 +3,8 @@ package prog8.codegen.target.cbm
import prog8.ast.base.Cx16VirtualRegisters import prog8.ast.base.Cx16VirtualRegisters
import prog8.ast.base.RegisterOrPair import prog8.ast.base.RegisterOrPair
import prog8.ast.expressions.ArrayIndexedExpression import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.statements.RegisterOrStatusflag import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
@@ -46,10 +46,10 @@ internal fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>,
it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY) it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
} }
} }
is FunctionCallExpression -> { is BuiltinFunctionCall -> {
if (expr.target.nameInSource == listOf("lsb") || expr.target.nameInSource == listOf("msb")) if (expr.name == "lsb" || expr.name == "msb")
return isClobberRisk(expr.args[0]) 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 isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
return !expr.isSimple return !expr.isSimple
} }

View File

@@ -267,43 +267,6 @@ internal class BeforeAsmAstChanger(val program: Program,
) )
} }
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
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<IAstModification> {
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<IAstModification> { override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val containingStatement = getContainingStatement(arrayIndexedExpression) val containingStatement = getContainingStatement(arrayIndexedExpression)

View File

@@ -7,10 +7,9 @@ import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.PassByReferenceDatatypes import prog8.ast.base.PassByReferenceDatatypes
import prog8.ast.base.WordDatatypes import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.AddressOf import prog8.ast.expressions.*
import prog8.ast.expressions.Expression import prog8.ast.statements.BuiltinFunctionCallStatement
import prog8.ast.expressions.IdentifierReference import prog8.ast.statements.FunctionCallStatement
import prog8.ast.expressions.TypecastExpression
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compilerinterface.IErrorReporter import prog8.compilerinterface.IErrorReporter
@@ -73,4 +72,59 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
return noModifications 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<IAstModification> {
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<IAstModification> {
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<IAstModification> {
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
}
} }

View File

@@ -206,6 +206,10 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
printout(functionCallExpr as IFunctionCall) printout(functionCallExpr as IFunctionCall)
} }
override fun visit(bfc: BuiltinFunctionCall) {
printout(bfc as IFunctionCall)
}
override fun visit(functionCallStatement: FunctionCallStatement) { override fun visit(functionCallStatement: FunctionCallStatement) {
printout(functionCallStatement as IFunctionCall) printout(functionCallStatement as IFunctionCall)
} }

View File

@@ -965,10 +965,6 @@ class FunctionCallExpression(override var target: IdentifierReference,
val stmt = target.targetStatement(program) ?: return InferredTypes.unknown() val stmt = target.targetStatement(program) ?: return InferredTypes.unknown()
when (stmt) { when (stmt) {
is BuiltinFunctionPlaceholder -> { 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) return program.builtinFunctions.returnType(target.nameInSource[0], this.args)
} }
is Subroutine -> { is Subroutine -> {
@@ -1132,3 +1128,39 @@ fun invertCondition(cond: Expression): BinaryExpression? {
} }
return null return null
} }
class BuiltinFunctionCall(override var target: IdentifierReference,
override var args: MutableList<Expression>,
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<String>): Boolean = target.referencesIdentifier(nameInSource) || args.any{it.referencesIdentifier(nameInSource)}
override fun inferType(program: Program) = program.builtinFunctions.returnType(name, this.args)
}

View File

@@ -95,6 +95,7 @@ abstract class AstWalker {
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> = noModifications open fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun before(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> = noModifications
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun before(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications open fun before(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications
@@ -139,6 +140,7 @@ abstract class AstWalker {
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> = noModifications open fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> = noModifications
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications open fun after(bfcs: BuiltinFunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications
@@ -266,6 +268,13 @@ abstract class AstWalker {
track(after(functionCallExpr, parent), functionCallExpr, parent) 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) { fun visit(functionCallStatement: FunctionCallStatement, parent: Node) {
track(before(functionCallStatement, parent), functionCallStatement, parent) track(before(functionCallStatement, parent), functionCallStatement, parent)
functionCallStatement.target.accept(this, functionCallStatement) functionCallStatement.target.accept(this, functionCallStatement)

View File

@@ -49,6 +49,11 @@ interface IAstVisitor {
functionCallExpr.args.forEach { it.accept(this) } functionCallExpr.args.forEach { it.accept(this) }
} }
fun visit(bfc: BuiltinFunctionCall) {
bfc.target.accept(this)
bfc.args.forEach { it.accept(this) }
}
fun visit(functionCallStatement: FunctionCallStatement) { fun visit(functionCallStatement: FunctionCallStatement) {
functionCallStatement.target.accept(this) functionCallStatement.target.accept(this)
functionCallStatement.args.forEach { it.accept(this) } functionCallStatement.args.forEach { it.accept(this) }

View File

@@ -176,6 +176,9 @@ private fun builtinAll(array: List<Double>): Double = if(array.all { it!=0.0 })
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): InferredTypes.InferredType { fun builtinFunctionReturnType(function: String, args: List<Expression>, 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 { fun datatypeFromIterableArg(arglist: Expression): DataType {
if(arglist is ArrayLiteral) { if(arglist is ArrayLiteral) {
val dt = arglist.value.map {it.inferType(program).getOr(DataType.UNDEFINED)}.toSet() val dt = arglist.value.map {it.inferType(program).getOr(DataType.UNDEFINED)}.toSet()

View File

@@ -3,7 +3,6 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- introduce BuiltinFunctionCallExpression as well?
- see if we can get rid of storing the origAstTarget in AsmAssignTarget - see if we can get rid of storing the origAstTarget in AsmAssignTarget