From f0d4c3aba9a2b85155fa243a138962b27c7e86dd Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 22 Mar 2020 16:06:34 +0100 Subject: [PATCH] moved initialvalues to asmgen, fixed sgn bug and internal float 0.0 variable disappearing bug --- compiler/res/prog8lib/c64flt.p8 | 11 ++- compiler/src/prog8/ast/AstToplevel.kt | 2 +- compiler/src/prog8/ast/base/Extensions.kt | 14 ++- .../prog8/ast/processing/TypecastsAdder.kt | 14 --- .../src/prog8/ast/statements/AstStatements.kt | 3 - compiler/src/prog8/compiler/Main.kt | 12 ++- .../target/AsmInitialValuesGatherer.kt | 31 +++++++ .../target/AsmVariablePreparer.kt} | 8 +- .../compiler/target/CompilationTarget.kt | 8 +- .../compiler/target/c64/codegen/AsmGen.kt | 31 +++++-- compiler/src/prog8/optimizer/CallGraph.kt | 2 +- examples/arithmetic/sgn.p8 | 8 +- examples/test.p8 | 93 ++++++++----------- 13 files changed, 135 insertions(+), 102 deletions(-) create mode 100644 compiler/src/prog8/compiler/target/AsmInitialValuesGatherer.kt rename compiler/src/prog8/{ast/processing/AnonScopeVarsToSubroutineMover.kt => compiler/target/AsmVariablePreparer.kt} (85%) diff --git a/compiler/res/prog8lib/c64flt.p8 b/compiler/res/prog8lib/c64flt.p8 index 1f530adfe..7cbf08dca 100644 --- a/compiler/res/prog8lib/c64flt.p8 +++ b/compiler/res/prog8lib/c64flt.p8 @@ -10,8 +10,8 @@ c64flt { ; ---- this block contains C-64 floating point related functions ---- - const float PI = 3.141592653589793 - const float TWOPI = 6.283185307179586 + const float PI = 3.141592653589793 + const float TWOPI = 6.283185307179586 ; ---- C64 basic and kernal ROM float constants and functions ---- @@ -34,7 +34,8 @@ c64flt { &float FL_PIHALF = $e2e0 ; PI / 2 &float FL_TWOPI = $e2e5 ; 2 * PI &float FL_FR4 = $e2ea ; .25 - float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel + ; oddly enough, 0.0 isn't available in the kernel. + float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel ; note: fac1/2 might get clobbered even if not mentioned in the function's name. @@ -209,8 +210,8 @@ sub print_fln (float value) { ; ---- prints the floating point value (with a newline at the end) using basic rom routines %asm {{ stx c64.SCRATCH_ZPREGX - lda #print_fln_value + lda #value jsr MOVFM ; load float into fac1 jsr FPRINTLN ; print fac1 with newline ldx c64.SCRATCH_ZPREGX diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index 6444e8a60..d436e7f4e 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -308,7 +308,7 @@ class GlobalNamespace(val modules: List): Node, INameScope { return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) { is Label, is VarDecl, is Block, is Subroutine -> stmt null -> null - else -> throw SyntaxError("wrong identifier target: $stmt", stmt.position) + else -> throw SyntaxError("wrong identifier target for $scopedName: $stmt", stmt.position) } } } diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index 700f97578..1109e0b37 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -3,7 +3,11 @@ package prog8.ast.base import prog8.ast.Module import prog8.ast.Program import prog8.ast.processing.* +import prog8.ast.statements.Block +import prog8.ast.statements.VarDecl import prog8.compiler.CompilationOptions +import prog8.compiler.target.AsmInitialValuesGatherer +import prog8.compiler.target.AsmVariablePreparer import prog8.optimizer.FlattenAnonymousScopesAndNopRemover @@ -12,12 +16,18 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: Err checker.visit(this) } -internal fun Program.moveAnonScopeVarsToSubroutine(errors: ErrorReporter) { - val mover = AnonScopeVarsToSubroutineMover(errors) +internal fun Program.prepareAsmVariables(errors: ErrorReporter) { + val mover = AsmVariablePreparer(this, errors) mover.visit(this) mover.applyModifications() } +internal fun Program.gatherInitialValues(): Map> { + val gather = AsmInitialValuesGatherer(this) + gather.visit(this) + return gather.initialValues +} + internal fun Program.reorderStatements() { val initvalueCreator = AddressOfInserter(this) initvalueCreator.visit(this) diff --git a/compiler/src/prog8/ast/processing/TypecastsAdder.kt b/compiler/src/prog8/ast/processing/TypecastsAdder.kt index 831a94a8c..6ab199ebe 100644 --- a/compiler/src/prog8/ast/processing/TypecastsAdder.kt +++ b/compiler/src/prog8/ast/processing/TypecastsAdder.kt @@ -16,20 +16,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke * (this includes function call arguments) */ - override fun after(decl: VarDecl, parent: Node): Iterable { - - // collect all variables that have an initialisation value - val declValue = decl.value - if(declValue!=null - && decl.type== VarDeclType.VAR - && decl.datatype in NumericDatatypes - && declValue.constValue(program)!=null) { - decl.definingBlock().initialValues[decl.scopedname] = decl - } - - return emptyList() - } - override fun after(expr: BinaryExpression, parent: Node): Iterable { val leftDt = expr.left.inferType(program) val rightDt = expr.right.inferType(program) diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 778effe61..f3ac56cdc 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -73,7 +73,6 @@ class Block(override val name: String, val idx = statements.indexOf(node) statements[idx] = replacement } - val initialValues = mutableMapOf() // will be gathered by one of the Ast processing steps override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this) @@ -271,8 +270,6 @@ class VarDecl(val type: VarDeclType, override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) - val scopedname: String by lazy { makeScopedName(name) } - override fun toString(): String { return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)" } diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index cb193389b..4c3abd976 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -116,11 +116,17 @@ fun compileProgram(filepath: Path, // printAst(programAst) if(writeAssembly) { - // asm generation directly from the Ast, no need for intermediate code + // asm generation directly from the Ast, val zeropage = CompilationTarget.machine.getZeropage(compilerOptions) - programAst.moveAnonScopeVarsToSubroutine(errors) + programAst.prepareAsmVariables(errors) errors.handle() - val assembly = CompilationTarget.asmGenerator(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize) + val initialValues = programAst.gatherInitialValues() + val assembly = CompilationTarget.asmGenerator( + programAst, + zeropage, + initialValues, + compilerOptions, + outputDir).compileToAssembly(optimize) assembly.assemble(compilerOptions) programName = assembly.name } diff --git a/compiler/src/prog8/compiler/target/AsmInitialValuesGatherer.kt b/compiler/src/prog8/compiler/target/AsmInitialValuesGatherer.kt new file mode 100644 index 000000000..d36c5de22 --- /dev/null +++ b/compiler/src/prog8/compiler/target/AsmInitialValuesGatherer.kt @@ -0,0 +1,31 @@ +package prog8.compiler.target + +import prog8.ast.Program +import prog8.ast.base.NumericDatatypes +import prog8.ast.base.VarDeclType +import prog8.ast.processing.IAstVisitor +import prog8.ast.statements.Block +import prog8.ast.statements.VarDecl + +internal class AsmInitialValuesGatherer(val program: Program): IAstVisitor { + val initialValues = mutableMapOf>() + + override fun visit(decl: VarDecl) { + // collect all variables that have an initialisation value + super.visit(decl) + val declValue = decl.value + if(declValue!=null + && decl.type== VarDeclType.VAR + && decl.datatype in NumericDatatypes + && declValue.constValue(program)!=null) { + + val block = decl.definingBlock() + var blockInits = initialValues[block] + if(blockInits==null) { + blockInits = mutableMapOf() + initialValues[block] = blockInits + } + blockInits[decl.makeScopedName(decl.name)] = decl + } + } +} diff --git a/compiler/src/prog8/ast/processing/AnonScopeVarsToSubroutineMover.kt b/compiler/src/prog8/compiler/target/AsmVariablePreparer.kt similarity index 85% rename from compiler/src/prog8/ast/processing/AnonScopeVarsToSubroutineMover.kt rename to compiler/src/prog8/compiler/target/AsmVariablePreparer.kt index 9362e445f..075bfded3 100644 --- a/compiler/src/prog8/ast/processing/AnonScopeVarsToSubroutineMover.kt +++ b/compiler/src/prog8/compiler/target/AsmVariablePreparer.kt @@ -1,13 +1,17 @@ -package prog8.ast.processing +package prog8.compiler.target import prog8.ast.Node +import prog8.ast.Program import prog8.ast.base.ErrorReporter +import prog8.ast.processing.AstWalker +import prog8.ast.processing.IAstModification import prog8.ast.statements.AnonymousScope import prog8.ast.statements.Subroutine import prog8.ast.statements.VarDecl -class AnonScopeVarsToSubroutineMover(val errors: ErrorReporter): AstWalker() { +class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstWalker() { + override fun after(scope: AnonymousScope, parent: Node): Iterable { val decls = scope.statements.filterIsInstance() val sub = scope.definingSubroutine() diff --git a/compiler/src/prog8/compiler/target/CompilationTarget.kt b/compiler/src/prog8/compiler/target/CompilationTarget.kt index 5c29ab8d8..703ae5f3b 100644 --- a/compiler/src/prog8/compiler/target/CompilationTarget.kt +++ b/compiler/src/prog8/compiler/target/CompilationTarget.kt @@ -1,16 +1,22 @@ package prog8.compiler.target import prog8.ast.Program +import prog8.ast.statements.Block +import prog8.ast.statements.VarDecl import prog8.compiler.CompilationOptions import prog8.compiler.Zeropage import java.nio.file.Path + +typealias InitialValues = Map> + + internal interface CompilationTarget { companion object { lateinit var name: String lateinit var machine: IMachineDefinition lateinit var encodeString: (str: String, altEncoding: Boolean) -> List lateinit var decodeString: (bytes: List, altEncoding: Boolean) -> String - lateinit var asmGenerator: (Program, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator + lateinit var asmGenerator: (Program, Zeropage, InitialValues, CompilationOptions, Path) -> IAssemblyGenerator } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index b551feb4e..21826b390 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -9,6 +9,7 @@ import prog8.ast.statements.* import prog8.compiler.* import prog8.compiler.target.IAssemblyGenerator import prog8.compiler.target.IAssemblyProgram +import prog8.compiler.target.InitialValues import prog8.compiler.target.c64.AssemblyProgram import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX @@ -26,9 +27,10 @@ import kotlin.math.absoluteValue internal class AsmGen(private val program: Program, - private val zeropage: Zeropage, - private val options: CompilationOptions, - private val outputDir: Path): IAssemblyGenerator { + private val zeropage: Zeropage, + private val initialValues: InitialValues, + private val options: CompilationOptions, + private val outputDir: Path): IAssemblyGenerator { private val assemblyLines = mutableListOf() private val globalFloatConsts = mutableMapOf() // all float values in the entire program (value -> varname) @@ -126,7 +128,7 @@ internal class AsmGen(private val program: Program, out(" ldx #\$ff\t; init estack pointer") out(" ; initialize the variables in each block") - program.allBlocks().filter { it.initialValues.any() }.forEach { out(" jsr ${it.name}.prog8_init_vars") } + program.allBlocks().filter { it in initialValues }.forEach { out(" jsr ${it.name}.prog8_init_vars") } out(" clc") when (zeropage.exitProgramStrategy) { @@ -173,10 +175,12 @@ internal class AsmGen(private val program: Program, // if any global vars need to be initialized, generate a subroutine that does this // it will be called from program init. - if(block.initialValues.isNotEmpty()) { + if(block in initialValues) { out("prog8_init_vars\t.proc\n") - block.initialValues.forEach { (scopedName, decl) -> - val target = AssignTarget(null, IdentifierReference(scopedName.split('.'), decl.position), null, null, decl.position) + initialValues.getValue(block).forEach { (scopedName, decl) -> + val scopedFullName = scopedName.split('.') + require(scopedFullName.first()==block.name) + val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position) val assign = Assignment(target, null, decl.value!!, decl.position) assign.linkParents(decl.parent) assignmentAsmGen.translate(assign) @@ -229,7 +233,7 @@ internal class AsmGen(private val program: Program, val variables = statements.filterIsInstance().filter { it.type==VarDeclType.VAR } for(variable in variables) { // should NOT allocate subroutine parameters on the zero page - val fullName = variable.scopedname + val fullName = variable.makeScopedName(variable.name) val zpVar = allocatedZeropageVariables[fullName] if(zpVar==null) { // This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space) @@ -305,7 +309,14 @@ internal class AsmGen(private val program: Program, } } DataType.ARRAY_F -> { - val array = (decl.value as ArrayLiteralValue).value + val array = + if(decl.value!=null) + (decl.value as ArrayLiteralValue).value + else { + // no init value, use zeros + val zero = decl.asDefaultValueDecl(decl.parent).value!! + Array(decl.arraysize!!.size()!!) { zero } + } val floatFills = array.map { val number = (it as NumericLiteralValue).number makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number)) @@ -359,7 +370,7 @@ internal class AsmGen(private val program: Program, // non-string variables normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach { - if(it.scopedname !in allocatedZeropageVariables) + if(it.makeScopedName(it.name) !in allocatedZeropageVariables) vardecl2asm(it) } } diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index 67201e736..9679050c4 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -127,7 +127,7 @@ class CallGraph(private val program: Program) : IAstVisitor { } override fun visit(decl: VarDecl) { - if (decl.autogeneratedDontRemove || (decl.definingModule().isLibraryModule && decl.type != VarDeclType.VAR)) { + if (decl.autogeneratedDontRemove || decl.definingModule().isLibraryModule) { // make sure autogenerated vardecls are in the used symbols addNodeAndParentScopes(decl) } diff --git a/examples/arithmetic/sgn.p8 b/examples/arithmetic/sgn.p8 index 3361dc7a0..bb8b74ecc 100644 --- a/examples/arithmetic/sgn.p8 +++ b/examples/arithmetic/sgn.p8 @@ -79,10 +79,10 @@ main { uw1 = 2222 uw2 = 999 - if sgn(uw2 as word - uw1 as word) != -1 - c64scr.print("sgn2 error6\n") - if sgn(uw2 - uw1) != -1 - c64scr.print("sgn2 error6\n") + if sgn((uw2 as word) - (uw1 as word)) != -1 + c64scr.print("sgn2 error6a\n") + if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned + c64scr.print("sgn2 error6b\n") f1 = 3.45 f2 = 1.11 diff --git a/examples/test.p8 b/examples/test.p8 index 30b878095..032dcbce1 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,64 +5,45 @@ main { - sub subje() { - ubyte xyz = 123 - ubyte foo - c64scr.print_ub(xyz) - c64.CHROUT('\n') - c64scr.print_ub(foo) - c64.CHROUT('\n') - xyz++ - foo++ - } - sub start() { - ubyte xyz = 99 ; TODO fix compiler error when removing unused var - word wcosa = cos8(xyz) - word wcosa_sinb = wcosa / 128 + ubyte ub1 + ubyte ub2 = 99 + uword uw1 + uword uw2 = 9999 + ubyte[5] array1 + ubyte[5] array2 = [22,33,44,55,66] - subje() - subje() - subje() + c64scr.print_ub(ub1) + c64.CHROUT(',') + c64scr.print_ub(ub2) + c64.CHROUT(',') + c64scr.print_uw(uw1) + c64.CHROUT(',') + c64scr.print_uw(uw2) + c64.CHROUT(',') + c64scr.print_ub(array1[0]) + c64.CHROUT(',') + c64scr.print_ub(array2[0]) + c64.CHROUT('\n') -; ubyte ub1 -; ubyte ub2 = 99 -; uword uw1 -; uword uw2 = 9999 -; ubyte[5] array1 -; ubyte[5] array2 = [22,33,44,55,66] -; -; c64scr.print_ub(ub1) -; c64.CHROUT(',') -; c64scr.print_ub(ub2) -; c64.CHROUT(',') -; c64scr.print_uw(uw1) -; c64.CHROUT(',') -; c64scr.print_uw(uw2) -; c64.CHROUT(',') -; c64scr.print_ub(array1[0]) -; c64.CHROUT(',') -; c64scr.print_ub(array2[0]) -; c64.CHROUT('\n') -; -; ub1++ -; ub2++ -; uw1++ -; uw2++ -; array1[0]++ -; array2[0]++ -; -; c64scr.print_ub(ub1) -; c64.CHROUT(',') -; c64scr.print_ub(ub2) -; c64.CHROUT(',') -; c64scr.print_uw(uw1) -; c64.CHROUT(',') -; c64scr.print_uw(uw2) -; c64.CHROUT(',') -; c64scr.print_ub(array1[0]) -; c64.CHROUT(',') -; c64scr.print_ub(array2[0]) -; c64.CHROUT('\n') + ub1++ + ub2++ + uw1++ + uw2++ + array1[0]++ + array2[0]++ + + c64scr.print_ub(ub1) + c64.CHROUT(',') + c64scr.print_ub(ub2) + c64.CHROUT(',') + c64scr.print_uw(uw1) + c64.CHROUT(',') + c64scr.print_uw(uw2) + c64.CHROUT(',') + c64scr.print_ub(array1[0]) + c64.CHROUT(',') + c64scr.print_ub(array2[0]) + c64.CHROUT('\n') } }