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) {
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)
if (assignment.aug_op != null)
output(" ${assignment.aug_op} ")

View File

@ -7,10 +7,6 @@ import prog8.compiler.CompilationOptions
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) {
val checker = AstChecker(this, compilerOptions, errors)
checker.visit(this)
@ -23,7 +19,7 @@ internal fun Program.moveAnonScopeVarsToSubroutine(errors: ErrorReporter) {
}
internal fun Program.reorderStatements() {
val initvalueCreator = VarInitValueCreator(this)
val initvalueCreator = AddressOfInserter(this)
initvalueCreator.visit(this)
initvalueCreator.applyModifications()

View File

@ -11,17 +11,14 @@ import prog8.functions.BuiltinFunctions
import prog8.functions.FSignature
internal class VarInitValueCreator(val program: Program): AstWalker() {
// For VarDecls that declare an initialization value:
// 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).
internal class AddressOfInserter(val program: Program): AstWalker() {
// Insert AddressOf (&) expression where required (string params to a UWORD function param etc).
// 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> {
if(decl.isArray && decl.value==null) {
// array datatype without initialization value, add list of zeros
// TODO let the code generator take care of this?
val arraysize = decl.arraysize!!.size()!!
val zero = decl.asDefaultValueDecl(decl).value!!
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))
}
val declvalue = decl.value
if(decl.type == VarDeclType.VAR && declvalue != null && decl.datatype in NumericDatatypes) {
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
)
)
}
if(decl.value!=null && decl.type==VarDeclType.VAR && !decl.isArray)
decl.definingBlock().initialValues += decl
return emptyList()
}

View File

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

View File

@ -3,7 +3,6 @@ package prog8.ast.processing
import prog8.ast.*
import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.base.initvarsSubName
import prog8.ast.expressions.*
import prog8.ast.statements.*
@ -108,21 +107,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
block.statements.addAll(0, directives)
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)
}

View File

@ -73,6 +73,7 @@ class Block(override val name: String,
val idx = statements.indexOf(node)
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: 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?,
var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?,

View File

@ -126,11 +126,7 @@ internal class AsmGen(private val program: Program,
out(" ldx #\$ff\t; init estack pointer")
out(" ; initialize the variables in each block")
for (block in program.allBlocks()) {
val initVarsSub = block.statements.singleOrNull { it is Subroutine && it.name == initvarsSubName }
if(initVarsSub!=null)
out(" jsr ${block.name}.$initvarsSubName")
}
program.allBlocks().filter { it.initialValues.any() }.forEach { out(" jsr ${it.name}.prog8_init_vars") }
out(" clc")
when (zeropage.exitProgramStrategy) {
@ -175,6 +171,19 @@ internal class AsmGen(private val program: Program,
stmts.forEach { translate(it) }
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")
}

View File

@ -7,7 +7,6 @@ import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.ParentSentinel
import prog8.ast.base.VarDeclType
import prog8.ast.base.initvarsSubName
import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.processing.IAstVisitor
@ -120,7 +119,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
override fun visit(subroutine: Subroutine) {
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
addNodeAndParentScopes(subroutine)
}

View File

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

View File

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