From fe5b2257325c02cd30991536cc7631a25218ff91 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 11 Aug 2019 12:29:18 +0200 Subject: [PATCH] asmsub stack arg --- .../src/prog8/ast/processing/AstChecker.kt | 13 ++++------ .../ast/processing/StatementReorderer.kt | 2 +- .../compiler/target/c64/codegen2/AsmGen2.kt | 26 +++++++++++-------- .../target/c64/codegen2/AsmOptimizer.kt | 4 +-- .../src/prog8/optimizer/StatementOptimizer.kt | 7 ----- docs/source/todo.rst | 9 +++++++ examples/test.p8 | 25 +++++------------- 7 files changed, 39 insertions(+), 47 deletions(-) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 80067a414..91dd1bdbc 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -317,14 +317,11 @@ internal class AstChecker(private val program: Program, err("carry parameter has to come last") } else { - // TODO: non-asm subroutines can only take numeric arguments for now. (not strings and arrays) Maybe this can be improved now that we have '&' ? - // the way string params are treated is almost okay (their address is passed) but the receiving subroutine treats it as an integer rather than referring back to the original string. - // the way array params are treated is buggy; it thinks the subroutine needs a byte parameter in place of a byte[] ... - // This is not easy to fix because strings and arrays are treated a bit simplistic (a "virtual" pointer to the value on the heap) - // while passing them as subroutine parameters would require a "real" pointer OR copying the VALUE to the subroutine's parameter variable (which is very inefficient). - // For now, don't pass strings and arrays as parameters and instead create the workaround as suggested in the error message below. - if(!subroutine.parameters.all{it.type in NumericDatatypes }) { - err("Non-asm subroutine can only take numerical parameters (no str/array types) for now. Workaround (for nested subroutine): access the variable from the outer scope directly.") + // Pass-by-reference datatypes can not occur as parameters to a subroutine directly + // Instead, their reference (address) should be passed (as an UWORD). + // The language has no typed pointers at this time. + if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) { + err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword for their address, or access the variable from the outer scope directly.") } } } diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index 533bc2148..4ed9e1ddc 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -319,7 +319,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi } } null -> {} - else -> TODO("call to something weird $sub ${call.target}") + else -> throw FatalAstException("call to something weird $sub ${call.target}") } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt index 792ddd244..492e1a07c 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt @@ -662,35 +662,35 @@ internal class AsmGen2(val program: Program, out(" ldx c64.SCRATCH_ZPREGX") // restore X again } - fun translateSubroutineArgument(arg: IndexedValue, value: Expression, sub: Subroutine) { + fun translateSubroutineArgument(parameter: IndexedValue, value: Expression, sub: Subroutine) { val sourceDt = value.inferType(program)!! - if(!argumentTypeCompatible(sourceDt, arg.value.type)) + if(!argumentTypeCompatible(sourceDt, parameter.value.type)) throw AssemblyError("argument type incompatible") if(sub.asmParameterRegisters.isEmpty()) { - // pass arg via a variable - val paramVar = arg.value + // pass parameter via a variable + val paramVar = parameter.value val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".") val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) target.linkParents(value.parent) when (value) { is NumericLiteralValue -> { // optimize when the argument is a constant literal - when(arg.value.type) { + when(parameter.value.type) { in ByteDatatypes -> assignFromByteConstant(target, value.number.toShort()) in WordDatatypes -> assignFromWordConstant(target, value.number.toInt()) DataType.FLOAT -> assignFromFloatConstant(target, value.number.toDouble()) in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?") - else -> throw AssemblyError("weird arg datatype") + else -> throw AssemblyError("weird parameter datatype") } } is IdentifierReference -> { // optimize when the argument is a variable - when (arg.value.type) { + when (parameter.value.type) { in ByteDatatypes -> assignFromByteVariable(target, value) in WordDatatypes -> assignFromWordVariable(target, value) DataType.FLOAT -> assignFromFloatVariable(target, value) in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?") - else -> throw AssemblyError("weird arg datatype") + else -> throw AssemblyError("weird parameter datatype") } } is RegisterExpr -> { @@ -718,13 +718,17 @@ internal class AsmGen2(val program: Program, } } } else { - // pass arg via a register parameter - val paramRegister = sub.asmParameterRegisters[arg.index] + // pass parameter via a register parameter + val paramRegister = sub.asmParameterRegisters[parameter.index] val statusflag = paramRegister.statusflag val register = paramRegister.registerOrPair val stack = paramRegister.stack when { - stack==true -> TODO("param on stack") + stack -> { + // push arg onto the stack + // note: argument order is reversed (first argument will be deepest on the stack) + translateExpression(value) + } statusflag!=null -> { if (statusflag == Statusflag.Pc) { // this param needs to be set last, right before the jsr diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/AsmOptimizer.kt b/compiler/src/prog8/compiler/target/c64/codegen2/AsmOptimizer.kt index a20e5cac6..a0eb3be0a 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen2/AsmOptimizer.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen2/AsmOptimizer.kt @@ -54,7 +54,7 @@ fun optimizeAssembly(lines: MutableList): Int { numberOfOptimizations++ } - // TODO more assembly optimizations? + // TODO more assembly optimizations return numberOfOptimizations } @@ -101,7 +101,7 @@ fun optimizeSameAssignments(linesByFourteen: List>>): // optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats) // the float one is the one that requires 2*7=14 lines of code to check... - // @todo a better place to do this is in the Compiler instead and work on opcodes, and never even create the inefficient asm... + // @todo a better place to do this is in the Compiler instead and transform the Ast, and never even create the inefficient asm in the first place... val removeLines = mutableListOf() for (pair in linesByFourteen) { diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 3ae972b15..80ef6697a 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -107,13 +107,6 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV if(subroutine.canBeAsmSubroutine) { optimizationsDone++ return subroutine.intoAsmSubroutine() // TODO this doesn't work yet due to parameter vardecl issue - - // TODO fix parameter passing so this also works: -// asmsub aa(byte arg @ Y) -> clobbers() -> () { -// byte local = arg ; @todo fix 'undefined symbol arg' by some sort of alias name for the parameter -// A=44 -// } - } if(subroutine !in callgraph.usedSymbols && !forceOutput) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 357341067..cb1ca9828 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -19,6 +19,15 @@ these should call optimized pieces of assembly code, so they run as fast as poss For now, we have the ``memcopy``, ``memset`` and ``strlen`` builtin functions. +Fixes +^^^^^ + +fix asmsub parameters so this works:: + + asmsub aa(byte arg @ Y) -> clobbers() -> () { + byte local = arg ; @todo fix 'undefined symbol arg' that occurs here + A=44 + } More optimizations diff --git a/examples/test.p8 b/examples/test.p8 index fcceb571b..922317925 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,25 +4,14 @@ main { sub start() { - derp.dop() - - A=derp.dop.zzz - - derp.dop.zzz=3 - - - uword addr = &derp.dop.name ; @todo strange error "pointer-of operand must be the name of a heap variable" - c64scr.print(&derp.dop.name) + A = testsub(33) } -} - -derp { - - sub dop() { - ubyte zzz=33 - str name="irmen" - - A=54 + asmsub testsub(ubyte foo @stack) -> ubyte @stack { + %asm {{ + Y=44 + rts + }} } + }