simplified handling of initial vardecl values in codegeneration

This commit is contained in:
Irmen de Jong 2020-03-22 02:45:42 +01:00
parent 4fbdd6d570
commit 78feef9d59
10 changed files with 68 additions and 82 deletions

View File

@ -283,14 +283,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
} }
override fun visit(assignment: Assignment) { override fun visit(assignment: Assignment) {
if(assignment is VariableInitializationAssignment) {
val targetVar = assignment.target.identifier?.targetVarDecl(program.namespace)
if(targetVar?.struct != null) {
// skip STRUCT init assignments
return
}
}
assignment.target.accept(this) assignment.target.accept(this)
if (assignment.aug_op != null) if (assignment.aug_op != null)
output(" ${assignment.aug_op} ") output(" ${assignment.aug_op} ")

View File

@ -7,10 +7,6 @@ import prog8.compiler.CompilationOptions
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
// the name of the subroutine that should be called for every block to initialize its variables
internal const val initvarsSubName = "prog8_init_vars"
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) { internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
val checker = AstChecker(this, compilerOptions, errors) val checker = AstChecker(this, compilerOptions, errors)
checker.visit(this) checker.visit(this)
@ -23,7 +19,7 @@ internal fun Program.moveAnonScopeVarsToSubroutine(errors: ErrorReporter) {
} }
internal fun Program.reorderStatements() { internal fun Program.reorderStatements() {
val initvalueCreator = VarInitValueCreator(this) val initvalueCreator = AddressOfInserter(this)
initvalueCreator.visit(this) initvalueCreator.visit(this)
initvalueCreator.applyModifications() initvalueCreator.applyModifications()

View File

@ -11,17 +11,14 @@ import prog8.functions.BuiltinFunctions
import prog8.functions.FSignature import prog8.functions.FSignature
internal class VarInitValueCreator(val program: Program): AstWalker() { internal class AddressOfInserter(val program: Program): AstWalker() {
// For VarDecls that declare an initialization value: // Insert AddressOf (&) expression where required (string params to a UWORD function param etc).
// add an assignment to set this initial value explicitly.
// This makes sure the variables get reset to the intended value on a next run of the program.
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
// TODO join this into the StatementReorderer? // TODO join this into the StatementReorderer?
// TODO actually I think the code generator should take care of this entirely, and this step should be removed from the ast modifications...
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.isArray && decl.value==null) { if(decl.isArray && decl.value==null) {
// array datatype without initialization value, add list of zeros // array datatype without initialization value, add list of zeros
// TODO let the code generator take care of this?
val arraysize = decl.arraysize!!.size()!! val arraysize = decl.arraysize!!.size()!!
val zero = decl.asDefaultValueDecl(decl).value!! val zero = decl.asDefaultValueDecl(decl).value!!
return listOf(IAstModification.SetExpression( // can't use replaceNode here because value is null return listOf(IAstModification.SetExpression( // can't use replaceNode here because value is null
@ -32,30 +29,8 @@ internal class VarInitValueCreator(val program: Program): AstWalker() {
decl)) decl))
} }
val declvalue = decl.value if(decl.value!=null && decl.type==VarDeclType.VAR && !decl.isArray)
if(decl.type == VarDeclType.VAR && declvalue != null && decl.datatype in NumericDatatypes) { decl.definingBlock().initialValues += decl
val value =
if(declvalue is NumericLiteralValue)
declvalue.cast(decl.datatype)
else
declvalue
val identifierName = listOf(decl.name)
val initvalue = VariableInitializationAssignment(
AssignTarget(null,
IdentifierReference(identifierName, decl.position),
null, null, decl.position),
null, value, decl.position
)
val zero = decl.asDefaultValueDecl(decl).value!!
return listOf(
IAstModification.Insert(decl, initvalue, parent),
IAstModification.ReplaceNode(
declvalue,
zero,
decl
)
)
}
return emptyList() return emptyList()
} }

View File

@ -40,7 +40,6 @@ internal class AstChecker(private val program: Program,
is VarDecl -> true is VarDecl -> true
is InlineAssembly -> true is InlineAssembly -> true
is INameScope -> true is INameScope -> true
is VariableInitializationAssignment -> true
is NopStatement -> true is NopStatement -> true
else -> false else -> false
} }

View File

