mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
changed @initonce to @dirty and meaning is now: not initialized at all.
This commit is contained in:
parent
3ee6058524
commit
64164c1c72
@ -76,7 +76,7 @@ class VarConstantValueTypeAdjuster(
|
||||
if (declValue != null) {
|
||||
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
|
||||
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
|
||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.initOnce, decl.position)
|
||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.dirty, decl.position)
|
||||
decl.value = null
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, const, parent)
|
||||
@ -96,7 +96,7 @@ class VarConstantValueTypeAdjuster(
|
||||
}
|
||||
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
|
||||
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
|
||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.initOnce, decl.position)
|
||||
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.dirty, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, const, parent),
|
||||
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||
@ -419,21 +419,6 @@ internal class ConstantIdentifierReplacer(
|
||||
return null
|
||||
}
|
||||
|
||||
if (decl.isArray && decl.initOnce) {
|
||||
if (decl.value == null) {
|
||||
// initonce array without initialization value, make an array of just zeros
|
||||
val size = decl.arraysize?.constIndex()
|
||||
if(size!=null) {
|
||||
val zeros = Array<Expression>(size) { decl.zeroElementValue() }
|
||||
val initvalue = ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), zeros, decl.position)
|
||||
decl.value = initvalue
|
||||
initvalue.linkParents(decl)
|
||||
}
|
||||
} else {
|
||||
errors.err("arrays with an initialization value already are initialized only once by default", decl.position)
|
||||
}
|
||||
}
|
||||
|
||||
val rangeExpr = decl.value as? RangeExpression ?: return null
|
||||
|
||||
// convert the initializer range expression from a range, to an actual array literal.
|
||||
|
@ -920,14 +920,14 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
|
||||
if(decl.datatype==DataType.STR) {
|
||||
if(!decl.initOnce)
|
||||
throw FatalAstException("string vars must be initonce")
|
||||
}
|
||||
|
||||
if (decl.initOnce) {
|
||||
if (decl.datatype != DataType.STR) {
|
||||
errors.warn("non-string initonce variable: value will not be reset in subsequent subroutine invocations", decl.position)
|
||||
if (decl.dirty) {
|
||||
if(decl.datatype==DataType.STR)
|
||||
errors.err("string variables cannot be @dirty", decl.position)
|
||||
else {
|
||||
if(decl.value==null)
|
||||
errors.info("dirty variable: initial value will be undefined", decl.position)
|
||||
else
|
||||
errors.err("dirty variable can't have initialization value", decl.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if (decl.type == VarDeclType.VAR && decl.value != null && (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL)) {
|
||||
if(!decl.initOnce)
|
||||
throw InternalCompilerException("vardecls for non-initonce variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||
throw InternalCompilerException("vardecls with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||
}
|
||||
|
||||
return noModifications
|
||||
|
@ -528,8 +528,10 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
when(srcVar.type) {
|
||||
VarDeclType.VAR -> {
|
||||
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
|
||||
if(srcVar.initOnce && value==null)
|
||||
throw FatalAstException("initonce without value $srcVar")
|
||||
if(srcVar.dirty && value!=null)
|
||||
throw FatalAstException("dirty with initializer value $srcVar")
|
||||
// if(value==null && !srcVar.dirty)
|
||||
// throw FatalAstException("no init value but not marked dirty $srcVar")
|
||||
return PtVariable(
|
||||
srcVar.name,
|
||||
srcVar.datatype,
|
||||
|
@ -180,7 +180,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
return VarDecl(
|
||||
variable.type, variable.origin, normalDt, variable.zeropage, variable.arraysize, variable.name, emptyList(),
|
||||
variable.value?.copy(), variable.sharedWithAsm, false, variable.alignment, variable.initOnce, variable.position
|
||||
variable.value?.copy(), variable.sharedWithAsm, false, variable.alignment, variable.dirty, variable.position
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -43,15 +43,16 @@ internal class StatementReorderer(
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if (decl.type == VarDeclType.VAR) {
|
||||
if(decl.dirty && decl.value!=null)
|
||||
errors.err("dirty variable can't have initialization value", decl.position)
|
||||
|
||||
if (decl.datatype in NumericDatatypes || decl.datatype==DataType.BOOL) {
|
||||
if(decl !in declsProcessedWithInitAssignment) {
|
||||
declsProcessedWithInitAssignment.add(decl)
|
||||
if (decl.value == null) {
|
||||
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
|
||||
if(decl.initOnce) {
|
||||
val zerovalue = decl.zeroElementValue()
|
||||
decl.value = zerovalue
|
||||
zerovalue.linkParents(decl)
|
||||
if(decl.dirty) {
|
||||
// no initialization at all!
|
||||
return noModifications
|
||||
}
|
||||
// A numeric vardecl without an initial value is initialized with zero,
|
||||
@ -72,9 +73,6 @@ internal class StatementReorderer(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(decl.initOnce) {
|
||||
return noModifications
|
||||
}
|
||||
// Transform the vardecl with initvalue to a plain vardecl + assignment
|
||||
// this allows for other optimizations to kick in.
|
||||
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
|
||||
@ -231,7 +229,7 @@ internal class StatementReorderer(
|
||||
it.sharedWithAsm,
|
||||
it.splitArray,
|
||||
it.alignment,
|
||||
it.initOnce,
|
||||
it.dirty,
|
||||
it.position
|
||||
)
|
||||
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||
|
@ -87,7 +87,7 @@ class Program(val name: String,
|
||||
val varName = "string_${internedStringsBlock.statements.size}"
|
||||
val decl = VarDecl(
|
||||
VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, emptyList(), string,
|
||||
sharedWithAsm = false, splitArray = false, alignment = 0u, initOnce = true, position = string.position
|
||||
sharedWithAsm = false, splitArray = false, alignment = 0u, dirty = false, position = string.position
|
||||
)
|
||||
internedStringsBlock.statements.add(decl)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
|
@ -328,8 +328,8 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
val options = it.decloptions()
|
||||
if(options.ALIGNPAGE().isNotEmpty() || options.ALIGNWORD().isNotEmpty())
|
||||
throw SyntaxError("cannot use alignments on parameters", it.toPosition())
|
||||
if(options.INITONCE().isNotEmpty())
|
||||
throw SyntaxError("cannot use @initonce on parameters", it.toPosition())
|
||||
if(options.DIRTY().isNotEmpty())
|
||||
throw SyntaxError("cannot use @dirty on parameters", it.toPosition())
|
||||
val zp = getZpOption(options)
|
||||
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
|
||||
@ -771,7 +771,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
|
||||
options.SHARED().isNotEmpty(),
|
||||
split,
|
||||
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
|
||||
dt==DataType.STR || options.INITONCE().isNotEmpty(),
|
||||
options.DIRTY().isNotEmpty(),
|
||||
toPosition()
|
||||
)
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ class VarDecl(val type: VarDeclType,
|
||||
val sharedWithAsm: Boolean,
|
||||
val splitArray: Boolean,
|
||||
val alignment: UInt,
|
||||
val initOnce: Boolean,
|
||||
val dirty: Boolean,
|
||||
override val position: Position) : Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
var allowInitializeWithZero = true
|
||||
@ -263,7 +263,7 @@ class VarDecl(val type: VarDeclType,
|
||||
sharedWithAsm = false,
|
||||
splitArray = false,
|
||||
alignment = 0u,
|
||||
initOnce = false,
|
||||
dirty = false,
|
||||
position = param.position
|
||||
)
|
||||
}
|
||||
@ -273,15 +273,10 @@ class VarDecl(val type: VarDeclType,
|
||||
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
|
||||
val arraysize = ArrayIndex.forArray(array)
|
||||
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array,
|
||||
sharedWithAsm = false, splitArray = splitArray, alignment = 0u, initOnce = false, position = array.position)
|
||||
sharedWithAsm = false, splitArray = splitArray, alignment = 0u, dirty = false, position = array.position)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if(datatype==DataType.STR && origin!=VarDeclOrigin.SUBROUTINEPARAM)
|
||||
require(initOnce) { "string variable must be initonce" }
|
||||
}
|
||||
|
||||
init {
|
||||
if(datatype in SplitWordArrayTypes)
|
||||
require(splitArray)
|
||||
@ -320,7 +315,7 @@ class VarDecl(val type: VarDeclType,
|
||||
if(names.size>1)
|
||||
throw FatalAstException("should not copy a vardecl that still has multiple names")
|
||||
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), name, names, value?.copy(),
|
||||
sharedWithAsm, splitArray, alignment, initOnce, position)
|
||||
sharedWithAsm, splitArray, alignment, dirty, position)
|
||||
copy.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
return copy
|
||||
}
|
||||
@ -335,19 +330,19 @@ class VarDecl(val type: VarDeclType,
|
||||
// just copy the initialization value to a separate vardecl for each component
|
||||
return names.map {
|
||||
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), value?.copy(),
|
||||
sharedWithAsm, splitArray, alignment, initOnce, position)
|
||||
sharedWithAsm, splitArray, alignment, dirty, position)
|
||||
copy.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
copy
|
||||
}
|
||||
} else {
|
||||
// evaluate the value once in the vardecl for the first component, and set the other components to the first
|
||||
val first = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), names[0], emptyList(), value?.copy(),
|
||||
sharedWithAsm, splitArray, alignment, initOnce, position)
|
||||
sharedWithAsm, splitArray, alignment, dirty, position)
|
||||
first.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
val firstVar = firstVarAsValue(first)
|
||||
return listOf(first) + names.drop(1 ).map {
|
||||
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), firstVar.copy(),
|
||||
sharedWithAsm, splitArray, alignment, initOnce, position)
|
||||
sharedWithAsm, splitArray, alignment, dirty, position)
|
||||
copy.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
copy
|
||||
}
|
||||
|
@ -236,6 +236,15 @@ when assembling the rest of the code). Example::
|
||||
byte @shared assemblyVariable = 42
|
||||
|
||||
|
||||
**uninitialized variables:**
|
||||
All variables will be initialized by prog8 at startup, they'll get their assigned initialization value, or be cleared to zero.
|
||||
This (re)initialization is also done on each subroutine entry for the variables declared in the subroutine.
|
||||
There may be certain scenarios where this initialization is redundant and/or where you want to avoid the overhead of it
|
||||
You can do so by using the ``@dirty`` tag on the variable declaration.
|
||||
This means that the variable will *not* be (re)initialized by Prog8 and that its value is undefined.
|
||||
You have to assign it a value yourself first, before using the variable. If you don't do that, the value can be anything, so beware.
|
||||
|
||||
|
||||
**memory alignment:**
|
||||
A string or array variable can be aligned to a couple of possible interval sizes in memory.
|
||||
The use for this is very situational, but two examples are: sprite data for the C64 that needs
|
||||
|
@ -385,6 +385,7 @@ Tag Effect
|
||||
@alignword aligns string or array variable on an even memory address
|
||||
@align64 aligns string or array variable on a 64 byte address interval (example: for C64 sprite data)
|
||||
@alignpage aligns string or array variable on a 256 byte address interval (example: to avoid page boundaries)
|
||||
@dirty the variable won't be initialized by Prog8 which means that its value is undefined. You'll have to set it yourself before using the variable. Used to reduce overhead in certain scenarios. 🦶🔫 Footgun warning.
|
||||
========== ======
|
||||
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- BUG: fix @initonce for variables that end up in zeropage. They remain uninitialized altogether now. Fix it or don't allow it?
|
||||
- add unit tests for @initonce variables
|
||||
- add docs about variables with @initonce initialization
|
||||
- add unit tests for @dirty variables
|
||||
|
||||
|
||||
for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed.
|
||||
|
108
examples/test.p8
108
examples/test.p8
@ -1,65 +1,71 @@
|
||||
%import floats
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; INIT ONCE tests
|
||||
; DIRTY tests
|
||||
|
||||
main {
|
||||
uword @shared @dirty globw
|
||||
uword @shared globwi = 4444
|
||||
float @shared @dirty globf
|
||||
float @shared globfi = 4
|
||||
ubyte[5] @shared @dirty globarr1
|
||||
ubyte[] @shared globarr2 = [11,22,33,44,55]
|
||||
|
||||
sub start() {
|
||||
uword w0
|
||||
uword @initonce w1
|
||||
uword @initonce w2
|
||||
uword @initonce w3
|
||||
uword @initonce w4 = 12345
|
||||
uword[4] wa
|
||||
uword[] @shared wb = [1111,2222,3333,4444]
|
||||
|
||||
dump()
|
||||
testdirty()
|
||||
txt.nl()
|
||||
w0++
|
||||
w1++
|
||||
w2++
|
||||
w3++
|
||||
w4++
|
||||
wa[1]++
|
||||
wa[2]++
|
||||
wa[3]++
|
||||
wb[0]++
|
||||
dump()
|
||||
txt.nl()
|
||||
|
||||
repeat 10 {
|
||||
footgun()
|
||||
}
|
||||
txt.nl()
|
||||
|
||||
sub dump() {
|
||||
txt.print_uw(w0)
|
||||
txt.spc()
|
||||
txt.print_uw(w1)
|
||||
txt.spc()
|
||||
txt.print_uw(w2)
|
||||
txt.spc()
|
||||
txt.print_uw(w3)
|
||||
txt.spc()
|
||||
txt.print_uw(w4)
|
||||
txt.spc()
|
||||
txt.print_uw(wa[1])
|
||||
txt.spc()
|
||||
txt.print_uw(wa[2])
|
||||
txt.spc()
|
||||
txt.print_uw(wa[3])
|
||||
txt.spc()
|
||||
txt.print_uw(wb[0])
|
||||
testdirty()
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub footgun() {
|
||||
; TODO should just be a nonlocal variable outside of the subroutine...?
|
||||
ubyte @shared @initonce @requirezp variable = 42 ; BUG: is never initialized now
|
||||
txt.print_ub(variable)
|
||||
sub testdirty() {
|
||||
uword @shared @dirty locw
|
||||
uword @shared locwi = 4444
|
||||
float @shared @dirty locf
|
||||
float @shared locfi = 4.0
|
||||
ubyte[5] @shared @dirty locarr1
|
||||
ubyte[] @shared locarr2 = [11,22,33,44,55]
|
||||
|
||||
txt.print("globals: ")
|
||||
txt.print_uw(globw)
|
||||
txt.spc()
|
||||
variable++
|
||||
floats.print(globf)
|
||||
txt.print(" with init: ")
|
||||
txt.print_uw(globwi)
|
||||
txt.spc()
|
||||
floats.print(globfi)
|
||||
txt.print(" arrays: ")
|
||||
txt.print_ub(globarr1[2])
|
||||
txt.spc()
|
||||
txt.print_ub(globarr2[2])
|
||||
txt.print("\nlocals: ")
|
||||
txt.print_uw(locw)
|
||||
txt.spc()
|
||||
floats.print(locf)
|
||||
txt.print(" with init: ")
|
||||
txt.print_uw(locwi)
|
||||
txt.spc()
|
||||
floats.print(locfi)
|
||||
txt.print(" arrays: ")
|
||||
txt.print_ub(locarr1[2])
|
||||
txt.spc()
|
||||
txt.print_ub(locarr2[2])
|
||||
txt.nl()
|
||||
|
||||
|
||||
globw++
|
||||
globwi++
|
||||
globf++
|
||||
globfi++
|
||||
globarr1[2]++
|
||||
globarr2[2]++
|
||||
locw++
|
||||
locwi++
|
||||
locf++
|
||||
locfi++
|
||||
locarr1[2]++
|
||||
locarr2[2]++
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ ALIGN64: '@align64' ;
|
||||
|
||||
ALIGNPAGE: '@alignpage' ;
|
||||
|
||||
INITONCE: '@initonce' ;
|
||||
DIRTY: '@dirty' ;
|
||||
|
||||
ARRAYSIG : '[' [ \t]* ']' ;
|
||||
|
||||
@ -159,7 +159,7 @@ directivearg : stringliteral | identifier | integerliteral ;
|
||||
|
||||
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ;
|
||||
|
||||
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | INITONCE)* ;
|
||||
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | DIRTY)* ;
|
||||
|
||||
varinitializer : vardecl '=' expression ;
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
<option name="HAS_STRING_ESCAPES" value="true" />
|
||||
</options>
|
||||
<keywords keywords="&;->;@;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
|
||||
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@initonce;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
|
||||
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
|
||||
<keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" />
|
||||
<keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
|
||||
</highlighting>
|
||||
|
@ -43,7 +43,7 @@ syn region prog8ArrayType matchgroup=prog8Type
|
||||
\ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]"
|
||||
\ transparent
|
||||
syn keyword prog8StorageClass const
|
||||
syn match prog8StorageClass "\(^\|\s\)\(@zp\|@bank\|@shared\|@split\|@nozp\|@requirezp\|@align64\|@alignword\|@alignpage\|@initonce\)\>"
|
||||
syn match prog8StorageClass "\(^\|\s\)\(@zp\|@bank\|@shared\|@split\|@nozp\|@requirezp\|@align64\|@alignword\|@alignpage\|@dirty\)\>"
|
||||
|
||||
syn region prog8Block start="{" end="}" transparent
|
||||
syn region prog8Expression start="(" end=")" transparent
|
||||
|
Loading…
Reference in New Issue
Block a user