diff --git a/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/AsmGen.kt index d13a4f718..2d9442aa4 100644 --- a/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/AsmGen.kt @@ -959,6 +959,9 @@ class AsmGen(private val program: Program, internal fun translateBuiltinFunctionCallExpression(functionCallExpr: FunctionCallExpression, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) = builtinFunctionsAsmGen.translateFunctioncallExpression(functionCallExpr, signature, resultToStack, resultRegister) + internal fun translateBuiltinFunctionCallStatement(functionCallStmt: FunctionCallStatement, signature: FSignature) = + builtinFunctionsAsmGen.translateFunctioncallStatement(functionCallStmt, signature) + internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) = functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression) @@ -1634,40 +1637,31 @@ $label nop""") val subroutine = pipe.definingSubroutine assignExpressionToVariable(pipe.expressions.first(), valueVar.joinToString("."), valueDt, subroutine) pipe.expressions.drop(1).dropLast(1).forEach { - val callName = it as IdentifierReference - val args = mutableListOf(IdentifierReference(valueVar, it.position)) - val call = FunctionCallExpression(callName, args,it.position) - call.linkParents(pipe) - valueDt = call.inferType(program).getOrElse { throw AssemblyError("invalid dt") } + valueDt = functioncallAsmGen.translateFunctionCall(it as IdentifierReference, listOf(IdentifierReference(valueVar, it.position)), pipe) + // assign result value from the functioncall back to the temp var: valueVar = getTempVarName(valueDt) - assignExpressionToVariable(call, valueVar.joinToString("."), valueDt, subroutine) + val valueVarTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, valueDt, subroutine, variableAsmName = valueVar.joinToString(".")) + val returnRegister = returnRegisterOfFunction(it)!! + assignRegister(returnRegister, valueVarTarget) } - // the last term in the pipe: - val callName = pipe.expressions.last() as IdentifierReference - val callTarget = callName.targetStatement(program)!! - when (callTarget) { + // the last term in the pipe, don't care about return var: + functioncallAsmGen.translateFunctionCallStatement( + pipe.expressions.last() as IdentifierReference, + listOf(IdentifierReference(valueVar, pipe.expressions.last().position)), + pipe + ) + } + + private fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair? { + return when (val targetRoutine = it.targetStatement(program)!!) { is BuiltinFunctionPlaceholder -> { - val args = mutableListOf(IdentifierReference(valueVar, callName.position)) - val call = FunctionCallStatement(callName, args, true, callName.position) - call.linkParents(pipe) - translate(call) - } - is Subroutine -> { - if(callTarget.isAsmSubroutine) { - val args = mutableListOf(IdentifierReference(valueVar, callName.position)) - val call = FunctionCallStatement(callName, args, true, callName.position) - call.linkParents(pipe) - translate(call) - } else { - // have to use GoSub and manual parameter assignment, because no codegen for FunctionCallStmt here - val param = callTarget.parameters.single() - val paramName = callTarget.scopedName.joinToString(".") + ".${param.name}" - val tempvar = IdentifierReference(valueVar, callName.position) - tempvar.linkParents(pipe) - assignExpressionToVariable(tempvar, paramName, param.type, subroutine) - out(" jsr ${asmSymbolName(callName)}") + when (BuiltinFunctions.getValue(targetRoutine.name).known_returntype) { + in ByteDatatypes -> RegisterOrPair.A + in WordDatatypes -> RegisterOrPair.AY + else -> return null } } + is Subroutine -> targetRoutine.asmReturnvaluesRegisters.single().registerOrPair!! else -> throw AssemblyError("invalid call target") } } diff --git a/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/FunctionCallAsmGen.kt b/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/FunctionCallAsmGen.kt index 2b17e103f..ef4a5653c 100644 --- a/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/FunctionCallAsmGen.kt +++ b/codeGeneration/src/prog8/codegen/target/cpu6502/codegen/FunctionCallAsmGen.kt @@ -11,6 +11,7 @@ import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignSource import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignTarget import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignment import prog8.codegen.target.cpu6502.codegen.assignment.TargetStorageKind +import prog8.compilerinterface.BuiltinFunctions import prog8.compilerinterface.CpuType @@ -126,9 +127,53 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg // remember: dealing with the X register and/or dealing with return values is the responsibility of the caller } + internal fun translateFunctionCall(target: IdentifierReference, args: Iterable, scope: Node): DataType { + when(val targetStmt = target.targetStatement(program)!!) { + is BuiltinFunctionPlaceholder -> { + val call = FunctionCallExpression(target, args.toMutableList(), scope.position) + call.linkParents(scope) + val signature = BuiltinFunctions.getValue(targetStmt.name) + asmgen.translateBuiltinFunctionCallExpression(call, signature, false, null) + return signature.known_returntype!! + } + is Subroutine -> { + val call = FunctionCallExpression(target, args.toMutableList(), scope.position) + call.linkParents(scope) + translateFunctionCall(call, true) + return call.inferType(program).getOrElse { throw AssemblyError("invalid dt") } + } + else -> throw AssemblyError("invalid call target") + } + } + + internal fun translateFunctionCallStatement(target: IdentifierReference, args: Iterable, scope: Node) { + when(val targetStmt = target.targetStatement(program)!!) { + is BuiltinFunctionPlaceholder -> { + val call = FunctionCallStatement(target, args.toMutableList(), true, scope.position) + call.linkParents(scope) + val signature = BuiltinFunctions.getValue(targetStmt.name) + asmgen.translateBuiltinFunctionCallStatement(call, signature) + } + is Subroutine -> { + if(targetStmt.isAsmSubroutine) { + val call = FunctionCallStatement(target, args.toMutableList(), true, scope.position) + call.linkParents(scope) + translateFunctionCallStatement(call) + } else { + // have to use manual parameter assignment and jsr, because no codegen for FunctionCallStmt here + val tempVar = args.single() + tempVar.linkParents(scope) + argumentViaVariable(targetStmt, targetStmt.parameters.single(), tempVar) + asmgen.out(" jsr ${asmgen.asmSymbolName(target)}") + } + } + else -> throw AssemblyError("invalid call target") + } + } + private fun argumentsViaVariables(sub: Subroutine, call: IFunctionCall) { for(arg in sub.parameters.withIndex().zip(call.args)) - argumentViaVariable(sub, arg.first, arg.second) + argumentViaVariable(sub, arg.first.value, arg.second) } private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) { @@ -272,15 +317,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg asmgen.out(" plp") // set the carry flag back to correct value } - private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue, value: Expression) { + private fun argumentViaVariable(sub: Subroutine, parameter: SubroutineParameter, value: Expression) { // pass parameter via a regular variable (not via registers) val valueIDt = value.inferType(program) val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } - if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) + if(!isArgumentTypeCompatible(valueDt, parameter.type)) throw AssemblyError("argument type incompatible") - val varName = asmgen.asmVariableName(sub.scopedName + parameter.value.name) - asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub) + val varName = asmgen.asmVariableName(sub.scopedName + parameter.name) + asmgen.assignExpressionToVariable(value, varName, parameter.type, sub) } private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue, value: Expression, registerOverride: RegisterOrPair? = null) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index cdeb9f5fb..f6112dbdc 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,7 @@ TODO For next compiler release (7.7) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- make pipe statement an expression so that it DOES return a result value (possibly) so you can assign it ?? - optimize codegen of pipe operator to avoid needless assigns to temp var - copying floats around: do it with a subroutine rather than 5 lda/sta pairs . is slower but floats are very slow already anyway and this should take a lot less program size. @@ -24,6 +25,7 @@ Blocked by an official Commander-x16 r39 release Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ - can we promise a left-to-right function call argument evaluation? without sacrificing performance +- for the pipe operator: recognise a placeholder (? or % or _) in a non-unary function call to allow things as 4 |> mkword(?, $44) |> print_uw - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_`` then we can get rid of the instruction lists in the machinedefinitions as well? - fix the asm-labels problem (github issue #62) diff --git a/examples/test.p8 b/examples/test.p8 index 8611eb77b..666831bea 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,16 +1,20 @@ %import textio +%import floats %zeropage basicsafe main { sub start() { - times_two(554 as ubyte) -; txt.print_uw(times_two(add_one(9+3))) -; txt.nl() -; 9 + 3 -; |> add_one |> times_two -; |> txt.print_uw + 1.234 |> addfloat |> addfloat |> floats.print_f + txt.nl() + 9 * 3 |> assemblything + |> sin8u + |> add_one |> times_two + |> txt.print_uw } + sub addfloat(float fl) -> float { + return fl+1.11 + } sub add_one(ubyte input) -> ubyte { return input+1 } @@ -18,6 +22,13 @@ main { sub times_two(ubyte input) -> uword { return input*$0002 } + + asmsub assemblything(ubyte input @A) -> ubyte @A { + %asm {{ + asl a + rts + }} + } }