moved initialvalues to asmgen, fixed sgn bug and internal float 0.0 variable disappearing bug

This commit is contained in:
Irmen de Jong 2020-03-22 16:06:34 +01:00
parent 3a99115070
commit f0d4c3aba9
13 changed files with 135 additions and 102 deletions

View File

@ -10,8 +10,8 @@
c64flt { c64flt {
; ---- this block contains C-64 floating point related functions ---- ; ---- this block contains C-64 floating point related functions ----
const float PI = 3.141592653589793 const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586
; ---- C64 basic and kernal ROM float constants and functions ---- ; ---- C64 basic and kernal ROM float constants and functions ----
@ -34,7 +34,8 @@ c64flt {
&float FL_PIHALF = $e2e0 ; PI / 2 &float FL_PIHALF = $e2e0 ; PI / 2
&float FL_TWOPI = $e2e5 ; 2 * PI &float FL_TWOPI = $e2e5 ; 2 * PI
&float FL_FR4 = $e2ea ; .25 &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. ; 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 ; ---- prints the floating point value (with a newline at the end) using basic rom routines
%asm {{ %asm {{
stx c64.SCRATCH_ZPREGX stx c64.SCRATCH_ZPREGX
lda #<print_fln_value lda #<value
ldy #>print_fln_value ldy #>value
jsr MOVFM ; load float into fac1 jsr MOVFM ; load float into fac1
jsr FPRINTLN ; print fac1 with newline jsr FPRINTLN ; print fac1 with newline
ldx c64.SCRATCH_ZPREGX ldx c64.SCRATCH_ZPREGX

View File

@ -308,7 +308,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) { return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
is Label, is VarDecl, is Block, is Subroutine -> stmt is Label, is VarDecl, is Block, is Subroutine -> stmt
null -> null null -> null
else -> throw SyntaxError("wrong identifier target: $stmt", stmt.position) else -> throw SyntaxError("wrong identifier target for $scopedName: $stmt", stmt.position)
} }
} }
} }

View File

@ -3,7 +3,11 @@ package prog8.ast.base
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.processing.* import prog8.ast.processing.*
import prog8.ast.statements.Block
import prog8.ast.statements.VarDecl
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.target.AsmInitialValuesGatherer
import prog8.compiler.target.AsmVariablePreparer
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
@ -12,12 +16,18 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: Err
checker.visit(this) checker.visit(this)
} }
internal fun Program.moveAnonScopeVarsToSubroutine(errors: ErrorReporter) { internal fun Program.prepareAsmVariables(errors: ErrorReporter) {
val mover = AnonScopeVarsToSubroutineMover(errors) val mover = AsmVariablePreparer(this, errors)
mover.visit(this) mover.visit(this)
mover.applyModifications() mover.applyModifications()
} }
internal fun Program.gatherInitialValues(): Map<Block, Map<String, VarDecl>> {
val gather = AsmInitialValuesGatherer(this)
gather.visit(this)
return gather.initialValues
}
internal fun Program.reorderStatements() { internal fun Program.reorderStatements() {
val initvalueCreator = AddressOfInserter(this) val initvalueCreator = AddressOfInserter(this)
initvalueCreator.visit(this) initvalueCreator.visit(this)

View File

@ -16,20 +16,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
* (this includes function call arguments) * (this includes function call arguments)
*/ */
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// 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<IAstModification> { override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val leftDt = expr.left.inferType(program) val leftDt = expr.left.inferType(program)
val rightDt = expr.right.inferType(program) val rightDt = expr.right.inferType(program)

View File

@ -73,7 +73,6 @@ class Block(override val name: String,
val idx = statements.indexOf(node) val idx = statements.indexOf(node)
statements[idx] = replacement statements[idx] = replacement
} }
val initialValues = mutableMapOf<String, VarDecl>() // will be gathered by one of the Ast processing steps
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = 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: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
val scopedname: String by lazy { makeScopedName(name) }
override fun toString(): String { override fun toString(): String {
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)" return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
} }

View File

@ -116,11 +116,17 @@ fun compileProgram(filepath: Path,
// printAst(programAst) // printAst(programAst)
if(writeAssembly) { 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) val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
programAst.moveAnonScopeVarsToSubroutine(errors) programAst.prepareAsmVariables(errors)
errors.handle() 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) assembly.assemble(compilerOptions)
programName = assembly.name programName = assembly.name
} }

View File

@ -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<Block, MutableMap<String, VarDecl>>()
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
}
}
}

View File

