diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 292d8f399..5056e8f7a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -317,7 +317,6 @@ class AsmGen(internal val program: Program, val (asmLabel, indirect) = getJumpTarget(stmt) jmp(asmLabel, indirect) } - is GoSub -> translate(stmt) is PostIncrDecr -> postincrdecrAsmGen.translate(stmt) is Label -> translate(stmt) is ConditionalBranch -> translate(stmt) @@ -441,15 +440,9 @@ class AsmGen(internal val program: Program, internal fun saveXbeforeCall(functionCall: IFunctionCall) = functioncallAsmGen.saveXbeforeCall(functionCall) - internal fun saveXbeforeCall(gosub: GoSub) = - functioncallAsmGen.saveXbeforeCall(gosub) - internal fun restoreXafterCall(functionCall: IFunctionCall) = functioncallAsmGen.restoreXafterCall(functionCall) - internal fun restoreXafterCall(gosub: GoSub) = - functioncallAsmGen.restoreXafterCall(gosub) - internal fun translateNormalAssignment(assign: AsmAssignment) = assignmentAsmGen.translateNormalAssignment(assign) @@ -872,18 +865,6 @@ $repeatLabel lda $counterVar } } - private fun translate(gosub: GoSub) { - val tgt = gosub.identifier.targetSubroutine(program) - if(tgt!=null && tgt.isAsmSubroutine) { - // no need to rescue X , this has been taken care of already - out(" jsr ${getJumpTarget(gosub)}") - } else { - saveXbeforeCall(gosub) - out(" jsr ${getJumpTarget(gosub)}") - restoreXafterCall(gosub) - } - } - private fun getJumpTarget(jump: Jump): Pair { val ident = jump.identifier val label = jump.generatedLabel @@ -903,8 +884,6 @@ $repeatLabel lda $counterVar } } - private fun getJumpTarget(gosub: GoSub): String = asmSymbolName(gosub.identifier) - private fun translate(ret: Return, withRts: Boolean=true) { ret.value?.let { returnvalue -> val sub = ret.definingSubroutine!! diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index bb58a46e6..a5a61ff43 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -7,7 +7,10 @@ import prog8.ast.expressions.AddressOf import prog8.ast.expressions.Expression import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteral -import prog8.ast.statements.* +import prog8.ast.statements.FunctionCallStatement +import prog8.ast.statements.InlineAssembly +import prog8.ast.statements.Subroutine +import prog8.ast.statements.SubroutineParameter import prog8.code.core.* import prog8.codegen.cpu6502.assignment.AsmAssignSource import prog8.codegen.cpu6502.assignment.AsmAssignTarget @@ -35,17 +38,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } } - internal fun saveXbeforeCall(gosub: GoSub) { - val sub = gosub.identifier.targetSubroutine(program) - if(sub?.shouldSaveX()==true) { - val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls - if(regSaveOnStack) - asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry) - else - asmgen.saveRegisterLocal(CpuRegister.X, gosub.definingSubroutine!!) - } - } - internal fun restoreXafterCall(stmt: IFunctionCall) { val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}") if(sub.shouldSaveX()) { @@ -57,17 +49,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } } - internal fun restoreXafterCall(gosub: GoSub) { - val sub = gosub.identifier.targetSubroutine(program) - if(sub?.shouldSaveX()==true) { - val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls - if(regSaveOnStack) - asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn) - else - asmgen.restoreRegisterLocal(CpuRegister.X) - } - } - internal fun optimizeIntArgsViaRegisters(sub: Subroutine) = (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) @@ -81,11 +62,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}") val subAsmName = asmgen.asmSymbolName(call.target) - if(!isExpression && !sub.isAsmSubroutine) { - if(!optimizeIntArgsViaRegisters(sub)) - throw AssemblyError("functioncall statements to non-asmsub should have been replaced by GoSub $call") - } - if(sub.isAsmSubroutine) { argumentsViaRegisters(sub, call) if (sub.inline && asmgen.options.optimize) { diff --git a/codeGenVirtual/test/Dummies.kt b/codeGenVirtual/test/Dummies.kt index 4b1510e46..21a95beb1 100644 --- a/codeGenVirtual/test/Dummies.kt +++ b/codeGenVirtual/test/Dummies.kt @@ -1,6 +1,9 @@ package prog8tests.vm.helpers -import prog8.code.core.* +import prog8.code.core.DataType +import prog8.code.core.Encoding +import prog8.code.core.IMemSizer +import prog8.code.core.IStringEncoding internal object DummyMemsizer : IMemSizer { diff --git a/codeGenVirtual/test/TestVmPeepholeOpt.kt b/codeGenVirtual/test/TestVmPeepholeOpt.kt index f4fc33fb9..262af3396 100644 --- a/codeGenVirtual/test/TestVmPeepholeOpt.kt +++ b/codeGenVirtual/test/TestVmPeepholeOpt.kt @@ -1,6 +1,5 @@ package prog8tests.vm -import io.kotest.assertions.fail import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import prog8.code.SymbolTable diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index 3097ca17a..62e7b2995 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -1,15 +1,18 @@ package prog8.optimizer -import prog8.ast.* +import prog8.ast.Node +import prog8.ast.Program import prog8.ast.base.FatalAstException import prog8.ast.base.UndefinedSymbolError import prog8.ast.expressions.* -import prog8.ast.statements.* +import prog8.ast.maySwapOperandOrder +import prog8.ast.statements.ForLoop +import prog8.ast.statements.VarDecl +import prog8.ast.statements.VarDeclType import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.AssociativeOperators import prog8.code.core.DataType -import prog8.compiler.BuiltinFunctions class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { diff --git a/codeOptimizers/src/prog8/optimizer/Inliner.kt b/codeOptimizers/src/prog8/optimizer/Inliner.kt index 7def61927..5c329332e 100644 --- a/codeOptimizers/src/prog8/optimizer/Inliner.kt +++ b/codeOptimizers/src/prog8/optimizer/Inliner.kt @@ -106,7 +106,7 @@ class Inliner(val program: Program): AstWalker() { } else false } - is Jump, is GoSub -> true + is Jump -> true else -> false } } @@ -170,15 +170,6 @@ class Inliner(val program: Program): AstWalker() { return super.before(program) } - override fun after(gosub: GoSub, parent: Node): Iterable { - val sub = gosub.identifier.targetStatement(program) as? Subroutine - return if(sub==null) - noModifications - else - possibleInlineFcallStmt(sub, gosub, parent) - - } - private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable { if(sub.inline && sub.parameters.isEmpty()) { require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index b4c6811a9..cd750a5fe 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -87,13 +87,16 @@ class StatementOptimizer(private val program: Program, if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) { val arg = functionCallStatement.args[0] if(!arg.isSimple && arg !is IFunctionCall) { - val name = getTempRegisterName(arg.inferType(program)) - val tempvar = IdentifierReference(name, functionCallStatement.position) - val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position) - return listOf( - IAstModification.InsertBefore(functionCallStatement, assignTempvar, parent as IStatementContainer), - IAstModification.ReplaceNode(arg, tempvar, functionCallStatement) - ) + val dt = arg.inferType(program) + if(dt.isInteger) { + val name = getTempRegisterName(dt) + val tempvar = IdentifierReference(name, functionCallStatement.position) + val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position) + return listOf( + IAstModification.InsertBefore(functionCallStatement, assignTempvar, parent as IStatementContainer), + IAstModification.ReplaceNode(arg, tempvar, functionCallStatement) + ) + } } } } @@ -244,25 +247,6 @@ class StatementOptimizer(private val program: Program, return noModifications } - - // NOTE: do NOT remove a jump to the next statement, because this will lead to wrong code when this occurs at the end of a subroutine - // if we want to optimize this away, it can be done later at code generation time. - - override fun after(gosub: GoSub, parent: Node): Iterable { - // if the next statement is return with no returnvalue, change into a regular jump if there are no parameters as well. - val subroutineParams = gosub.identifier.targetSubroutine(program)?.parameters - if(subroutineParams!=null && subroutineParams.isEmpty()) { - val returnstmt = gosub.nextSibling() as? Return - if(returnstmt!=null && returnstmt.value==null) { - return listOf( - IAstModification.Remove(returnstmt, parent as IStatementContainer), - IAstModification.ReplaceNode(gosub, Jump(null, gosub.identifier, null, gosub.position), parent) - ) - } - } - return noModifications - } - override fun before(assignment: Assignment, parent: Node): Iterable { val binExpr = assignment.value as? BinaryExpression diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt index b9881fd13..57685b8b8 100644 --- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt @@ -6,11 +6,12 @@ import com.github.michaelbull.result.getOrElse import com.github.michaelbull.result.mapError import prog8.ast.Program import prog8.ast.base.FatalAstException -import prog8.ast.determineGosubArguments import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.code.ast.* -import prog8.code.core.* +import prog8.code.core.DataType +import prog8.code.core.Position +import prog8.code.core.SourceCode import java.io.File import kotlin.io.path.Path import kotlin.io.path.isRegularFile @@ -43,7 +44,6 @@ class IntermediateAstMaker(val program: Program) { is Directive -> transform(statement) is ForLoop -> transform(statement) is FunctionCallStatement -> transform(statement) - is GoSub -> transform(statement) is IfElse -> transform(statement) is InlineAssembly -> transform(statement) is Jump -> transform(statement) @@ -85,12 +85,6 @@ class IntermediateAstMaker(val program: Program) { } private fun transform(srcAssign: Assignment): PtNode { - if(srcAssign.origin==AssignmentOrigin.PARAMETERASSIGN) { - // assignments that are setting the parameters for a function call, - // will be gathered at the GoSub itself later. - return PtNop(srcAssign.position) - } - val assign = PtAssignment(srcAssign.position) assign.add(transform(srcAssign.target)) assign.add(transformExpression(srcAssign.value)) @@ -229,28 +223,6 @@ class IntermediateAstMaker(val program: Program) { return call } - private fun transform(gosub: GoSub): PtFunctionCall { - // Gather the Goto and any preceding parameter assignments back into a single Function call node. - // (the reason it was split up in the first place, is because the Compiler Ast optimizers - // can then work on any complex expressions that are used as arguments.) - val arguments = determineGosubArguments(gosub) - - val parameters = gosub.identifier.targetSubroutine(program)!!.parameters - if(arguments.size != parameters.size) - throw FatalAstException("mismatched number of parameter assignments for function call") - - val target = transform(gosub.identifier) - val call = PtFunctionCall(target.targetName, true, DataType.UNDEFINED, gosub.position) - - // put arguments in correct order for the parameters - parameters.forEach { - val argument = arguments.getValue(it.name) - call.add(transformExpression(argument)) - } - - return call - } - private fun transform(srcIf: IfElse): PtIfElse { val ifelse = PtIfElse(srcIf.position) ifelse.add(transformExpression(srcIf.condition)) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index e899e7cc7..1a4934463 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -202,16 +202,6 @@ internal class AstChecker(private val program: Program, super.visit(jump) } - override fun visit(gosub: GoSub) { - val targetStatement = checkFunctionOrLabelExists(gosub.identifier, gosub) - if(targetStatement!=null) { - if(targetStatement is BuiltinFunctionPlaceholder) - errors.err("can't gosub to a builtin function", gosub.position) - } - - super.visit(gosub) - } - override fun visit(block: Block) { val addr = block.address if(addr!=null && addr>65535u) { diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index bd21e0ea7..6aea03fcf 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -10,7 +10,6 @@ import prog8.ast.statements.VarDeclOrigin import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.* -import prog8.compiler.printProgram internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) { diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt index eed889689..d8c30f90e 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -7,7 +7,10 @@ import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification -import prog8.code.core.* +import prog8.code.core.CompilationOptions +import prog8.code.core.Encoding +import prog8.code.core.IErrorReporter +import prog8.code.core.NumericDatatypes import prog8.code.target.C64Target import prog8.code.target.Cx16Target diff --git a/compiler/src/prog8/compiler/astprocessing/ParentNodeChecker.kt b/compiler/src/prog8/compiler/astprocessing/ParentNodeChecker.kt index af1fab594..f7c44782c 100644 --- a/compiler/src/prog8/compiler/astprocessing/ParentNodeChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/ParentNodeChecker.kt @@ -119,12 +119,6 @@ internal class ParentNodeChecker: AstWalker() { return noModifications } - override fun before(gosub: GoSub, parent: Node): Iterable { - if(gosub.parent!==parent) - throw FatalAstException("parent node mismatch at $gosub") - return noModifications - } - override fun before(label: Label, parent: Node): Iterable { if(label.parent!==parent) throw FatalAstException("parent node mismatch at $label") diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index fc2e93fba..a9c9ff5c7 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -7,9 +7,6 @@ import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.* -import prog8.code.target.VMTarget -import prog8.codegen.cpu6502.asmsub6502ArgsEvalOrder -import prog8.codegen.cpu6502.asmsub6502ArgsHaveRegisterClobberRisk import prog8.compiler.BuiltinFunctions internal class StatementReorderer(val program: Program, @@ -26,7 +23,6 @@ internal class StatementReorderer(val program: Program, // - in-place assignments are reordered a bit so that they are mostly of the form A = A // - sorts the choices in when statement. // - insert AddressOf (&) expression where required (string params to a UWORD function param etc.). - // - replace subroutine calls (statement) by just assigning the arguments to the parameters and then a GoSub to the routine. private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option") @@ -387,155 +383,4 @@ internal class StatementReorderer(val program: Program, ) return listOf(IAstModification.ReplaceNode(assign, strcopy, assign.parent)) } - - override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { - val function = functionCallStatement.target.targetStatement(program) - ?: throw FatalAstException("no target for $functionCallStatement") - checkUnusedReturnValues(functionCallStatement, function, errors) - return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) - } -} - - -internal fun tryReplaceCallWithGosub( - functionCallStatement: FunctionCallStatement, - parent: Node, - program: Program, - options: CompilationOptions -): Iterable { - val callee = functionCallStatement.target.targetStatement(program)!! - if(callee is Subroutine) { - if(callee.inline) - return emptyList() - return if(callee.isAsmSubroutine) { - if(options.compTarget.name==VMTarget.NAME) - emptyList() - else - tryReplaceCallAsmSubWithGosub(functionCallStatement, parent, callee) - } - else - tryReplaceCallNormalSubWithGosub(functionCallStatement, parent, callee, program) - } - return emptyList() -} - -private fun tryReplaceCallNormalSubWithGosub(call: FunctionCallStatement, parent: Node, callee: Subroutine, program: Program): Iterable { - val noModifications = emptyList() - - if(callee.parameters.isEmpty()) { - // 0 params -> just GoSub - return listOf(IAstModification.ReplaceNode(call, GoSub(call.target, call.position), parent)) - } - - if(callee.parameters.size==1) { - if(callee.parameters[0].type in IntegerDatatypes) { - // optimization: 1 integer param is passed via register(s) directly, not by assignment to param variable - return noModifications - } - } - else if(callee.parameters.size==2) { - if(callee.parameters[0].type in ByteDatatypes && callee.parameters[1].type in ByteDatatypes) { - // optimization: 2 simple byte param is passed via 2 registers directly, not by assignment to param variables - return noModifications - } - } - - val assignParams = - callee.parameters.zip(call.args).map { - var argumentValue = it.second - val paramIdentifier = IdentifierReference(callee.scopedName + it.first.name, argumentValue.position) - val argDt = argumentValue.inferType(program).getOrElse { throw FatalAstException("invalid dt") } - if(argDt in ArrayDatatypes) { - // pass the address of the array instead - if(argumentValue is IdentifierReference) - argumentValue = AddressOf(argumentValue, argumentValue.position) - } - Assignment(AssignTarget(paramIdentifier, null, null, argumentValue.position), argumentValue, AssignmentOrigin.PARAMETERASSIGN, argumentValue.position) - } - val scope = AnonymousScope(assignParams.toMutableList(), call.position) - scope.statements += GoSub(call.target, call.position) - return listOf(IAstModification.ReplaceNode(call, scope, parent)) -} - -private fun tryReplaceCallAsmSubWithGosub( - call: FunctionCallStatement, - parent: Node, - callee: Subroutine -): Iterable { - val noModifications = emptyList() - - if(callee.parameters.isEmpty()) { - // 0 params -> just GoSub - val scope = AnonymousScope(mutableListOf(), call.position) - if(callee.shouldSaveX()) { - scope.statements += FunctionCallStatement(IdentifierReference(listOf("rsavex"), call.position), mutableListOf(), true, call.position) - } - scope.statements += GoSub(call.target, call.position) - if(callee.shouldSaveX()) { - scope.statements += FunctionCallStatement(IdentifierReference(listOf("rrestorex"), call.position), mutableListOf(), true, call.position) - } - return listOf(IAstModification.ReplaceNode(call, scope, parent)) - } else if(!asmsub6502ArgsHaveRegisterClobberRisk(call.args, callee.asmParameterRegisters)) { - // No register clobber risk, let the asmgen assign values to the registers directly. - // this is more efficient than first evaluating them to the stack. - // As complex expressions will be flagged as a clobber-risk, these will be simplified below. - return noModifications - } else { - // clobber risk; evaluate the arguments on the CPU stack first (in reverse order)... - return makeGosubWithArgsViaCpuStack(call, call.position, parent, callee) - } -} - -private fun makeGosubWithArgsViaCpuStack( - call: IFunctionCall, - position: Position, - parent: Node, - callee: Subroutine -): Iterable { - - fun popCall(targetName: List, dt: DataType, position: Position): FunctionCallStatement { - return FunctionCallStatement( - IdentifierReference(listOf(if(dt in ByteDatatypes) "pop" else "popw"), position), - mutableListOf(IdentifierReference(targetName, position)), - true, position - ) - } - - fun pushCall(value: Expression, dt: DataType, position: Position): FunctionCallStatement { - val pushvalue = when(dt) { - DataType.UBYTE, DataType.UWORD -> value - in PassByReferenceDatatypes -> value - DataType.BYTE -> TypecastExpression(value, DataType.UBYTE, true, position) - DataType.WORD -> TypecastExpression(value, DataType.UWORD, true, position) - else -> throw FatalAstException("invalid dt $dt $value") - } - - return FunctionCallStatement( - IdentifierReference(listOf(if(dt in ByteDatatypes) "push" else "pushw"), position), - mutableListOf(pushvalue), - true, position - ) - } - - val argOrder = asmsub6502ArgsEvalOrder(callee) - val scope = AnonymousScope(mutableListOf(), position) - if(callee.shouldSaveX()) { - scope.statements += FunctionCallStatement(IdentifierReference(listOf("rsavex"), position), mutableListOf(), true, position) - } - argOrder.reversed().forEach { - val arg = call.args[it] - val param = callee.parameters[it] - scope.statements += pushCall(arg, param.type, arg.position) - } - // ... and pop them off again into the registers. - argOrder.forEach { - val param = callee.parameters[it] - val targetName = callee.scopedName + param.name - scope.statements += popCall(targetName, param.type, position) - } - scope.statements += GoSub(call.target, position) - if(callee.shouldSaveX()) { - scope.statements += FunctionCallStatement(IdentifierReference(listOf("rrestorex"), position), mutableListOf(), true, position) - } - return listOf(IAstModification.ReplaceNode(call as Node, scope, parent)) } diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 23732c5b3..9bfd3af56 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -6,7 +6,8 @@ import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.FatalAstException import prog8.ast.expressions.* -import prog8.ast.statements.* +import prog8.ast.statements.AnonymousScope +import prog8.ast.statements.Assignment import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.code.core.* @@ -218,9 +219,5 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } return noModifications } - - override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { - return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) - } } diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 41ec0c386..675d95e48 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -5,6 +5,7 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.types.instanceOf +import prog8.ast.IFunctionCall import prog8.ast.expressions.IdentifierReference import prog8.ast.statements.* import prog8.code.core.DataType @@ -286,6 +287,6 @@ class TestSubroutines: FunSpec({ stmts.last() shouldBe instanceOf() stmts.dropLast(1).last() shouldBe instanceOf() // this prevents the fallthrough - stmts.dropLast(2).last() shouldBe instanceOf() + stmts.dropLast(2).last() shouldBe instanceOf() } }) diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 92c2a88f6..5c7e8ab42 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -240,11 +240,6 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: } } - override fun visit(gosub: GoSub) { - output("gosub ") - gosub.identifier.accept(this) - } - override fun visit(ifElse: IfElse) { output("if ") ifElse.condition.accept(this) diff --git a/compilerAst/src/prog8/ast/Extensions.kt b/compilerAst/src/prog8/ast/Extensions.kt index ce83051d1..dcf0aab07 100644 --- a/compilerAst/src/prog8/ast/Extensions.kt +++ b/compilerAst/src/prog8/ast/Extensions.kt @@ -3,9 +3,10 @@ package prog8.ast import prog8.ast.base.FatalAstException import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.Expression -import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.InferredTypes -import prog8.ast.statements.* +import prog8.ast.statements.VarDecl +import prog8.ast.statements.VarDeclOrigin +import prog8.ast.statements.VarDeclType import prog8.code.core.DataType import prog8.code.core.Position import prog8.code.core.ZeropageWish @@ -60,35 +61,6 @@ fun getTempRegisterName(dt: InferredTypes.InferredType): List { } } -fun determineGosubArguments(gosub: GoSub): Map { - val parent = gosub.parent as IStatementContainer - val gosubIdx = parent.statements.indexOf(gosub) - val previousNodes = parent.statements.subList(0, gosubIdx).reversed() - - val arguments = mutableMapOf() - for (node in previousNodes) { - if(node !is Assignment || node.origin!=AssignmentOrigin.PARAMETERASSIGN) - break - arguments[node.target.identifier!!.nameInSource.last()] = node.value - } - - // instead of just assigning to the parameters, another way is to use push()/pop() - if(previousNodes.isNotEmpty()) { - val first = previousNodes[0] as? IFunctionCall - if(first!=null && (first.target.nameInSource.singleOrNull() in arrayOf("pop", "popw"))) { - val numPops = previousNodes.indexOfFirst { (it as? IFunctionCall)?.target?.nameInSource?.singleOrNull() !in arrayOf("pop", "popw") } - val pops = previousNodes.subList(0, numPops) - val pushes = previousNodes.subList(numPops, numPops+numPops).reversed() - for ((push, pop) in pushes.zip(pops)) { - val name = ((pop as IFunctionCall).args.single() as IdentifierReference).nameInSource.last() - val arg = (push as IFunctionCall).args.single() - arguments[name] = arg - } - } - } - return arguments -} - fun maySwapOperandOrder(binexpr: BinaryExpression): Boolean { fun ok(expr: Expression): Boolean { return when(expr) { diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 3b759a062..c75cfa8be 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -584,24 +584,6 @@ class Jump(var address: UInt?, "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)" } -// a GoSub is ONLY created internally for calling subroutines, there's no syntax for it in the language -class GoSub(val identifier: IdentifierReference, override val position: Position) : Statement() { - override lateinit var parent: Node - - override fun linkParents(parent: Node) { - this.parent = parent - identifier.linkParents(this) - } - - override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") - override fun copy() = GoSub(identifier.copy(), position) - override fun accept(visitor: IAstVisitor) = visitor.visit(this) - override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - - override fun toString() = - "GoSub($identifier; pos=$position)" -} - class FunctionCallStatement(override var target: IdentifierReference, override var args: MutableList, val void: Boolean, @@ -1065,7 +1047,6 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position // Calls to builtin functions will be replaced with this node just before handing the Ast to the codegen. // this is meant to eventually (?) be able to not have any FunctionCallStatement nodes to worry about anymore -// in the codegen, because they have been converted into GoSub (for instance) or this node. // However, if/when the codegen is moved over to use the new CodeAst (PtProgram etc. etc.) this is all moot. class BuiltinFunctionCallStatement(override var target: IdentifierReference, override var args: MutableList, diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index b333daed0..213bc648f 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -114,7 +114,6 @@ abstract class AstWalker { open fun before(ifElse: IfElse, parent: Node): Iterable = noModifications open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable = noModifications open fun before(jump: Jump, parent: Node): Iterable = noModifications - open fun before(gosub: GoSub, parent: Node): Iterable = noModifications open fun before(label: Label, parent: Node): Iterable = noModifications open fun before(memread: DirectMemoryRead, parent: Node): Iterable = noModifications open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable = noModifications @@ -157,7 +156,6 @@ abstract class AstWalker { open fun after(ifElse: IfElse, parent: Node): Iterable = noModifications open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable = noModifications open fun after(jump: Jump, parent: Node): Iterable = noModifications - open fun after(gosub: GoSub, parent: Node): Iterable = noModifications open fun after(label: Label, parent: Node): Iterable = noModifications open fun after(memread: DirectMemoryRead, parent: Node): Iterable = noModifications open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable = noModifications @@ -307,12 +305,6 @@ abstract class AstWalker { track(after(jump, parent), jump, parent) } - fun visit(gosub: GoSub, parent: Node) { - track(before(gosub, parent), gosub, parent) - gosub.identifier.accept(this, gosub) - track(after(gosub, parent), gosub, parent) - } - fun visit(ifElse: IfElse, parent: Node) { track(before(ifElse, parent), ifElse, parent) ifElse.condition.accept(this, ifElse) diff --git a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt index b2e80c739..c02170c08 100644 --- a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt +++ b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt @@ -70,10 +70,6 @@ interface IAstVisitor { jump.identifier?.accept(this) } - fun visit(gosub: GoSub) { - gosub.identifier.accept(this) - } - fun visit(ifElse: IfElse) { ifElse.condition.accept(this) ifElse.truepart.accept(this) diff --git a/compilerAst/src/prog8/compiler/CallGraph.kt b/compilerAst/src/prog8/compiler/CallGraph.kt index 026adef34..ccac28876 100644 --- a/compilerAst/src/prog8/compiler/CallGraph.kt +++ b/compilerAst/src/prog8/compiler/CallGraph.kt @@ -100,17 +100,6 @@ class CallGraph(private val program: Program, private val allowMissingIdentifier super.visit(jump) } - override fun visit(gosub: GoSub) { - val otherSub = gosub.identifier.targetSubroutine(program) - if (otherSub != null) { - gosub.definingSubroutine?.let { thisSub -> - calls[thisSub] = calls.getValue(thisSub) + otherSub - calledBy[otherSub] = calledBy.getValue(otherSub) + gosub - } - } - super.visit(gosub) - } - override fun visit(identifier: IdentifierReference) { val target = identifier.targetStatement(program) if(allowMissingIdentifierTargetVarDecls) { diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 6f48ed899..bc63c40c5 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -931,7 +931,7 @@ callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for syscall(callnr), syscall1(callnr, arg), syscall2(callnr, arg1, arg2), syscall3(callnr, arg1, arg2, arg3) Functions for doing a system call on targets that support this. Currently no actual target uses this though except, possibly, the experimental code generation target! - The regular 6502 based compiler targets just use a gosub to asmsub kernal routines at + The regular 6502 based compiler targets just use a subroutine call to asmsub kernal routines at specific memory locations. So these builtin function calls are not useful yet except for experimentation in new code generation targets. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 7af5666fe..f30c5bf1e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -17,7 +17,7 @@ Need help with Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ Compiler: -- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is. This info is needed for more advanced optimizations and later code generation steps. +- vm Instructions needs to know what the read-registers/memory are, and what the write-register/memory is. This info is needed for more advanced optimizations and later code generation steps. - vm: implement remaining sin/cos functions in math.p8 - vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now - vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8 @@ -44,9 +44,10 @@ Compiler: - add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing - ast: don't rewrite by-reference parameter type to uword, but keep the original type (str, array) BUT that makes the handling of these types different between the scope they are defined in, and the - scope they get passed in by reference... unless we make str and array types by-reference ALWAYS? BUT that - makes simple code accessing them in the declared scope very slow because that then has to always go through + scope they get passed in by reference... unless we make str and array types by-reference ALWAYS? + BUT that makes simple code accessing them in the declared scope very slow because that then has to always go through the pointer rather than directly referencing the variable symbol in the generated asm.... + Or maybe make codegen smart to check if it's a subroutine parameter or local declared variable? Libraries: @@ -67,7 +68,7 @@ Expressions: - rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'? "Three address code" was mentioned. https://en.wikipedia.org/wiki/Three-address_code these variables have to be unique for each subroutine because they could otherwise be interfered with from irq routines etc. - The VM IL solves this already (by using unlimited registers) but still lacks a translation to 6502. + The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502. - this removes the need for the BinExprSplitter? (which is problematic and very limited now) and perhaps the assignment splitting in BeforeAsmAstChanger too