mirror of
https://github.com/irmen/prog8.git
synced 2025-01-24 06:30:24 +00:00
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:
parent
ca1a8cd617
commit
0e614ad6fc
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
^^^^^^^^
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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 ;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user