cleanup of Pipe codegen

This commit is contained in:
Irmen de Jong 2022-01-08 01:33:40 +01:00
parent 17694c1d01
commit 75d857027e
4 changed files with 92 additions and 40 deletions

View File

@ -959,6 +959,9 @@ class AsmGen(private val program: Program,
internal fun translateBuiltinFunctionCallExpression(functionCallExpr: FunctionCallExpression, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) = internal fun translateBuiltinFunctionCallExpression(functionCallExpr: FunctionCallExpression, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCallExpr, signature, resultToStack, resultRegister) builtinFunctionsAsmGen.translateFunctioncallExpression(functionCallExpr, signature, resultToStack, resultRegister)
internal fun translateBuiltinFunctionCallStatement(functionCallStmt: FunctionCallStatement, signature: FSignature) =
builtinFunctionsAsmGen.translateFunctioncallStatement(functionCallStmt, signature)
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) = internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression) functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
@ -1634,40 +1637,31 @@ $label nop""")
val subroutine = pipe.definingSubroutine val subroutine = pipe.definingSubroutine
assignExpressionToVariable(pipe.expressions.first(), valueVar.joinToString("."), valueDt, subroutine) assignExpressionToVariable(pipe.expressions.first(), valueVar.joinToString("."), valueDt, subroutine)
pipe.expressions.drop(1).dropLast(1).forEach { pipe.expressions.drop(1).dropLast(1).forEach {
val callName = it as IdentifierReference valueDt = functioncallAsmGen.translateFunctionCall(it as IdentifierReference, listOf(IdentifierReference(valueVar, it.position)), pipe)
val args = mutableListOf<Expression>(IdentifierReference(valueVar, it.position)) // assign result value from the functioncall back to the temp var:
val call = FunctionCallExpression(callName, args,it.position)
call.linkParents(pipe)
valueDt = call.inferType(program).getOrElse { throw AssemblyError("invalid dt") }
valueVar = getTempVarName(valueDt) 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: // the last term in the pipe, don't care about return var:
val callName = pipe.expressions.last() as IdentifierReference functioncallAsmGen.translateFunctionCallStatement(
val callTarget = callName.targetStatement(program)!! pipe.expressions.last() as IdentifierReference,
when (callTarget) { listOf(IdentifierReference(valueVar, pipe.expressions.last().position)),
pipe
)
}
private fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair? {
return when (val targetRoutine = it.targetStatement(program)!!) {
is BuiltinFunctionPlaceholder -> { is BuiltinFunctionPlaceholder -> {
val args = mutableListOf<Expression>(IdentifierReference(valueVar, callName.position)) when (BuiltinFunctions.getValue(targetRoutine.name).known_returntype) {
val call = FunctionCallStatement(callName, args, true, callName.position) in ByteDatatypes -> RegisterOrPair.A
call.linkParents(pipe) in WordDatatypes -> RegisterOrPair.AY
translate(call) else -> return null
}
is Subroutine -> {
if(callTarget.isAsmSubroutine) {
val args = mutableListOf<Expression>(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)}")
} }
} }
is Subroutine -> targetRoutine.asmReturnvaluesRegisters.single().registerOrPair!!
else -> throw AssemblyError("invalid call target") else -> throw AssemblyError("invalid call target")
} }
} }

View File

@ -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.AsmAssignTarget
import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignment import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.codegen.target.cpu6502.codegen.assignment.TargetStorageKind import prog8.codegen.target.cpu6502.codegen.assignment.TargetStorageKind
import prog8.compilerinterface.BuiltinFunctions
import prog8.compilerinterface.CpuType 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 // 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<Expression>, 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<Expression>, 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) { private fun argumentsViaVariables(sub: Subroutine, call: IFunctionCall) {
for(arg in sub.parameters.withIndex().zip(call.args)) 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) { 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 asmgen.out(" plp") // set the carry flag back to correct value
} }
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) { private fun argumentViaVariable(sub: Subroutine, parameter: SubroutineParameter, value: Expression) {
// pass parameter via a regular variable (not via registers) // pass parameter via a regular variable (not via registers)
val valueIDt = value.inferType(program) val valueIDt = value.inferType(program)
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") } val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
if(!isArgumentTypeCompatible(valueDt, parameter.value.type)) if(!isArgumentTypeCompatible(valueDt, parameter.type))
throw AssemblyError("argument type incompatible") throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedName + parameter.value.name) val varName = asmgen.asmVariableName(sub.scopedName + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub) asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
} }
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) { private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) {

View File

@ -3,6 +3,7 @@ TODO
For next compiler release (7.7) 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 - 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 . - 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. 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 Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
- can we promise a left-to-right function call argument evaluation? without sacrificing performance - 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_`` - 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? then we can get rid of the instruction lists in the machinedefinitions as well?
- fix the asm-labels problem (github issue #62) - fix the asm-labels problem (github issue #62)

View File

@ -1,16 +1,20 @@
%import textio %import textio
%import floats
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
times_two(554 as ubyte) 1.234 |> addfloat |> addfloat |> floats.print_f
; txt.print_uw(times_two(add_one(9+3))) txt.nl()
; txt.nl() 9 * 3 |> assemblything
; 9 + 3 |> sin8u
; |> add_one |> times_two |> add_one |> times_two
; |> txt.print_uw |> txt.print_uw
} }
sub addfloat(float fl) -> float {
return fl+1.11
}
sub add_one(ubyte input) -> ubyte { sub add_one(ubyte input) -> ubyte {
return input+1 return input+1
} }
@ -18,6 +22,13 @@ main {
sub times_two(ubyte input) -> uword { sub times_two(ubyte input) -> uword {
return input*$0002 return input*$0002
} }
asmsub assemblything(ubyte input @A) -> ubyte @A {
%asm {{
asl a
rts
}}
}
} }