@ -1,13 +1,17 @@
package prog8.ast.processing package prog8.compiler.target
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.ErrorReporter import prog8.ast.base.ErrorReporter
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.AnonymousScope import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl 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<IAstModification> { override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
val decls = scope.statements.filterIsInstance<VarDecl>() val decls = scope.statements.filterIsInstance<VarDecl>()
val sub = scope.definingSubroutine() val sub = scope.definingSubroutine()

View File

@ -1,16 +1,22 @@
package prog8.compiler.target package prog8.compiler.target
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.statements.Block
import prog8.ast.statements.VarDecl
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage import prog8.compiler.Zeropage
import java.nio.file.Path import java.nio.file.Path
typealias InitialValues = Map<Block, Map<String, VarDecl>>
internal interface CompilationTarget { internal interface CompilationTarget {
companion object { companion object {
lateinit var name: String lateinit var name: String
lateinit var machine: IMachineDefinition lateinit var machine: IMachineDefinition
lateinit var encodeString: (str: String, altEncoding: Boolean) -> List<Short> lateinit var encodeString: (str: String, altEncoding: Boolean) -> List<Short>
lateinit var decodeString: (bytes: List<Short>, altEncoding: Boolean) -> String lateinit var decodeString: (bytes: List<Short>, altEncoding: Boolean) -> String
lateinit var asmGenerator: (Program, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator lateinit var asmGenerator: (Program, Zeropage, InitialValues, CompilationOptions, Path) -> IAssemblyGenerator
} }
} }

View File

@ -9,6 +9,7 @@ import prog8.ast.statements.*
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.IAssemblyGenerator import prog8.compiler.target.IAssemblyGenerator
import prog8.compiler.target.IAssemblyProgram import prog8.compiler.target.IAssemblyProgram
import prog8.compiler.target.InitialValues
import prog8.compiler.target.c64.AssemblyProgram import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
@ -26,9 +27,10 @@ import kotlin.math.absoluteValue
internal class AsmGen(private val program: Program, internal class AsmGen(private val program: Program,
private val zeropage: Zeropage, private val zeropage: Zeropage,
private val options: CompilationOptions, private val initialValues: InitialValues,
private val outputDir: Path): IAssemblyGenerator { private val options: CompilationOptions,
private val outputDir: Path): IAssemblyGenerator {
private val assemblyLines = mutableListOf<String>() private val assemblyLines = mutableListOf<String>()
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname) private val globalFloatConsts = mutableMapOf<Double, String>() // 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(" ldx #\$ff\t; init estack pointer")
out(" ; initialize the variables in each block") 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") out(" clc")
when (zeropage.exitProgramStrategy) { 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 // if any global vars need to be initialized, generate a subroutine that does this
// it will be called from program init. // it will be called from program init.
if(block.initialValues.isNotEmpty()) { if(block in initialValues) {
out("prog8_init_vars\t.proc\n") out("prog8_init_vars\t.proc\n")
block.initialValues.forEach { (scopedName, decl) -> initialValues.getValue(block).forEach { (scopedName, decl) ->
val target = AssignTarget(null, IdentifierReference(scopedName.split('.'), decl.position), null, null, decl.position) 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) val assign = Assignment(target, null, decl.value!!, decl.position)
assign.linkParents(decl.parent) assign.linkParents(decl.parent)
assignmentAsmGen.translate(assign) assignmentAsmGen.translate(assign)
@ -229,7 +233,7 @@ internal class AsmGen(private val program: Program,
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR } val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
for(variable in variables) { for(variable in variables) {
// should NOT allocate subroutine parameters on the zero page // should NOT allocate subroutine parameters on the zero page
val fullName = variable.scopedname val fullName = variable.makeScopedName(variable.name)
val zpVar = allocatedZeropageVariables[fullName] val zpVar = allocatedZeropageVariables[fullName]
if(zpVar==null) { 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) // 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 -> { 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 floatFills = array.map {
val number = (it as NumericLiteralValue).number val number = (it as NumericLiteralValue).number
makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number)) makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number))
@ -359,7 +370,7 @@ internal class AsmGen(private val program: Program,
// non-string variables // non-string variables
normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach { normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
if(it.scopedname !in allocatedZeropageVariables) if(it.makeScopedName(it.name) !in allocatedZeropageVariables)
vardecl2asm(it) vardecl2asm(it)
} }
} }

View File

@ -127,7 +127,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
override fun visit(decl: VarDecl) { 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 // make sure autogenerated vardecls are in the used symbols
addNodeAndParentScopes(decl) addNodeAndParentScopes(decl)
} }

View File

@ -79,10 +79,10 @@ main {
uw1 = 2222 uw1 = 2222
uw2 = 999 uw2 = 999
if sgn(uw2 as word - uw1 as word) != -1 if sgn((uw2 as word) - (uw1 as word)) != -1
c64scr.print("sgn2 error6\n") c64scr.print("sgn2 error6a\n")
if sgn(uw2 - uw1) != -1 if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned
c64scr.print("sgn2 error6\n") c64scr.print("sgn2 error6b\n")
f1 = 3.45 f1 = 3.45
f2 = 1.11 f2 = 1.11

View File

@ -5,64 +5,45 @@
main { 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() { sub start() {
ubyte xyz = 99 ; TODO fix compiler error when removing unused var ubyte ub1
word wcosa = cos8(xyz) ubyte ub2 = 99
word wcosa_sinb = wcosa / 128 uword uw1
uword uw2 = 9999
ubyte[5] array1
ubyte[5] array2 = [22,33,44,55,66]
subje() c64scr.print_ub(ub1)
subje() c64.CHROUT(',')
subje() 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 ub1++
; ubyte ub2 = 99 ub2++
; uword uw1 uw1++
; uword uw2 = 9999 uw2++
; ubyte[5] array1 array1[0]++
; ubyte[5] array2 = [22,33,44,55,66] array2[0]++
;
; c64scr.print_ub(ub1) c64scr.print_ub(ub1)
; c64.CHROUT(',') c64.CHROUT(',')
; c64scr.print_ub(ub2) c64scr.print_ub(ub2)
; c64.CHROUT(',') c64.CHROUT(',')
; c64scr.print_uw(uw1) c64scr.print_uw(uw1)
; c64.CHROUT(',') c64.CHROUT(',')
; c64scr.print_uw(uw2) c64scr.print_uw(uw2)
; c64.CHROUT(',') c64.CHROUT(',')
; c64scr.print_ub(array1[0]) c64scr.print_ub(array1[0])
; c64.CHROUT(',') c64.CHROUT(',')
; c64scr.print_ub(array2[0]) c64scr.print_ub(array2[0])
; c64.CHROUT('\n') 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')
} }
} }