@ -3,7 +3,6 @@ package prog8.ast.processing
import prog8.ast.* import prog8.ast.*
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.base.initvarsSubName
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
@ -108,21 +107,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
block.statements.addAll(0, directives) block.statements.addAll(0, directives)
block.linkParents(block.parent) block.linkParents(block.parent)
// create subroutine that initializes the block's variables (if any)
val varInits = block.statements.withIndex().filter { it.value is VariableInitializationAssignment }
if(varInits.isNotEmpty()) {
val statements = varInits.map{it.value}.toMutableList()
val varInitSub = Subroutine(initvarsSubName, emptyList(), emptyList(), emptyList(), emptyList(),
emptySet(), null, false, statements, block.position)
varInitSub.keepAlways = true
varInitSub.linkParents(block)
block.statements.add(varInitSub)
// remove the varinits from the block's statements
for(index in varInits.map{it.index}.reversed())
block.statements.removeAt(index)
}
return super.visit(block) return super.visit(block)
} }

View File

@ -73,6 +73,7 @@ class Block(override val name: String,
val idx = statements.indexOf(node) val idx = statements.indexOf(node)
statements[idx] = replacement statements[idx] = replacement
} }
val initialValues = mutableListOf<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)
@ -373,11 +374,6 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value:
} }
} }
// This is a special class so the compiler can see if the assignments are for initializing the vars in the scope,
// or just a regular assignment. It could optimize the initialization step from this.
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: Expression, position: Position)
: Assignment(target, aug_op, value, position)
data class AssignTarget(val register: Register?, data class AssignTarget(val register: Register?,
var identifier: IdentifierReference?, var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?, var arrayindexed: ArrayIndexedExpression?,

View File

@ -126,11 +126,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")
for (block in program.allBlocks()) { program.allBlocks().filter { it.initialValues.any() }.forEach { out(" jsr ${it.name}.prog8_init_vars") }
val initVarsSub = block.statements.singleOrNull { it is Subroutine && it.name == initvarsSubName }
if(initVarsSub!=null)
out(" jsr ${block.name}.$initvarsSubName")
}
out(" clc") out(" clc")
when (zeropage.exitProgramStrategy) { when (zeropage.exitProgramStrategy) {
@ -175,6 +171,19 @@ internal class AsmGen(private val program: Program,
stmts.forEach { translate(it) } stmts.forEach { translate(it) }
subroutine.forEach { translateSubroutine(it as Subroutine) } subroutine.forEach { translateSubroutine(it as Subroutine) }
// 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()) {
out("prog8_init_vars\t.proc\n")
block.initialValues.forEach {
val target = AssignTarget(null, IdentifierReference(listOf(it.scopedname), it.position), null, null, it.position)
val assign = Assignment(target, null, it.value!!, it.position)
assign.linkParents(it.parent)
assignmentAsmGen.translate(assign)
}
out(" rts\n .pend")
}
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n") out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
} }

View File

@ -7,7 +7,6 @@ import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.ParentSentinel import prog8.ast.base.ParentSentinel
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.base.initvarsSubName
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.processing.IAstVisitor import prog8.ast.processing.IAstVisitor
@ -120,7 +119,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
override fun visit(subroutine: Subroutine) { override fun visit(subroutine: Subroutine) {
if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|| subroutine.name == initvarsSubName || subroutine.definingModule().isLibraryModule) { || subroutine.definingModule().isLibraryModule) {
// make sure the entrypoint is mentioned in the used symbols // make sure the entrypoint is mentioned in the used symbols
addNodeAndParentScopes(subroutine) addNodeAndParentScopes(subroutine)
} }

View File

@ -196,13 +196,14 @@ class AstVm(val program: Program, compilationTarget: String) {
for (m in program.modules) { for (m in program.modules) {
for (b in m.statements.filterIsInstance<Block>()) { for (b in m.statements.filterIsInstance<Block>()) {
for (s in b.statements.filterIsInstance<Subroutine>()) { for (s in b.statements.filterIsInstance<Subroutine>()) {
if (s.name == initvarsSubName) { TODO("initialize variable values")
try { // if (s.name == initvarsSubName) {
executeSubroutine(s, emptyList(), null) // try {
} catch (x: LoopControlReturn) { // executeSubroutine(s, emptyList(), null)
// regular return // } catch (x: LoopControlReturn) {
} // // regular return
} // }
// }
} }
} }
} }

View File

@ -6,9 +6,44 @@
main { main {
sub start() { sub start() {
c64scr.clear_screen('*',7) ubyte ub1
c64.CHRIN() ubyte ub2 = 99
c64scr.clear_screen('.',2) 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')
} }
} }