added @shared flag to vardecl to mark variable as shared with assembly code elsewhere, to not have it optimized away

This commit is contained in:
Irmen de Jong 2021-05-19 01:19:25 +02:00
parent ca1a8cd617
commit 0e614ad6fc
12 changed files with 59 additions and 22 deletions

View File

@ -174,7 +174,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
fun unused(decl: VarDecl): Boolean {
if(decl.type!=VarDeclType.VAR || decl.autogeneratedDontRemove)
if(decl.type!=VarDeclType.VAR || decl.autogeneratedDontRemove || decl.sharedWithAsm)
return false
if(decl.definingBlock() !in usedBlocks)

View File

@ -30,6 +30,7 @@ internal class StatementOptimizer(private val program: Program,
val decl = VarDecl(VarDeclType.VAR, returnvar.second, ZeropageWish.DONTCARE, null, retvarName, null,
isArray = false,
autogeneratedDontRemove = true,
sharedWithAsm = false,
position = returnvar.third
)
returnvar.first.statements.add(0, decl)

View File

@ -103,7 +103,7 @@ internal class UnusedCodeRemover(private val program: Program,
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type==VarDeclType.VAR) {
val forceOutput = "force_output" in decl.definingBlock().options()
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.definingBlock().isInLibrary) {
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock().isInLibrary) {
if (callgraph.unused(decl)) {
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, decl.definingScope()))

View File

@ -502,7 +502,7 @@ class TestMemory {
@Test
private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
@ -522,7 +522,7 @@ class TestMemory {
@Test
fun testInValidRamC64_variable() {
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -535,7 +535,7 @@ class TestMemory {
@Test
fun testInValidRamC64_memmap_variable() {
val address = 0x1000
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -548,7 +548,7 @@ class TestMemory {
@Test
fun testNotInValidRamC64_memmap_variable() {
val address = 0xd020
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -560,7 +560,7 @@ class TestMemory {
@Test
fun testInValidRamC64_array() {
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
@ -574,7 +574,7 @@ class TestMemory {
@Test
fun testInValidRamC64_array_memmapped() {
val address = 0x1000
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
@ -588,7 +588,7 @@ class TestMemory {
@Test
fun testNotValidRamC64_array_memmapped() {
val address = 0xe000
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)

View File

@ -297,7 +297,7 @@ class Program(val name: String,
val varName = "string_${internedStringsBlock.statements.size}"
val decl = VarDecl(
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
isArray = false, autogeneratedDontRemove = true, position = string.position
isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position
)
internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock)

View File

@ -69,6 +69,7 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
it.expression().toAst(encoding),
vd.ARRAYSIG() != null || vd.arrayindex() != null,
false,
vd.SHARED()!=null,
it.toPosition()
)
}
@ -85,6 +86,7 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
cvarinit.expression().toAst(encoding),
vd.ARRAYSIG() != null || vd.arrayindex() != null,
false,
vd.SHARED() != null,
cvarinit.toPosition()
)
}
@ -101,6 +103,7 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
mvarinit.expression().toAst(encoding),
vd.ARRAYSIG() != null || vd.arrayindex() != null,
false,
vd.SHARED()!=null,
mvarinit.toPosition()
)
}
@ -603,6 +606,7 @@ private fun prog8Parser.VardeclContext.toAst(encoding: IStringEncoding): VarDecl
null,
ARRAYSIG() != null || arrayindex() != null,
false,
SHARED()!=null,
toPosition()
)
}

View File

@ -164,6 +164,7 @@ open class VarDecl(val type: VarDeclType,
var value: Expression?,
val isArray: Boolean,
val autogeneratedDontRemove: Boolean,
val sharedWithAsm: Boolean,
override val position: Position) : Statement(), ISymbolStatement {
override lateinit var parent: Node
var allowInitializeWithZero = true
@ -183,7 +184,7 @@ open class VarDecl(val type: VarDeclType,
val declaredType = ArrayToElementTypes.getValue(arrayDt)
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, array,
isArray = true, autogeneratedDontRemove = true, position = array.position)
isArray = true, autogeneratedDontRemove = true, sharedWithAsm = false, position = array.position)
}
fun defaultZero(dt: DataType, position: Position) = when(dt) {
@ -239,7 +240,7 @@ open class VarDecl(val type: VarDeclType,
}
fun copy(): VarDecl {
val c = VarDecl(type, declaredDatatype, zeropage, arraysize, name, value, isArray, autogeneratedDontRemove, position)
val c = VarDecl(type, declaredDatatype, zeropage, arraysize, name, value, isArray, autogeneratedDontRemove, sharedWithAsm, position)
c.allowInitializeWithZero = this.allowInitializeWithZero
return c
}
@ -247,7 +248,7 @@ open class VarDecl(val type: VarDeclType,
// a vardecl used only for subroutine parameters
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, false, true, position)
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, false, true, false, position)
class ArrayIndex(var indexExpr: Expression,
override val position: Position) : Node {

View File

@ -202,6 +202,16 @@ Example::
byte @zp zeropageCounter = 42
*shared tag:*
If you add the ``@shared`` tag to the variable declaration, the compiler will know that this variable
is a prog8 variable shared with some assembly code elsewhere. This means that the assembly code can
refer to the variable even if it's otherwise not used in prog8 code itself.
(usually, these kinds of 'unused' variables are optimized away by the compiler, resulting in an error
when assembling the rest of the code). Example::
byte @shared assemblyVariable = 42
Integers
^^^^^^^^

View File

@ -235,9 +235,11 @@ for them. You can give them an initial value as well. That value can be a simple
or an expression. If you don't provide an intial value yourself, zero will be used.
You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
when selecting variables to be put into zeropage.
You can add a ``@shared`` shared-tag, to tell the compiler that the variable is shared
with some assembly code and that it should not be optimized away if not used elsewhere.
The syntax is::
<datatype> [ @zp ] <variable name> [ = <initial value> ]
<datatype> [ @shared ] [ @zp ] <variable name> [ = <initial value> ]
Various examples::
@ -252,7 +254,8 @@ Various examples::
byte[5] values ; array of 5 bytes, initially set to zero
byte[5] values = 255 ; initialize with five 255 bytes
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
word @shared asmvar ; variable is used in assembly code but not elsewhere
Data types

View File

@ -2,8 +2,7 @@
TODO
====
- possible idea: option to mark vardecls 'shared' to indicate they should not be optimized away because they're shared with assembly code?
However: who even needs variables declared in prog8 code that are only used by assembly???
- should give error when passing invalid command line arguments
- test all examples (including imgviewer, assembler and petaxian) before release of the new version

View File

@ -4,12 +4,31 @@ main {
sub start() {
ubyte @shared xx=99
uword @shared asmvar
%asm {{
inc xx
lda asmvar
ldy asmvar+1
}}
txt.nl()
str string1 = "stringvalue\n"
str string2 = "stringvalue\n"
str string3 = "stringvalue\n"
str string4 = "a"
str string5 = "bb"
txt.print(string1)
txt.print(string2)
txt.print(string3)
string1[1]='?'
string2[2] = '?'
string3[3] = '?'
txt.print(string1)
txt.print(string2)
txt.print(string3)
txt.print("a")
txt.print("a")
txt.print(string4)
@ -18,9 +37,6 @@ main {
txt.print(string5)
txt.print("\n")
txt.print("\n\n")
txt.print(string1)
txt.print(string2)
txt.print(string3)
txt.print("hello\n")
txt.print("hello\n")
txt.print("hello\n")

View File

@ -62,11 +62,14 @@ ZEROPAGE :
'@zp'
;
SHARED :
'@shared'
;
ARRAYSIG :
'[]'
;
cpuregister: 'A' | 'X' | 'Y';
register: 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' | 'Pc' | 'Pz' | 'Pn' | 'Pv' | 'R0' | 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6' | 'R7' | 'R8' | 'R9' | 'R10' | 'R11' | 'R12' | 'R13' | 'R14' | 'R15';
@ -134,7 +137,7 @@ directive :
directivearg : stringliteral | identifier | integerliteral ;
vardecl: datatype ZEROPAGE? (arrayindex | ARRAYSIG) ? varname=identifier ;
vardecl: datatype SHARED? ZEROPAGE? (arrayindex | ARRAYSIG) ? varname=identifier ;
varinitializer : vardecl '=' expression ;