Compare commits

..

136 Commits

Author SHA1 Message Date
Irmen de Jong
df56be32b2 working on long variable type 2025-07-15 20:28:03 +02:00
Irmen de Jong
dc434d034a fix address-of identifier alias replacement stripping array index away 2025-07-14 23:54:26 +02:00
Irmen de Jong
3f34b83e0d sdks 2025-07-14 21:54:40 +02:00
Irmen de Jong
9c63ef39c7 fix pointer test 2025-07-07 23:09:45 +02:00
Irmen de Jong
9f6106452e revert & to untyped pointer, added && for typed pointer address-of 2025-07-07 16:17:07 +02:00
Irmen de Jong
f9fbfe30e3 fix &x +/- offset pointer arithmetic expression 2025-07-07 13:11:57 +02:00
Irmen de Jong
9a9bf170c6 Merge branch 'master' into structs 2025-07-06 23:17:00 +02:00
Irmen de Jong
b6c0bac96f identified problems with pointer array as parameter 2025-07-06 20:33:20 +02:00
Irmen de Jong
8ede098154 fix pointer array initialization 2025-07-06 19:42:54 +02:00
Irmen de Jong
2a4a3b786e cleanup error message for currently unsupported deref'd pointer assignments 2025-07-06 14:59:42 +02:00
Irmen de Jong
b4e0a2019e fixed assignment to a[i]^^ 2025-07-06 13:38:22 +02:00
Irmen de Jong
e14c3f8b59 code cleanups 2025-07-06 00:52:37 +02:00
Irmen de Jong
c81f76226d Merge branch 'master' into structs 2025-07-06 00:37:58 +02:00
Irmen de Jong
dcce519c69 Merge branch 'master' into structs
# Conflicts:
#	.idea/libraries/KotlinJavaRuntime.xml
#	build.gradle.kts
#	gradle.properties
2025-07-06 00:07:16 +02:00
Irmen de Jong
54d41b7f6f fixed a[i]^^ 2025-07-05 23:54:08 +02:00
Irmen de Jong
0541b84d09 Merge branch 'master' into structs 2025-06-29 16:34:32 +02:00
Irmen de Jong
2119817e4a Merge branch 'master' into structs 2025-06-24 21:14:53 +02:00
Irmen de Jong
a68cf3c812 fix animalgame node reuse 2025-06-18 23:34:47 +02:00
Irmen de Jong
c2bf9024f8 start writing docs about structs and pointers, update syntax files with ^^ 2025-06-18 19:00:18 +02:00
Irmen de Jong
bd72eaad4c Merge branch 'refs/heads/master' into structs
# Conflicts:
#	examples/test.p8
2025-06-18 17:53:20 +02:00
Irmen de Jong
1d306e5cdc moved new animalgame 2025-06-17 23:21:54 +02:00
Irmen de Jong
b137164fe6 allow str assigned to ^^ubyte without an explicit cast 2025-06-17 18:29:48 +02:00
Irmen de Jong
67d4ad50e1 add new animals example (that uses a pointer tree) 2025-06-17 01:08:36 +02:00
Irmen de Jong
c71066af4c fixing name lookup issue 2025-06-16 22:15:51 +02:00
Irmen de Jong
6f0a0981bd fixing name lookup issue 2025-06-16 00:21:54 +02:00
Irmen de Jong
49a4d9ba37 allow str as struct field type (^^ubyte) and strings in struct initializers 2025-06-15 00:29:59 +02:00
Irmen de Jong
fcdfa741b9 Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt
2025-06-14 21:07:23 +02:00
Irmen de Jong
3bab177d50 working on pointers/binarytree example 2025-06-13 23:20:15 +02:00
Irmen de Jong
12abafb917 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
#	gradle.properties
2025-06-12 00:11:58 +02:00
Irmen de Jong
0be90dedf2 check for split word array as argument 2025-06-11 21:35:36 +02:00
Irmen de Jong
e6bab3ceeb IR: adding LOADFIELD and STOREFIELD instructions 2025-06-09 01:41:45 +02:00
Irmen de Jong
59387b2ae8 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-06-07 20:28:23 +02:00
Irmen de Jong
f8f20440d3 allow pointer to be treated as uword value in augmented assignments 2025-06-03 21:33:35 +02:00
Irmen de Jong
f8722faa4e Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-06-03 21:15:19 +02:00
Irmen de Jong
26fbbf48a4 adapt new antlr visitor to the pointer/struct additions 2025-06-03 01:28:58 +02:00
Irmen de Jong
d5cc414221 Merge branch 'master' into structs
# Conflicts:
#	codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt
#	compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	examples/test.p8
2025-06-02 20:52:50 +02:00
Irmen de Jong
d28f154f1c Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-06-01 17:53:41 +02:00
Irmen de Jong
1c02179c5c refactor loadIndexReg() 2025-05-30 21:39:17 +02:00
Irmen de Jong
77584493fd support a.b.ptr[i]^^.value as expression (RHS) 2025-05-30 20:28:10 +02:00
Irmen de Jong
a36709e638 Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/Compiler.kt
2025-05-30 12:39:23 +02:00
Irmen de Jong
8d63cce749 working on deref after array indexing 2025-05-30 11:30:49 +02:00
Irmen de Jong
8e7bbcdbe0 clear syntax error for yet unsupported deref after array indexing 2025-05-29 21:14:38 +02:00
Irmen de Jong
37ecdc47b3 allow ptr1^^ = ptr^^ (replaces it with memcopy) 2025-05-29 16:34:47 +02:00
Irmen de Jong
112ca3cc53 allow sizeof(&thing), add sys.SIZEOF_POINTER 2025-05-29 15:58:29 +02:00
Irmen de Jong
33b3a1664c replace sizeof(list^^) with sizeof(List) to allow it to compile. Same with simple pointers. 2025-05-29 14:13:42 +02:00
Irmen de Jong
8a0c02e264 Merge branch 'refs/heads/master' into structs
# Conflicts:
#	codeCore/src/prog8/code/target/NormalMemSizer.kt
#	codeCore/src/prog8/code/target/VMTarget.kt
#	compiler/src/prog8/compiler/BuiltinFunctions.kt
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	examples/test.p8
2025-05-29 13:42:16 +02:00
Irmen de Jong
e40ace9dea todo 2025-05-29 01:35:53 +02:00
Irmen de Jong
4c0e6e2640 fix split/nosplit pointer arrays, replace ubyteptr^^ with @(ubyteptr), fix double dereference 2025-05-29 00:36:55 +02:00
Irmen de Jong
08b314c37d IR: fix various register type mismatches 2025-05-28 22:15:07 +02:00
Irmen de Jong
86da9d3c7e assigning to plain pointer with array indexing 2025-05-28 18:08:53 +02:00
Irmen de Jong
4e61e25c02 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/TestTypecasts.kt
2025-05-27 23:52:59 +02:00
Irmen de Jong
5097d52d99 IR codegen for pointer indexing expressions, -assignment 2025-05-27 23:41:08 +02:00
Irmen de Jong
09d2185bb1 PtArrayIndexer variable is now nullable (because it could be a ptr deref instead) 2025-05-25 23:04:32 +02:00
Irmen de Jong
5c02e2bd71 fix a ptr indexing case, fix address-of fields 2025-05-25 21:32:31 +02:00
Irmen de Jong
fb01389b3d cleaning up pointer deref 2025-05-25 18:33:37 +02:00
Irmen de Jong
aaa81210ce cleaning up pointer indexing 2025-05-25 02:56:32 +02:00
Irmen de Jong
51269257ea fix a.b.c.d desugaring into pointer deref chain 2025-05-24 14:48:02 +02:00
Irmen de Jong
23a853db1e Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstChecker.kt
#	examples/test.p8
2025-05-23 19:00:59 +02:00
Irmen de Jong
db6c887795 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/ast/TestVariousCompilerAst.kt
2025-05-21 00:27:45 +02:00
Irmen de Jong
61fe55168a Merge branch 'master' into structs
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	examples/test.p8
2025-05-20 22:35:04 +02:00
Irmen de Jong
1c4999ec87 adding ptr unit tests 2025-05-20 20:57:05 +02:00
Irmen de Jong
c726d3f937 fix ptr errors 2025-05-19 22:49:07 +02:00
Irmen de Jong
f70341df1b fix ptr errors 2025-05-19 20:06:31 +02:00
Irmen de Jong
f0b791452e fix many ptr deref errors 2025-05-19 01:33:57 +02:00
Irmen de Jong
adf5600a9b simplify 2025-05-18 18:37:19 +02:00
Irmen de Jong
6d4ccc5feb fix pointer variable not getting marked as used in some cases 2025-05-18 13:23:56 +02:00
Irmen de Jong
5f3829d5cc partly fix weird errors for ptr indexed expressions 2025-05-17 22:56:41 +02:00
Irmen de Jong
770ebdcd4a party fix weird errors for ptr indexed assignment 2025-05-17 20:54:02 +02:00
Irmen de Jong
96f690e749 fix a ptr indexing error 2025-05-17 20:27:38 +02:00
Irmen de Jong
eabdd3a8f3 fix the ptr.uword[index] assignment target error 2025-05-17 14:57:24 +02:00
Irmen de Jong
50650b966b repeat countervars again in zeropage if possible, fix pointer arithmetic error 2025-05-17 14:18:56 +02:00
Irmen de Jong
65e34d4989 stricter types for & operator (address-of), and fix invalid recursive subroutine flagging related to struct definition 2025-05-17 11:32:54 +02:00
Irmen de Jong
05dad5ab5f cleanup 6502 pointer usage checks 2025-05-16 21:44:28 +02:00
Irmen de Jong
1a69a2f1bc fix some ptr vs uword type checks 2025-05-16 20:31:15 +02:00
Irmen de Jong
435faafaad fix split-word storage (lsb/msb) of arrays of pointers 2025-05-16 17:53:15 +02:00
Irmen de Jong
686b32dc29 replace ^^str by ^^ubyte and allow returning ubyte/uword when pointer is expected 2025-05-16 17:53:15 +02:00
Irmen de Jong
0e64a22910 tweak address-of types 2025-05-16 17:53:15 +02:00
Irmen de Jong
4f0839f27e rewrite pointer[0] into @(pointer) if its ^^ubyte 2025-05-16 17:53:15 +02:00
Irmen de Jong
bb1953267d Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-05-15 23:09:30 +02:00
Irmen de Jong
cd8aae4681 allow @(..) to take a ^^ubyte address pointer, not only uwords. 2025-05-15 22:12:42 +02:00
Irmen de Jong
11456496bd Merge branch 'master' into structs
# Conflicts:
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-05-15 21:35:31 +02:00
Irmen de Jong
86eef7039f @(..) now also accepts pointer to ubyte address 2025-05-15 20:07:02 +02:00
Irmen de Jong
f4b2264fcf fix struct type checks for subroutine call arguments 2025-05-14 23:33:55 +02:00
Irmen de Jong
9b36ae2277 implement inplace boolean short-circuit operators on pointer dereferenced booleans 2025-05-14 21:29:50 +02:00
Irmen de Jong
913ab03963 get rid of invalid ARRAY_STRUCT data type (arrays of struct instance are not yet supported) 2025-05-14 20:43:00 +02:00
Irmen de Jong
38448e471c Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt
#	examples/test.p8
2025-05-13 23:42:13 +02:00
Irmen de Jong
d138a7a567 add struct and pointers to IDEA syntax 2025-05-11 23:35:53 +02:00
Irmen de Jong
ea27d732ab Merge branch 'refs/heads/master' into structs 2025-05-11 23:35:13 +02:00
Irmen de Jong
924e28e9b3 Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
#	compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt
#	compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt
#	compilerAst/src/prog8/ast/AstToSourceTextConverter.kt
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	compilerAst/src/prog8/ast/walk/AstWalker.kt
#	compilerAst/src/prog8/ast/walk/IAstVisitor.kt
#	docs/source/todo.rst
#	examples/test.p8
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-05-11 23:23:06 +02:00
Irmen de Jong
60a73248cd todo 2025-05-11 03:16:48 +02:00
Irmen de Jong
abbb7d7ba3 fix struct pointers in subroutine parameters and return values 2025-05-11 02:08:59 +02:00
Irmen de Jong
59c378089e fix some struct type and symbol lookup errors 2025-05-11 00:52:35 +02:00
Irmen de Jong
0b789b5f0b added most inplace operators for pointer deref 2025-05-10 20:58:01 +02:00
Irmen de Jong
4382b96a9a tweaking pointer deref in IR 2025-05-10 19:52:06 +02:00
Irmen de Jong
246e4f35a6 Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	compiler/test/ast/TestConst.kt
#	docs/source/todo.rst
#	examples/test.p8
2025-05-10 16:36:16 +02:00
Irmen de Jong
506062c6b6 start implementing ptr deref augmented assigns 2025-05-09 23:05:27 +02:00
Irmen de Jong
8353c689ca start making '&' (address-of) return a typed pointer, fixes some errors 2025-05-08 23:40:41 +02:00
Irmen de Jong
e98e6f70ac Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstChecker.kt
#	compiler/test/TestTypecasts.kt
#	docs/source/todo.rst
#	examples/test.p8
#	gradle.properties
2025-05-07 23:26:21 +02:00
Irmen de Jong
134352ed7c Merge branch 'master' into structs
# Conflicts:
#	compiler/test/TestTypecasts.kt
#	docs/source/todo.rst
#	examples/test.p8
#	virtualmachine/src/prog8/vm/VmProgramLoader.kt
2025-05-07 22:32:41 +02:00
Irmen de Jong
5de626aab8 support comparison operators on pointers 2025-05-06 22:26:27 +02:00
Irmen de Jong
7aad5d486e Merge pull request #168 from adiee5/master
Update `nano` syntax highlighting
2025-05-06 18:17:58 +02:00
Irmen de Jong
701f155951 Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
#	compiler/test/TestSymbolTable.kt
#	docs/source/todo.rst
#	examples/test.p8
#	intermediate/src/prog8/intermediate/IRFileReader.kt
#	intermediate/src/prog8/intermediate/IRFileWriter.kt
#	intermediate/src/prog8/intermediate/IRSymbolTable.kt
#	simpleAst/src/prog8/code/SymbolTable.kt
#	simpleAst/src/prog8/code/SymbolTableMaker.kt
#	virtualmachine/src/prog8/vm/VmProgramLoader.kt
2025-05-06 17:59:36 +02:00
Irmen de Jong
8c324d7514 tweak error 2025-05-06 17:50:53 +02:00
adiee5
97390db5f5 Update prog8.nanorc 2025-05-05 21:06:56 +02:00
Irmen de Jong
af920d1427 pointer arithmetic for '-', fixed '+' 2025-05-05 21:06:49 +02:00
Irmen de Jong
779ebc0537 pointer arithmetic for '+' 2025-05-05 18:09:19 +02:00
Irmen de Jong
38949b82c3 type check tuning 2025-05-05 15:41:32 +02:00
Irmen de Jong
d11386ef26 type check tuning 2025-05-04 23:23:21 +02:00
Irmen de Jong
0e0377d1f0 IR/VM implemented struct allocations and initialization 2025-05-04 14:04:44 +02:00
Irmen de Jong
55e0dbab27 preparing for statically allocating struct instances 2025-05-03 23:44:29 +02:00
Irmen de Jong
4dc82f2c83 preparing for statically allocating struct instances 2025-05-03 19:00:27 +02:00
Irmen de Jong
1ba5587404 allow syntax for declaring variables with struct instance type 2025-05-03 16:14:22 +02:00
Irmen de Jong
835c4b6da3 allow multi-field declarations in structs, get rid of . -> ^^ rewrite 2025-05-03 12:32:29 +02:00
Irmen de Jong
dbd955b61e tweak typecheck and better code for constant indexes in pointer derefs 2025-05-02 23:44:26 +02:00
Irmen de Jong
d20e2fd88c structs will be a new major version of the compiler 2025-05-02 22:25:26 +02:00
Irmen de Jong
e0dea89477 added support for ptr[x].field 2025-05-02 22:16:20 +02:00
Irmen de Jong
6fc2902895 fixing ptr traversal typecheck issues 2025-05-02 00:41:42 +02:00
Irmen de Jong
c96e4b40d4 building syntax support for ptr[x].field
attempting to do this by making '.' an expression operator
2025-05-02 00:41:42 +02:00
Irmen de Jong
37da3e2170 parser 2025-05-02 00:41:42 +02:00
Irmen de Jong
2661d3c489 allow array syntax on pointers 2025-05-02 00:41:42 +02:00
Irmen de Jong
b89bbb9281 allow pointers in subroutines params and return values 2025-05-02 00:41:42 +02:00
Irmen de Jong
696bf636ed better parsing of directive names results in better error messages when an invalid one is found 2025-05-02 00:41:42 +02:00
Irmen de Jong
40952a788a PtSub: params and returns now as children (in PtSignature node) for easier Ast walking
PtPointerDeref: same but for its start identifier
2025-05-02 00:41:42 +02:00
Irmen de Jong
0162e7a0c1 fix the scoping problems on subtypes 2025-05-02 00:41:42 +02:00
Irmen de Jong
6ce099f176 IR: fix ptr type checks and struct field assignment errors 2025-05-02 00:41:42 +02:00
Irmen de Jong
476a4bac8e IR: LOADI allows r1 and r2 to be the same for pointer chain dereference optimalization 2025-05-02 00:41:42 +02:00
Irmen de Jong
63a410a6df implicit cast to bool for numeric or pointers as condition arguments (to if, while, until) 2025-05-02 00:41:42 +02:00
Irmen de Jong
cca27faa3b fix pointer value assignment (not dereferencing the actual pointer) 2025-05-02 00:41:42 +02:00
Irmen de Jong
803e6bd81a fix uword vs pointer type errors and casts 2025-05-02 00:41:42 +02:00
Irmen de Jong
88269628a2 had to turn ^type syntax into ^^type to avoid confusion with the eor operator once again 2025-05-02 00:41:42 +02:00
Irmen de Jong
b920d553a0 make address-of dereference work 2025-05-02 00:41:42 +02:00
Irmen de Jong
5e2d0d0dfc fix param order of AssignTarget 2025-05-02 00:41:42 +02:00
Irmen de Jong
2ae3bd68eb more pointer dereferencing for chains 2025-05-02 00:41:42 +02:00
Irmen de Jong
9c183f27eb pointer dereferencing for chains 2025-05-02 00:41:42 +02:00
Irmen de Jong
8046023e82 pointer dereferencing for simple types (read and write) 2025-05-02 00:41:42 +02:00
Irmen de Jong
e328520588 initial struct and typed pointer support 2025-05-02 00:41:40 +02:00
131 changed files with 8241 additions and 2261 deletions

3
.gitignore vendored
View File

@@ -7,10 +7,13 @@ build/
dist/
output/
out/
out-new/
out-old/
.*cache/
*.directory
*.prg
*.bin
*.p8ir
*.labels.txt
*.vm.txt
*.vice-mon-list

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,7 @@ What does Prog8 provide?
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- modularity, symbol scoping, subroutines. No need for forward declarations.
- various data types other than just bytes (16-bit words, floats, strings)
- Structs and typed pointers
- floating point math is supported on certain targets
- access to most Kernal ROM routines as external subroutine definitions you can call normally
- tight control over Zeropage usage

View File

@@ -101,7 +101,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
@@ -131,10 +131,15 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"peekl" to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)),
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"pokebool" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
"pokebowl" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"pokel" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"rsave" to FSignature(false, null),

View File

@@ -13,6 +13,9 @@ enum class BaseDataType {
STR, // pass by reference
ARRAY, // pass by reference, subtype is the element type
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
POINTER, // typed pointer, subtype is whatever type is pointed to
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
UNDEFINED;
@@ -26,6 +29,7 @@ enum class BaseDataType {
this.isArray && other.isArray -> false
this.isArray -> other != FLOAT
this == STR -> other != FLOAT
this.isPointer -> other.isByteOrBool
else -> true
}
@@ -34,7 +38,8 @@ enum class BaseDataType {
this == other -> true
this.isArray && other.isArray -> true
this.isByteOrBool -> other.isByteOrBool
this.isWord -> other.isWord
this.isWord -> other.isWord || other.isPointer
this.isPointer -> other.isWord
this == STR && other== UWORD || this== UWORD && other== STR -> true
this == STR && other.isArray -> true
this.isArray && other == STR -> true
@@ -50,84 +55,162 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
val BaseDataType.isPassByRef get() = this.isIterable
val BaseDataType.isPassByValue get() = !this.isIterable
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
interface ISubType {
val scopedNameString: String
fun memsize(sizer: IMemSizer): Int
fun sameas(other: ISubType): Boolean
}
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) {
init {
if(base.isArray) {
require(sub != null)
if(base.isSplitWordArray)
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
when {
base.isPointerArray -> {
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
}
base.isArray -> {
require(sub != null && subType==null && subTypeFromAntlr==null)
if(base.isSplitWordArray)
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
}
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
else -> {
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
"sub and subtype can't both be set"
}
}
}
else if(base==BaseDataType.STR)
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
else
require(sub == null) { "only string and array base types can have a subtype"}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DataType) return false
return base == other.base && sub == other.sub
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
}
override fun hashCode(): Int = Objects.hash(base, sub)
override fun hashCode(): Int = Objects.hash(base, sub, subType)
fun setActualSubType(actualSubType: ISubType) {
subType = actualSubType
subTypeFromAntlr = null
}
companion object {
val UBYTE = DataType(BaseDataType.UBYTE, null)
val BYTE = DataType(BaseDataType.BYTE, null)
val UWORD = DataType(BaseDataType.UWORD, null)
val WORD = DataType(BaseDataType.WORD, null)
val LONG = DataType(BaseDataType.LONG, null)
val FLOAT = DataType(BaseDataType.FLOAT, null)
val BOOL = DataType(BaseDataType.BOOL, null)
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
val BYTE = DataType(BaseDataType.BYTE, null, null)
val UWORD = DataType(BaseDataType.UWORD, null, null)
val WORD = DataType(BaseDataType.WORD, null, null)
val LONG = DataType(BaseDataType.LONG, null, null)
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
val BOOL = DataType(BaseDataType.BOOL, null, null)
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
private val simpletypes = mapOf(
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
)
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
fun forDt(dt: BaseDataType): DataType {
if(dt.isStructInstance)
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
return simpletypes.getValue(dt)
}
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
return if(splitwordarray && actualElementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
else {
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
DataType(BaseDataType.ARRAY, actualElementDt)
DataType(BaseDataType.ARRAY, actualElementDt, null)
else
throw NoSuchElementException("invalid element dt $elementDt")
throw NoSuchElementException("invalid basic element dt $elementDt")
}
}
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
fun pointer(dt: DataType): DataType = if(dt.isBasic)
DataType(BaseDataType.POINTER, dt.base, null)
else
DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
}
fun elementToArray(splitwords: Boolean = true): DataType {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
else arrayFor(base, false)
}
fun elementType(): DataType =
if(base.isArray || base==BaseDataType.STR)
forDt(sub!!)
else
throw IllegalArgumentException("not an array")
when {
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
else -> throw IllegalArgumentException("not an array")
}
fun typeForAddressOf(msb: Boolean): DataType {
if (isUndefined)
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
else {
if (isBasic)
return pointer(base)
if (isString)
return pointer(BaseDataType.UBYTE)
if (isPointer)
return UWORD
if (isArray) {
if (msb || isSplitWordArray)
return pointer(BaseDataType.UBYTE)
val elementDt = elementType()
require(elementDt.isBasic)
return pointer(elementDt)
}
if (subType != null)
return pointer(this)
return UWORD
}
}
fun dereference(): DataType {
require(isPointer || isUnsignedWord)
return when {
isUnsignedWord -> forDt(BaseDataType.UBYTE)
sub!=null -> forDt(sub)
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
else -> throw IllegalArgumentException("cannot dereference this pointer type")
}
}
override fun toString(): String = when(base) {
BaseDataType.ARRAY -> {
@@ -148,6 +231,15 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.POINTER -> {
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
}
BaseDataType.ARRAY_POINTER -> {
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
}
BaseDataType.STRUCT_INSTANCE -> {
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
}
else -> base.name.lowercase()
}
@@ -160,6 +252,30 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
BaseDataType.LONG -> "long"
BaseDataType.FLOAT -> "float"
BaseDataType.STR -> "str"
BaseDataType.POINTER -> {
when {
sub!=null -> "^^${sub.name.lowercase()}"
subType!=null -> "^^${subType!!.scopedNameString}"
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
else -> "?????"
}
}
BaseDataType.STRUCT_INSTANCE -> {
when {
sub!=null -> sub.name.lowercase()
subType!=null -> subType!!.scopedNameString
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
else -> "?????"
}
}
BaseDataType.ARRAY_POINTER -> {
when {
sub!=null -> "^^${sub.name.lowercase()}["
subType!=null -> "^^${subType!!.scopedNameString}["
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
else -> "????? ["
}
}
BaseDataType.ARRAY -> {
when(sub) {
BaseDataType.UBYTE -> "ubyte["
@@ -187,18 +303,36 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
BaseDataType.POINTER -> {
when {
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
targetType.isPointer -> this.isUnsignedWord || this == targetType
else -> false
}
}
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
BaseDataType.ARRAY_POINTER -> TODO("check assignability of array of pointers")
BaseDataType.UNDEFINED -> false
}
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
memsizer.memorySize(sub)
} else if(subType!=null) {
subType!!.memsize(memsizer)
} else {
memsizer.memorySize(base)
}
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
val isUndefined = base == BaseDataType.UNDEFINED
val isByte = base.isByte
val isUnsignedByte = base == BaseDataType.UBYTE
@@ -214,22 +348,25 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
val isSigned = base.isSigned
val isUnsigned = !base.isSigned
val isArray = base.isArray
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
val isPointer = base.isPointer
val isStructInstance = base.isStructInstance
val isPointerArray = base.isPointerArray
val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
val isString = base == BaseDataType.STR
val isBool = base == BaseDataType.BOOL
val isFloat = base == BaseDataType.FLOAT
val isLong = base == BaseDataType.LONG
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
val isIterable = base.isIterable
val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue
@@ -280,7 +417,7 @@ enum class RegisterOrPair {
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> ""
else -> throw kotlin.IllegalArgumentException("invalid register param type")
else -> throw IllegalArgumentException("invalid register param type")
}
return listOf("cx16", name.lowercase()+suffix)
}

View File

@@ -31,10 +31,10 @@ class C128Target: ICompilationTarget,
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override val BSSHIGHRAM_START = 0u // TODO address?
override val BSSHIGHRAM_END = 0u // TODO address?
override val BSSGOLDENRAM_START = 0u // TODO address?
override val BSSGOLDENRAM_END = 0u // TODO address?
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam

View File

@@ -7,7 +7,9 @@ import prog8.code.core.IMemSizer
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
if(dt.isPointerArray)
return 2 * numElements!! // array of pointers is just array of uwords
else if(dt.isArray) {
if(numElements==null) return 2 // treat it as a pointer size
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
@@ -26,6 +28,8 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> floatsize * (numElements ?: 1)
dt.isLong -> 4 * (numElements ?: 1)
dt.isPointer -> 2 // pointer is just a uword
dt.isStructInstance -> dt.subType!!.memsize(this)
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
else -> 2 * (numElements ?: 1)
}

View File

@@ -37,7 +37,10 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
is PtBlock -> node.name = "p8b_${node.name}"
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
is PtConstant -> node.name = "p8c_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> {
node.name = "p8v_${node.name}"
}
is PtStructDecl -> { /* do nothing */ }
}
}
@@ -50,10 +53,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
}
}
is PtSub -> {
prefixNamedNode(node)
node.parameters.forEach { prefixNamedNode(it) }
}
is PtSub -> prefixNamedNode(node)
is PtFunctionCall -> {
val stNode = st.lookup(node.name)!!
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
@@ -175,10 +175,10 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
newValue.add(elt)
else {
val newAddr = PtAddressOf(elt.position)
newAddr.children.add(elt.identifier.prefix(newAddr, st))
if(elt.arrayIndexExpr!=null)
newAddr.children.add(elt.arrayIndexExpr!!)
val newAddr = PtAddressOf(elt.type, elt.position)
newAddr.add(elt.identifier!!.prefix(newAddr, st))
if (elt.arrayIndexExpr != null)
newAddr.add(elt.arrayIndexExpr!!)
newAddr.parent = arrayValue
newValue.add(newAddr)
}
@@ -227,6 +227,7 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
StNodeType.CONSTANT -> 'c'
StNodeType.BUILTINFUNC -> 's'
StNodeType.MEMORYSLAB -> 'v'
StNodeType.STRUCTINSTANCE -> 'i'
else -> '?'
}
val newName = prefixScopedName(name, prefixType)
@@ -316,9 +317,9 @@ class AsmGen6502Internal (
if(symbolTable.allVariables.isNotEmpty()) {
println("Static variables (not in ZeroPage):")
symbolTable.allVariables
.filterNot { allocator.isZpVar(it.scopedName) }
.sortedBy { it.scopedName }.forEach {
println(" ${it.dt}\t${it.scopedName}\t")
.filterNot { allocator.isZpVar(it.scopedNameString) }
.sortedBy { it.scopedNameString }.forEach {
println(" ${it.dt}\t${it.scopedNameString}\t")
}
}
if(allocator.globalFloatConsts.isNotEmpty()) {
@@ -330,9 +331,9 @@ class AsmGen6502Internal (
if(symbolTable.allMemMappedVariables.isNotEmpty()) {
println("Memory mapped:")
symbolTable.allMemMappedVariables
.sortedWith( compareBy( {it.address}, {it.scopedName} ))
.sortedWith( compareBy( {it.address}, {it.scopedNameString} ))
.forEach { mvar ->
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedName}")
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedNameString}")
}
}
if(symbolTable.allMemorySlabs.isNotEmpty()) {
@@ -420,7 +421,7 @@ class AsmGen6502Internal (
}
fun asmVariableName(st: StNode, scope: IPtSubroutine?): String {
val name = asmVariableName(st.scopedName)
val name = asmVariableName(st.scopedNameString)
if(scope==null)
return name
// remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
@@ -621,7 +622,7 @@ class AsmGen6502Internal (
is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtNodeGroup -> stmt.children.forEach { translate(it) }
is PtJmpTable -> translate(stmt)
is PtNop -> {}
is PtNop, is PtStructDecl, is PtSubSignature -> {}
else -> throw AssemblyError("missing asm translation for $stmt")
}
}
@@ -838,7 +839,7 @@ class AsmGen6502Internal (
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
val counterVar = createTempVarReused(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65C02), stmt)
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
out("""
ldy #>$loopcount
@@ -858,7 +859,7 @@ $repeatLabel""")
// note: A/Y must have been loaded with the number of iterations!
// the iny + double dec is microoptimization of the 16 bit loop
val repeatLabel = makeLabel("repeat")
val counterVar = createTempVarReused(BaseDataType.UWORD, false, stmt)
val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
out("""
cmp #0
beq +
@@ -1046,17 +1047,21 @@ $repeatLabel""")
if(evaluateAddressExpression) {
val arrayIdx = jump.target as? PtArrayIndexer
if (arrayIdx!=null) {
val arrayVariable = arrayIdx.variable
if(arrayVariable==null)
TODO("support for ptr indexing ${arrayIdx.position}")
if (isTargetCpu(CpuType.CPU65C02)) {
if (!arrayIdx.splitWords) {
// if the jump target is an address in a non-split array (like a jump table of only pointers),
// on the 65c02, more optimal assembly can be generated using JMP (address,X)
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
out(" asl a | tax")
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
return JumpTarget(asmSymbolName(arrayVariable), true, true, false)
} else {
// print a message when more optimal code is possible for 65C02 cpu
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
if(variable is StStaticVariable && variable.length!!<=128)
val variable = symbolTable.lookup(arrayVariable.name)!!
if(variable is StStaticVariable && variable.length!!<=128u)
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
}
} else {
@@ -1075,17 +1080,18 @@ $repeatLabel""")
}
private fun translate(ret: PtReturn) {
val returnvalue = ret.children.singleOrNull()
val returnvalue = ret.children.singleOrNull() as? PtExpression
val sub = ret.definingSub()!!
val returnRegs = sub.returnsWhatWhere()
if(returnvalue!=null) {
if (sub.returns.single().isNumericOrBool) {
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
if (sub.signature.returns.single().isNumericOrBool) {
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
}
else {
// all else take its address and assign that also to AY register pair
val addrofValue = PtAddressOf(returnvalue.position)
val addrOfDt = returnvalue.type.typeForAddressOf(false)
val addrofValue = PtAddressOf(addrOfDt, returnvalue.position)
addrofValue.add(returnvalue as PtIdentifier)
addrofValue.parent = ret.parent
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
@@ -1265,8 +1271,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else {
out(" sta ${asmSymbolName(addrOf.identifier)}+${constOffset}")
out(" sta ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true
}
}
@@ -1305,8 +1313,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
} else {
out(" lda ${asmSymbolName(addrOf.identifier)}+${constOffset}")
out(" lda ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true
}
}
@@ -1355,8 +1365,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else {
out(" sta ${asmSymbolName(addrOf.identifier)}-${constOffset}")
out(" sta ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true
}
}
@@ -1386,8 +1398,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
} else {
out(" lda ${asmSymbolName(addrOf.identifier)}-${constOffset}")
out(" lda ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true
}
}
@@ -1418,7 +1432,15 @@ $repeatLabel""")
val node = stScope.astNode
if(node is PtSubroutineParameter)
return node
return node!!.definingSub()?.parameters?.singleOrNull { it.name===name }
val params = node!!.definingSub()?.signature?.children
if(params!=null) {
for(param in params) {
param as PtSubroutineParameter
if(param.name==name)
return param
}
}
return null
}
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
@@ -1604,7 +1626,9 @@ $repeatLabel""")
if(constIndex!=null) {
val offset = program.memsizer.memorySize(value.type, constIndex)
if(offset<256) {
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y")
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
return out(" ldy #$offset | $compare ${asmVariableName(value.variable!!)},y")
}
}
cmpViaScratch()

View File

@@ -44,6 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"setlsb" -> funcSetLsbMsb(fcall, false)
"setmsb" -> funcSetLsbMsb(fcall, true)
"memory" -> funcMemory(fcall, discardResult, resultRegister)
"structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
"peekw" -> funcPeekW(fcall, resultRegister)
"peekf" -> funcPeekF(fcall, resultRegister)
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
@@ -380,8 +381,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as PtString).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
val addressOf = PtAddressOf(fcall.position)
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), fcall.position)
addressOf.add(slabname)
addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
@@ -390,6 +392,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign, fcall.definingISub())
}
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult)
throw AssemblyError("should not discard result of struct allocation at $fcall")
if(fcall.args.isEmpty())
TODO("struct alloc in BSS")
else
TODO("static struct alloc with values")
}
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
when(fcall.args[0].type.base) {
@@ -415,8 +427,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
}
is PtMemoryByte -> {
@@ -438,8 +453,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords)
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
else
@@ -465,7 +482,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" ror ${varname},x")
}
is PtMemoryByte -> {
@@ -498,7 +518,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords)
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
else
@@ -521,8 +543,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
}
is PtMemoryByte -> {
@@ -545,7 +569,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable)
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords)
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
else
@@ -571,7 +597,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" rol ${varname},x")
}
is PtMemoryByte -> {
@@ -604,7 +632,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords)
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
else
@@ -634,6 +664,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val msbAdd: Int
if(indexer.splitWords) {
val arrayVariable = indexer.variable
if(arrayVariable==null)
TODO("support for ptr indexing ${indexer.position}")
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
indexer.children[0].parent = indexer
elementSize = 1
@@ -1112,7 +1144,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the msb byte out of the word array
if(arg.splitWords) {
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
if(arg.variable==null)
TODO("support for ptr indexing ${arg.position}")
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@@ -1129,7 +1163,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
val arrayVar = asmgen.asmVariableName(arg.variable)
if(arg.variable==null)
TODO("support for ptr indexing ${arg.position}")
val arrayVar = asmgen.asmVariableName(arg.variable!!)
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@@ -1211,7 +1247,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the lsb byte out of the word array
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
if(arg.variable==null)
TODO("support for ptr indexing ${arg.position}")
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@@ -1276,7 +1315,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) {
is PtIdentifier -> {
val addr = PtAddressOf(value.position)
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), value.position)
addr.add(value)
addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen)
@@ -1290,7 +1329,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
val addr = PtAddressOf(value.position)
val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), value.position)
addr.add(variable)
addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
@@ -1310,7 +1349,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(value.position)
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), value.position)
addr.add(value)
addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen)
@@ -1328,7 +1367,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(value.position)
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false),value.position)
addr.add(value)
addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen)

View File

@@ -500,10 +500,10 @@ $endLabel""")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel)
val iterableName = asmgen.asmVariableName(ident)
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!!
else -> 0
else -> 0u
}
when {
iterableDt.isString -> {
@@ -549,7 +549,7 @@ $loopLabel sty $indexVar
lda $iterableName,y
sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
if(numElements<=255u) {
asmgen.out("""
ldy $indexVar
iny
@@ -565,7 +565,7 @@ $loopLabel sty $indexVar
beq $endLabel""")
}
if(!asmgen.options.romable) {
if(numElements>=16) {
if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
@@ -592,7 +592,7 @@ $loopLabel sty $indexVar
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
if(numElements<=255u) {
asmgen.out("""
ldy $indexVar
iny
@@ -608,7 +608,7 @@ $loopLabel sty $indexVar
beq $endLabel""")
}
if(!asmgen.options.romable) {
if(numElements>=16) {
if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
@@ -635,12 +635,12 @@ $loopLabel sty $indexVar
lda $iterableName+1,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=127) {
if(numElements<=127u) {
asmgen.out("""
ldy $indexVar
iny
iny
cpy #${numElements*2}
cpy #${numElements*2u}
beq $endLabel
bne $loopLabel""")
} else {
@@ -653,7 +653,7 @@ $loopLabel sty $indexVar
beq $endLabel""")
}
if(!asmgen.options.romable) {
if(numElements>=16) {
if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(

View File

@@ -36,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val symbol = asmgen.symbolTable.lookup(call.name)!!
if(symbol.type == StNodeType.LABEL) {
require(call.void)
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
return
}
@@ -139,15 +139,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
}
else if(sub is PtSub) {
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
if(optimizeIntArgsViaCpuRegisters(parameters)) {
// Note that if the args fit into cpu registers, we don't concern ourselves here
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
// That is now up to the subroutine itself.
useCpuRegistersForArgs(call.args, sub)
} else {
// arguments via variables
val paramValues = sub.parameters.zip(call.args)
val (normalParams, registerParams) = paramValues.partition { it.first.register == null }
val paramValues = parameters.zip(call.args)
val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
if (normalParams.isNotEmpty()) {
for (p in normalParams)
argumentViaVariable(sub, p.first, p.second)
@@ -166,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
val params = sub.parameters
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
when(params.size) {
1 -> {
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
@@ -339,7 +340,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
val src = if(value.type.isPassByRef) {
if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY)
val addr = PtAddressOf(value.type.typeForAddressOf(false),Position.DUMMY)
addr.add(value)
addr.parent = scope as PtNode
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)

View File

@@ -885,7 +885,9 @@ _jump jmp (${target.asmLabel})
private fun loadAndCmp0MSB(value: PtExpression) {
when(value) {
is PtArrayIndexer -> {
val varname = asmgen.asmVariableName(value.variable)
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varname = asmgen.asmVariableName(value.variable!!)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
if(value.splitWords)
asmgen.out(" lda ${varname}_msb,y")
@@ -901,8 +903,8 @@ _jump jmp (${target.asmLabel})
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
} else {
var varname = asmgen.asmVariableName(value.identifier)
if(value.identifier.type.isSplitWordArray) {
var varname = asmgen.asmVariableName(value.identifier!!)
if(value.identifier!!.type.isSplitWordArray) {
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
}
asmgen.out(" lda #>$varname")
@@ -980,8 +982,10 @@ _jump jmp (${target.asmLabel})
if(value is PtIdentifier)
return compareLsbMsb(value.name, value.name+"+1")
if(value is PtArrayIndexer) {
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val constIndex = value.index.asConstInteger()
val varname = asmgen.asmVariableName(value.variable)
val varname = asmgen.asmVariableName(value.variable!!)
if(constIndex!=null) {
if(value.splitWords) {
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
@@ -1127,8 +1131,10 @@ _jump jmp (${target.asmLabel})
if(value is PtIdentifier)
return compareLsbMsb(value.name, value.name+"+1")
if(value is PtArrayIndexer) {
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val constIndex = value.index.asConstInteger()
val varname = asmgen.asmVariableName(value.variable)
val varname = asmgen.asmVariableName(value.variable!!)
if(constIndex!=null) {
if(value.splitWords) {
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
@@ -1241,7 +1247,9 @@ _jump jmp (${target.asmLabel})
is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger()
if(constIndex!=null) {
val varName = asmgen.asmVariableName(value.variable)
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varName = asmgen.asmVariableName(value.variable!!)
if(value.splitWords) {
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
}
@@ -1262,7 +1270,9 @@ _jump jmp (${target.asmLabel})
is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger()
if (constIndex != null) {
val varName = asmgen.asmVariableName(value.variable)
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varName = asmgen.asmVariableName(value.variable!!)
if(value.splitWords) {
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
}
@@ -1643,8 +1653,10 @@ _jump jmp (${target.asmLabel})
fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
val constIndex = left.index.asConstInteger()
if(constIndex!=null) {
if(left.variable==null)
TODO("support for ptr indexing ${left.position}")
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
val varName = asmgen.asmVariableName(left.variable)
val varName = asmgen.asmVariableName(left.variable!!)
if(left.splitWords) {
return if(notEquals)
translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
@@ -1707,13 +1719,13 @@ _jump jmp (${target.asmLabel})
}
}
is PtAddressOf -> {
if(left.isFromArrayElement)
if(left.isFromArrayElement) {
fallbackTranslateForSimpleCondition(stmt)
else {
val varname = if(left.identifier.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
} else {
val varname = if(left.identifier!!.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
} else {
left.identifier.name
left.identifier!!.name
}
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
translateAYNotEquals("#<$varname", "#>$varname")
@@ -1759,13 +1771,13 @@ _jump jmp (${target.asmLabel})
}
}
is PtAddressOf -> {
if(left.isFromArrayElement)
if(left.isFromArrayElement) {
fallbackTranslateForSimpleCondition(stmt)
else {
val varname = if(left.identifier.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
} else {
val varname = if(left.identifier!!.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
} else {
left.identifier.name
left.identifier!!.name
}
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
translateAYEquals("#<$varname", "#>$varname")

View File

@@ -349,7 +349,7 @@ internal class ProgramAndVarsGen(
val varsInBlock = getVars(scope)
// Zeropage Variables
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
zeropagevars2asm(varnames)
// MemDefs and Consts
@@ -363,7 +363,7 @@ internal class ProgramAndVarsGen(
// normal statically allocated variables
val variables = varsInBlock
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
.map { it.value as StStaticVariable }
nonZpVariables2asm(variables)
}
@@ -417,7 +417,7 @@ internal class ProgramAndVarsGen(
val varsInSubroutine = getVars(scope)
// Zeropage Variables
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
zeropagevars2asm(varnames)
// MemDefs and Consts
@@ -435,7 +435,7 @@ internal class ProgramAndVarsGen(
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
entrypointInitialization()
val params = sub.parameters
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
asmgen.out("; simple int arg(s) passed via cpu register(s)")
@@ -491,7 +491,7 @@ internal class ProgramAndVarsGen(
// normal statically allocated variables
val variables = varsInSubroutine
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
.map { it.value as StStaticVariable }
nonZpVariables2asm(variables)
@@ -546,12 +546,12 @@ internal class ProgramAndVarsGen(
stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value"
outputStringvar(varname, 0, it.value.second, it.value.first)
outputStringvar(varname, 0u, it.value.second, it.value.first)
}
arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value"
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null)
arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
}
asmgen.out("+")
@@ -619,7 +619,7 @@ internal class ProgramAndVarsGen(
fun generate(section: String, variables: List<StStaticVariable>) {
asmgen.out(" .section $section")
val (notAligned, aligned) = variables.partition { it.align == 0 }
val (notAligned, aligned) = variables.partition { it.align == 0u }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
}
@@ -644,8 +644,8 @@ internal class ProgramAndVarsGen(
if(varsWithInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables with init value")
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
notAlignedStrings.forEach {
outputStringvar(
it.name,
@@ -692,23 +692,27 @@ internal class ProgramAndVarsGen(
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
dt.isSplitWordArray -> {
alignVar(variable.align)
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
dt.isArray -> {
alignVar(variable.align)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
asmgen.out("${variable.name}\t.fill $numbytes")
}
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
dt.isPointerArray -> {
TODO("pointers are not supported yet")
}
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun alignVar(align: Int) {
if(align > 1)
private fun alignVar(align: UInt) {
if(align > 1u)
asmgen.out(" .align ${align.toHex()}")
}
@@ -745,7 +749,7 @@ internal class ProgramAndVarsGen(
throw AssemblyError("all string vars should have been interned into prog")
}
dt.isArray -> {
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
}
else -> {
throw AssemblyError("weird dt")
@@ -753,7 +757,7 @@ internal class ProgramAndVarsGen(
}
}
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
alignVar(align)
when {
dt.isUnsignedByteArray || dt.isBoolArray -> {
@@ -855,7 +859,7 @@ internal class ProgramAndVarsGen(
}
}
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) {
private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
alignVar(align)
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())

View File

@@ -52,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 }
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
var numVariablesAllocatedInZP = 0
@@ -60,9 +60,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
varsRequiringZp.forEach { variable ->
val result = zeropage.allocate(
variable.scopedName,
variable.scopedNameString,
variable.dt,
variable.length,
variable.length?.toInt(),
variable.astNode?.position ?: Position.DUMMY,
errors
)
@@ -79,9 +79,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) {
varsPreferringZp.forEach { variable ->
val result = zeropage.allocate(
variable.scopedName,
variable.scopedNameString,
variable.dt,
variable.length,
variable.length?.toInt(),
variable.astNode?.position ?: Position.DUMMY,
errors
)
@@ -92,16 +92,16 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// try to allocate the "don't care" interger variables into the zeropage until it is full.
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) {
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
for (variable in sortedList) {
if(variable.dt.isIntegerOrBool) {
if(zeropage.free.isEmpty()) {
break
} else {
val result = zeropage.allocate(
variable.scopedName,
variable.scopedNameString,
variable.dt,
variable.length,
variable.length?.toInt(),
variable.astNode?.position ?: Position.DUMMY,
errors
)

View File

@@ -46,6 +46,12 @@ internal class AnyExprAsmGen(
}
return assignFloatBinExpr(expr, assign)
}
expr.type.isPointer -> {
require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
"both operands must be pointers or uwords"
}
throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
}
else -> throw AssemblyError("weird expression type in assignment")
}
}

View File

@@ -39,8 +39,11 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
val asmVarname: String by lazy {
if (array == null)
variableAsmName!!
else
asmgen.asmVariableName(array.variable)
else {
if(array.variable==null)
TODO("asmVarname for array with pointer")
asmgen.asmVariableName(array.variable!!)
}
}
init {
@@ -160,8 +163,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val asmVarname: String
get() = if(array==null)
variableAsmName!!
else
asmgen.asmVariableName(array.variable)
else {
if(array.variable==null)
TODO("asmVarname for array with pointer")
asmgen.asmVariableName(array.variable!!)
}
companion object {
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
@@ -203,7 +209,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
val sub = symbol.astNode as IPtSubroutine
val returnType =
if(sub is PtSub && sub.returns.size>1)
if(sub is PtSub && sub.signature.returns.size>1)
DataType.UNDEFINED // TODO list of types instead?
else
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second

View File

@@ -249,7 +249,12 @@ internal class AssignmentAsmGen(
SourceStorageKind.ARRAY -> {
val value = assign.source.array!!
val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.variable)
val valueVar = value.variable
if(valueVar==null) {
TODO("translate assignmenton pointer ${value.position}")
return
}
val arrayVarName = asmgen.asmVariableName(valueVar)
val constIndex = value.index.asConstInteger()
if(value.splitWords) {
@@ -438,17 +443,21 @@ internal class AssignmentAsmGen(
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
when(val value = assign.source.expression!!) {
is PtAddressOf -> {
val source = asmgen.symbolTable.lookup(value.identifier.name)
require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" }
val arrayDt = value.identifier.type
val sourceName =
if(value.isMsbForSplitArray)
asmgen.asmSymbolName(value.identifier) + "_msb"
else if(arrayDt.isSplitWordArray)
asmgen.asmSymbolName(value.identifier) + "_lsb" // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(value.identifier)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
val identifier = value.identifier
if(identifier==null) TODO("read &dereference")
else {
val source = asmgen.symbolTable.lookup(identifier.name)
require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" }
val arrayDt = identifier.type
val sourceName =
if (value.isMsbForSplitArray)
asmgen.asmSymbolName(identifier) + "_msb"
else if (arrayDt.isSplitWordArray)
asmgen.asmSymbolName(identifier) + "_lsb" // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(identifier)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
}
}
is PtBool -> throw AssemblyError("source kind should have been literalboolean")
is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
@@ -606,7 +615,7 @@ internal class AssignmentAsmGen(
val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.translateFunctionCall(value)
if(sub is PtSub && sub.returns.size>1) {
if(sub is PtSub && sub.signature.returns.size>1) {
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
// (this allows unencumbered use of many Rx registers if you don't return that many values)
val returnRegs = sub.returnsWhatWhere()
@@ -922,12 +931,18 @@ internal class AssignmentAsmGen(
val tgt = PtAssignTarget(false, assign.target.position)
val targetarray = assign.target.array!!
val array = PtArrayIndexer(assign.target.datatype, targetarray.position)
array.add(targetarray.variable)
array.add(targetarray.index)
tgt.add(array)
assignTrue = PtAssignment(assign.position)
assignTrue.add(tgt)
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
val targetArrayVar = targetarray.variable
if(targetArrayVar==null) {
TODO("optimized comparison on pointer ${targetarray.position}")
} else {
array.add(targetArrayVar)
array.add(targetarray.index)
tgt.add(array)
assignTrue = PtAssignment(assign.position)
assignTrue.add(tgt)
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
}
}
TargetStorageKind.REGISTER -> { /* handled earlier */ return true }
TargetStorageKind.VOID -> { /* do nothing */ return true }
@@ -1312,25 +1327,35 @@ internal class AssignmentAsmGen(
val rightArrayIndexer = expr.right as? PtArrayIndexer
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type.isByte && right.type.isByte) {
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
asmgen.out(" clc | adc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
val leftArrayVar = leftArrayIndexer.variable
if(leftArrayVar==null) {
TODO("optimized plusmin pointer ${leftArrayIndexer.position}")
} else {
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
if (!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
if (!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(leftArrayVar)
asmgen.out(" clc | adc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
}
} else if(rightArrayIndexer!=null && rightArrayIndexer.type.isByte && left.type.isByte) {
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
if (expr.operator == "+")
asmgen.out(" clc | adc $arrayvarname,y")
else
asmgen.out(" sec | sbc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
val rightArrayVar = rightArrayIndexer.variable
if(rightArrayVar==null) {
TODO("optimized plusmin pointer ${rightArrayIndexer.position}")
} else {
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
if (!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
if (!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(rightArrayVar)
if (expr.operator == "+")
asmgen.out(" clc | adc $arrayvarname,y")
else
asmgen.out(" sec | sbc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
}
} else if(expr.operator=="+" && leftMemByte!=null && right.type.isByte && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
return true
@@ -1356,7 +1381,7 @@ internal class AssignmentAsmGen(
return true
}
}
} else if(dt.isWord) {
} else if(dt.isWord || dt.isPointer) {
fun doAddOrSubWordExpr() {
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
@@ -1383,11 +1408,13 @@ internal class AssignmentAsmGen(
when (right) {
is PtAddressOf -> {
var symbol = asmgen.asmVariableName(right.identifier)
if(right.isFromArrayElement) {
TODO("address-of array element $symbol at ${right.position}")
TODO("address-of array element at ${right.position}")
} else if(right.dereference!=null) {
TODO("read &dereference")
} else {
if(right.identifier.type.isSplitWordArray) {
var symbol = asmgen.asmVariableName(right.identifier!!)
if(right.identifier!!.type.isSplitWordArray) {
symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb"
}
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
@@ -1614,10 +1641,16 @@ internal class AssignmentAsmGen(
}
val rightArray = expr.right as? PtArrayIndexer
if(rightArray!=null) {
val rightArrayVar = rightArray.variable
if(rightArrayVar==null) {
TODO("optimized bitwise pointer ${rightArray.position}")
return false
}
val constIndex = rightArray.index.asConstInteger()
if(constIndex!=null) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${program.memsizer.memorySize(rightArray.type, constIndex)}"
val valueVarname = "${asmgen.asmSymbolName(rightArrayVar)} + ${program.memsizer.memorySize(rightArray.type, constIndex)}"
when(expr.operator) {
"&" -> asmgen.out(" and $valueVarname")
"|" -> asmgen.out(" ora $valueVarname")
@@ -1726,12 +1759,17 @@ internal class AssignmentAsmGen(
is PtArrayIndexer -> {
val constIndex = right.index.asConstInteger()
if(constIndex!=null) {
val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${program.memsizer.memorySize(right.type, constIndex)}"
when(operator) {
"and" -> asmgen.out(" and $valueVarname")
"or" -> asmgen.out(" ora $valueVarname")
"xor" -> asmgen.out(" eor $valueVarname")
else -> throw AssemblyError("invalid logical operator")
val rightArrayVar = right.variable
if(rightArrayVar==null) {
TODO("assign result into A pointer ${right.position}")
} else {
val valueVarname = "${asmgen.asmSymbolName(rightArrayVar)} + ${program.memsizer.memorySize(right.type, constIndex)}"
when(operator) {
"and" -> asmgen.out(" and $valueVarname")
"or" -> asmgen.out(" ora $valueVarname")
"xor" -> asmgen.out(" eor $valueVarname")
else -> throw AssemblyError("invalid logical operator")
}
}
}
else assignViaScratch()
@@ -1980,7 +2018,7 @@ $endLabel""")
val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.UNDEFINED to 0
else -> DataType.UNDEFINED to 0u
}
when {
dt.isString -> {
@@ -1988,7 +2026,7 @@ $endLabel""")
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null)
asmgen.out(" pla")
asmgen.out(" ldy #${numElements-1}")
asmgen.out(" ldy #${numElements-1u}")
asmgen.out(" jsr prog8_lib.containment_bytearray")
}
dt.isFloatArray -> {
@@ -2143,7 +2181,7 @@ $endLabel""")
assignExpressionToRegister(value, RegisterOrPair.A, valueDt.isSigned)
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.A, valueDt.base)
}
valueDt.isWord -> {
valueDt.isWord || valueDt.isPointer -> {
assignExpressionToRegister(value, RegisterOrPair.AY, valueDt.isSigned)
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.AY, valueDt.base)
}
@@ -2296,6 +2334,12 @@ $endLabel""")
return
}
if(targetDt.isUnsignedWord && valueDt.isPointer) {
assignExpressionToRegister(value, RegisterOrPair.AY, false)
assignRegisterpairWord(target, RegisterOrPair.AY)
return
}
// No more special optimized cases yet. Do the rest via more complex evaluation
// note: cannot use assignTypeCastedValue because that is ourselves :P
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
@@ -2622,6 +2666,18 @@ $endLabel""")
}
}
BaseDataType.STR -> throw AssemblyError("cannot typecast a string value")
BaseDataType.POINTER -> {
if(targetDt.isWord || targetDt.isPointer) {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("non-word regs")
}
} else {
throw AssemblyError("cannot assign pointer to $targetDt")
}
}
else -> throw AssemblyError("weird type")
}
}
@@ -4088,8 +4144,10 @@ $endLabel""")
addressOf != null -> {
if(addressOf.isFromArrayElement) {
TODO("address-of array element $addressOf")
} else if(addressOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addressOf.position}")
} else {
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier)}")
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier!!)}")
}
}
addressExpr is PtIdentifier -> {

View File

@@ -224,7 +224,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
TargetStorageKind.ARRAY -> {
val indexNum = target.array!!.index as? PtNumber
val targetArrayVar = target.array!!.variable
if(targetArrayVar==null) {
TODO("array indexing on pointer ${target.position}")
return
}
val indexNum = target.array.index as? PtNumber
if (indexNum!=null) {
val index = indexNum.number.toInt()
if(target.array.splitWords) {
@@ -320,7 +325,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda ${target.array.variable.name},y")
asmgen.out(" lda ${targetArrayVar.name},y")
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> {
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
@@ -366,7 +371,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda $tempVar")
}
}
asmgen.out(" sta ${target.array.variable.name},y")
asmgen.out(" sta ${targetArrayVar.name},y")
}
target.datatype.isWord -> {
@@ -377,11 +382,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false)
if(target.array.splitWords) {
asmgen.out(" lda ${target.array.variable.name}_lsb,y")
asmgen.out(" ldx ${target.array.variable.name}_msb,y")
asmgen.out(" lda ${targetArrayVar.name}_lsb,y")
asmgen.out(" ldx ${targetArrayVar.name}_msb,y")
} else {
asmgen.out(" lda ${target.array.variable.name},y")
asmgen.out(" ldx ${target.array.variable.name}+1,y")
asmgen.out(" lda ${targetArrayVar.name},y")
asmgen.out(" ldx ${targetArrayVar.name}+1,y")
}
val block = target.origAstTarget?.definingBlock()
when(value.kind) {
@@ -450,9 +455,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
asmgen.restoreRegisterStack(CpuRegister.Y, true)
if(target.array.splitWords)
asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y")
asmgen.out(" sta ${targetArrayVar.name}_lsb,y | txa | sta ${targetArrayVar.name}_msb,y")
else
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
asmgen.out(" sta ${targetArrayVar.name},y | txa | sta ${targetArrayVar.name}+1,y")
}
target.datatype.isFloat -> {
@@ -515,7 +520,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
val arrayvar = asmgen.asmVariableName(array.variable)
val arrayVar = array.variable
if(arrayVar==null) {
TODO("indexed inc/dec on pointer ${array.position}")
return false
}
val arrayvar = asmgen.asmVariableName(arrayVar)
when {
array.type.isByte -> {
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
@@ -972,7 +982,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out("+\tinc ${'$'}ffff\t; modified")
asmgen.out($$"+\tinc $ffff\t; modified")
}
} else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
@@ -992,7 +1002,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out("+\tdec ${'$'}ffff\t; modified")
asmgen.out($$"+\tdec $ffff\t; modified")
}
} else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
@@ -1125,11 +1135,18 @@ $shortcutLabel:""")
}
if(value is PtArrayIndexer && value.isSimple()) {
val valueVar = value.variable
if(valueVar==null) {
TODO("inplace modification on pointer ${value.position}")
return
}
// use the already existing optimized codegen for regular assignments x += array[index]
val binexpr = PtBinaryExpression(operator, dt, value.position)
binexpr.add(PtIdentifier(name, dt, value.position))
val arrayValue = PtArrayIndexer(value.type, value.position)
arrayValue.add(value.variable)
arrayValue.add(valueVar)
arrayValue.add(value.index)
binexpr.add(arrayValue)
binexpr.parent = value
@@ -2466,7 +2483,7 @@ $shortcutLabel:""")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
valueDt.isWord -> {
valueDt.isWord || valueDt.isPointer -> {
// the value is a proper 16-bit word, so use both bytes of it.
when (operator) {
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
@@ -2888,7 +2905,7 @@ $shortcutLabel:""")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
valueDt.isWord -> {
valueDt.isWord || valueDt.isPointer -> {
// the value is a proper 16-bit word, so use both bytes of it.
if(value is PtArrayIndexer && value.isSimple()) {
@@ -2903,7 +2920,12 @@ $shortcutLabel:""")
"-" -> {
if(value.index.type.isByte) {
// it's an array indexed by a byte so we can use sbc array,y
val arrayname = value.variable.name
val valueVar = value.variable
if(valueVar==null) {
TODO("inplace modification on pointer ${value.position}")
return
}
val arrayname = valueVar.name
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
if(value.splitWords) {
asmgen.out("""

View File

@@ -5,7 +5,9 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
if(dt.isPointerArray)
return 2 * numElements!!
else if(dt.isArray) {
require(numElements != null)
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements

View File

@@ -124,30 +124,100 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val target = augAssign.target
val targetDt = irType(target.type)
val value = augAssign.value
val memTarget = target.memory
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
val symbol = target.identifier?.name
val array = target.array
val value = augAssign.value
val signed = target.type.isSigned
val pointerDeref = target.pointerDeref
val chunks: IRCodeChunks
val chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
if(pointerDeref!=null) {
val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
val oldvalueReg = codeGen.registers.next(targetDt)
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
operandTr = expressionEval.translateExpression(value)
inplaceInstrs += operandTr.chunks
}
if(targetDt== IRDataType.FLOAT) {
if(fieldOffset>0u)
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()), null)
else
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
when(augAssign.operator) {
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"+" -> { /* inplace + is a no-op */ }
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
}
} else {
if(fieldOffset>0u)
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()), null)
else
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
when(augAssign.operator) {
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"or=" -> {
val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
val valueTr = expressionEval.translateExpression(value)
inplaceInstrs += valueTr.chunks
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
}
"and=" -> {
val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
val valueTr = expressionEval.translateExpression(value)
inplaceInstrs += valueTr.chunks
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
}
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
"+" -> { /* inplace + is a no-op */ }
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
}
}
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
chunks = inplaceInstrs
} else {
chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} ?: fallbackAssign(augAssign)
}
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} ?: fallbackAssign(augAssign)
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
return chunks
}
@@ -240,45 +310,53 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
when(operator) {
"+" -> { }
"-" -> {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val skipCarryLabel = codeGen.createLabelName()
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), skipCarryLabel)
} else {
val indexReg = loadIndex()
val registerLsb = codeGen.registers.next(IRDataType.BYTE)
val registerMsb = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb)
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0)
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel)
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb)
}
result += IRCodeChunk(skipCarryLabel, null).also {
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
}
}
}
"~" -> {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), null)
} else {
val indexReg = loadIndex()
val register = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
}
}
}
@@ -292,44 +370,55 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
when(operator) {
"+" -> { }
"-" -> {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else {
val indexReg = loadIndex()
val register = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
}
}
}
"~" -> {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else {
val indexReg = loadIndex()
val register = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
}
}
}
"not" -> {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val register = codeGen.registers.next(vmDt)
if(constIndex!=null) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
} else {
val indexReg = loadIndex()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
}
}
}
@@ -343,6 +432,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val targetPointerDeref = assignment.target.pointerDeref
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
@@ -400,80 +490,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result
}
else if(targetArray!=null) {
val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type, null)
val fixedIndex = targetArray.index.asConstInteger()
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
}
} else {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
}
val eltSize = codeGen.program.memsizer.memorySize(targetArray.type, null)
val variable = targetArray.variable
if(variable==null)
translateRegularAssignPointerIndexed(result, targetArray.pointerderef!!, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
else if(variable.type.isPointer)
assignToIndexedSimplePointer(result, variable, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
else
translateRegularAssignArrayIndexed(result, variable.name, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
return result
}
else if(targetMemory!=null) {
@@ -497,31 +521,33 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result
}
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
// STOREIX only works with byte index.
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
if(ptrWithOffset!=null) {
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
if(constOffset in 0..255) {
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val pointerReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
}
return result
}
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result
}
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result
}
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
@@ -531,29 +557,191 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result
}
else if(targetPointerDeref!=null) {
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
val actualValueReg = if(targetPointerDeref.type.isFloat) valueFpRegister else valueRegister
codeGen.storeValueAtPointersLocation(result, addressReg, offset, targetPointerDeref.type, zero, actualValueReg)
return result
}
else
throw AssemblyError("weird assigntarget")
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also returned.
private fun assignToIndexedSimplePointer(
result: MutableList<IRCodeChunkBase>,
targetIdent: PtIdentifier,
eltSize: Int,
targetArray: PtArrayIndexer,
zeroValue: Boolean,
targetDt: IRDataType,
valueRegister: Int,
valueFpRegister: Int
) {
val pointerTr = expressionEval.translateExpression(targetIdent)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
val result = mutableListOf<IRCodeChunkBase>()
if(itemsize==1 || array.splitWords) {
val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
val constIndex = targetArray.index.asConstInteger()
if(zeroValue) {
if(constIndex!=null) {
val offset = eltSize * constIndex
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = offset), null)
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
}
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), true, -1)
} else {
if(constIndex!=null) {
val offset = eltSize * constIndex
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = offset), null)
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
}
val realValueReg = if(targetDt == IRDataType.FLOAT) valueFpRegister else valueRegister
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), false, realValueReg)
}
}
private fun translateRegularAssignArrayIndexed(
result: MutableList<IRCodeChunkBase>,
variable: String,
eltSize: Int,
targetArray: PtArrayIndexer,
zero: Boolean,
targetDt: IRDataType,
valueRegister: Int,
valueFpRegister: Int
) {
val fixedIndex = targetArray.index.asConstInteger()
val arrayLength = codeGen.symbolTable.getLength(variable)
if(zero) {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*eltSize)
}
result += chunk
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
}
} else {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*eltSize
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
}
result += chunk
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*eltSize)
}
result += chunk
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
}
}
private fun translateRegularAssignPointerIndexed(
result: MutableList<IRCodeChunkBase>,
pointerderef: PtPointerDeref,
eltSize: Int,
targetArray: PtArrayIndexer,
zero: Boolean,
targetDt: IRDataType,
valueRegister: Int,
valueFpRegister: Int
) {
val pointerTr = expressionEval.translateExpression(pointerderef)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
val fixedIndex = targetArray.index.asConstInteger()
if(fixedIndex!=null) {
val offset = fixedIndex*eltSize
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
if(zero) {
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg), null)
} else {
addInstr(
result,
if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = valueFpRegister, reg1 = pointerReg)
else
IRInstruction(Opcode.STOREI, targetDt, reg1 = valueRegister, reg2 = pointerReg), null
)
}
} else {
// index is an expression
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
if(zero) {
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg), null)
} else {
addInstr(result,
if(targetDt== IRDataType.FLOAT)
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 =valueFpRegister, reg1 = pointerReg)
else
IRInstruction(Opcode.STOREI, targetDt, reg1 = valueRegister, reg2 = pointerReg)
, null)
}
}
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
private fun operatorAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
@@ -565,14 +753,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
}
} else {
val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -594,6 +782,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorLogicalAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
@@ -605,7 +797,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -652,20 +844,24 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(array.splitWords) {
val valueRegLsb = codeGen.registers.next(IRDataType.BYTE)
val valueRegMsb = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
}
} else {
val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -695,10 +891,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(array.splitWords) {
throw AssemblyError("logical or on (split) word array should not happen")
} else {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -804,11 +1004,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) {
if(constValue!=1) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val valueReg=codeGen.registers.next(eltDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -861,13 +1065,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(constValue==1) {
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else {
val valueReg=codeGen.registers.next(eltDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -927,14 +1135,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(constIndex!=null) {
val skip = codeGen.createLabelName()
if(constValue==1) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val lsbReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
}
result += IRCodeChunk(skip, null).also {
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
}
return result
} else {
@@ -944,7 +1156,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return null // fallback to slow method // TODO("inplace split word array -")
}
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?,
vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
@@ -954,13 +1167,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(constValue==1) {
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else {
val valueReg=codeGen.registers.next(elementDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue)
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result
@@ -971,39 +1188,36 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return null // TODO("optimized memory in-place +"")
val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(constAddress!=null)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null)
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
}
else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
else
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(constAddress!=null)
addInstr(result, if (constAddress != null)
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null)
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
}
else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = constAddress)
if (constAddress != null)
addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress), null)
else
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null)
addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null)
}
}
return result
@@ -1016,10 +1230,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(constIndex!=null) {
val skip = codeGen.createLabelName()
if(constValue==1) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
}
result += IRCodeChunk(skip, null)
return result
@@ -1037,24 +1255,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(array.splitWords) {
repeat(constValue) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
}
}
} else {
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constValue==1) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
} else {
val valueReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
}
@@ -1074,13 +1296,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
IRInstruction(opc, vmDt, labelSymbol = symbol)
addInstr(result, ins, null)
} else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(constAddress!=null)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = constAddress)
IRInstruction(opc, vmDt, reg1 = shiftReg, address = constAddress)
else
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
IRInstruction(opc, vmDt, reg1 = shiftReg, labelSymbol = symbol)
addInstr(result, ins, null)
}
return result
@@ -1093,24 +1320,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
if(array.splitWords) {
repeat(constValue) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
}
}
} else {
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constValue==1) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
} else {
val valueReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
}
@@ -1129,12 +1360,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
, null)
} else {
val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1)
val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = constAddress)
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, address = constAddress)
else
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, labelSymbol = symbol)
,null)
}
return result
@@ -1142,6 +1378,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorXorInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val arrayVariableName = array.variable?.name
if(arrayVariableName==null)
TODO("support for ptr indexing ${array.position}")
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
@@ -1153,14 +1393,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
}
} else {
val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
}
}
return result

View File

@@ -1,5 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
@@ -27,9 +28,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"lsb" -> funcLsb(call)
"memory" -> funcMemory(call)
"peek" -> funcPeek(call, IRDataType.BYTE)
"peekbool" -> funcPeek(call, IRDataType.BYTE)
"peekw" -> funcPeek(call, IRDataType.WORD)
"peekf" -> funcPeek(call, IRDataType.FLOAT)
"poke" -> funcPoke(call, IRDataType.BYTE)
"pokebool" -> funcPoke(call, IRDataType.BYTE)
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
"pokew" -> funcPoke(call, IRDataType.WORD)
"pokef" -> funcPoke(call, IRDataType.FLOAT)
"pokemon" -> funcPokemon(call)
@@ -46,6 +50,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"structalloc" -> funcStructAlloc(call)
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
}
}
@@ -494,8 +500,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val name = (call.args[0] as PtString).value
val code = IRCodeChunk(null, null)
val resultReg = codeGen.registers.next(IRDataType.WORD)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabPrefix.prog8_memoryslab_$name")
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
}
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val resultReg = codeGen.registers.next(IRDataType.WORD)
val labelname = SymbolTable.labelnameForStructInstance(call)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = labelname)
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
}
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -545,7 +559,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val arr = (arg as? PtArrayIndexer)
val index = arr?.index?.asConstInteger()
if(arr!=null && index!=null) {
val variable = arr.variable.name
if(arr.variable==null)
TODO("support for ptr indexing ${arr.position}")
val variable = arr.variable!!.name
if(arr.splitWords) {
result += IRCodeChunk(null, null).also {
when(opcodeMemAndReg.first) {
@@ -614,7 +630,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(target.splitWords) {
// lsb/msb in split arrays, element index 'size' is always 1
val constIndex = target.index.asConstInteger()
val varName = target.variable.name + if(msb) "_msb" else "_lsb"
if(target.variable==null)
TODO("support for ptr indexing ${target.position}")
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
if(isConstZeroValue) {
if(constIndex!=null) {
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
@@ -648,6 +666,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
else {
val targetVariable = target.variable
if(targetVariable==null)
TODO("support for ptr indexing ${target.position}")
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
val constIndex = target.index.asConstInteger()
if(isConstZeroValue) {
@@ -656,7 +678,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val offset = eltSize*constIndex + if(msb) 1 else 0
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
}
} else {
val indexTr = exprGen.translateExpression(target.index)
@@ -666,7 +688,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
if(msb)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
}
}
} else {
@@ -677,7 +699,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val offset = eltSize*constIndex + if(msb) 1 else 0
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
}
} else {
val indexTr = exprGen.translateExpression(target.index)
@@ -687,7 +709,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
if(msb)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
}
}
}

View File

@@ -86,12 +86,107 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
is PtFunctionCall -> translate(expr)
is PtContainmentCheck -> translate(expr)
is PtPointerDeref -> translate(expr)
is PtRange,
is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
}
}
private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
var pointerReg: Int
if(deref.startpointer.type.isStructInstance) {
TODO("translate structinstance deref???")
/*
val arrayIndexer = deref.startpointer as? PtArrayIndexer
if(arrayIndexer==null)
throw AssemblyError("when start pointer is struct instance, array indexer is expected PTR[x]")
// first evaluate the adress of POINTER[x].a
// which is: value in pointer + x*sizeof(struct) + offsetof(a)
// then use traverseDerefChainToCalculateFinalAddress on b.c.d.field
val struct = deref.startpointer.type.subType as StStruct
val chain = ArrayDeque(deref.chain)
val firstField = struct.getField(chain.removeFirst(), codeGen.program.memsizer)
val pointerTr = translateExpression(arrayIndexer.variable)
result += pointerTr.chunks
pointerReg = pointerTr.resultReg
val constIndex = arrayIndexer.index.asConstInteger()
if(constIndex!=null) {
val offset = constIndex * struct.size.toInt()
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
} else {
val indexTr = translateExpression(arrayIndexer.index)
result += indexTr.chunks
// multiply the index by the size of the struct and add that to the pointer, then add the offset of the field,
// and retrieve the pointer value that is stored there, or the actual value if it's a pointer to simple type
result += IRCodeChunk(null, null).also {
val indexReg: Int
if(arrayIndexer.index.type.isByte) {
// extend array index to word
indexReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
} else {
indexReg = indexTr.resultReg
}
it += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
}
}
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = firstField.second.toInt())
if (firstField.first.isPointer) {
// get the address stored in the pointer and use that for the rest of the chain
// LOADI has an exception to allo reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
} else {
require(chain.isEmpty())
// it's a pointer to a simple value, so keep the pointer as-is
}
}
// now use traverseDerefChainToCalculateFinalAddress on b.c.d and finally field or on b.c.d.field if field isn't a field.
val derefField = if(deref.type.isPointer) null else chain.removeLastOrNull()
actualDeref = PtPointerDeref(deref.type, chain, derefField, deref.position)
actualDeref.add(arrayIndexer.variable)
*/
} else {
val tr = translateExpression(deref.startpointer)
result += tr.chunks
pointerReg = tr.resultReg
}
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
result += instructions
if(offset==0u) {
val irdt = irType(deref.type)
return if(deref.type.isFloat) {
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
} else {
val resultReg = codeGen.registers.next(irdt)
addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
ExpressionCodeResult(result, irdt, resultReg, -1)
}
}
// load field with offset
return if(deref.type.isFloat) {
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
} else {
val irdt = irType(deref.type)
val resultReg = codeGen.registers.next(irdt)
addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
ExpressionCodeResult(result, irdt, resultReg, -1)
}
}
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
if((ifExpr.condition as? PtPrefix)?.operator=="not")
@@ -160,19 +255,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(expr: PtAddressOf): ExpressionCodeResult {
val vmDt = irType(expr.type)
val symbol = expr.identifier.name
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
val result = mutableListOf<IRCodeChunkBase>()
val resultRegister = codeGen.registers.next(vmDt)
val identifier = expr.identifier
fun loadAddressOfArrayLabel(reg: Int) {
if (expr.isMsbForSplitArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
} else if (expr.identifier.type.isSplitWordArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
} else if (identifier!!.type.isSplitWordArray) {
// the _lsb split array comes first in memory
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
} else
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
}
if(expr.isFromArrayElement) {
@@ -183,30 +277,41 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
ixWord
} else indexTr.resultReg
if(expr.identifier.type.isUnsignedWord) {
val resultRegister = codeGen.registers.next(vmDt)
if(identifier!!.type.isUnsignedWord) {
require(!expr.isMsbForSplitArray)
result += IRCodeChunk(null, null).also {
val ptr = codeGen.symbolTable.lookup(expr.identifier.name)
val ptr = codeGen.symbolTable.lookup(identifier.name)
it += if(ptr is StConstant)
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
else
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
}
} else {
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
result += IRCodeChunk(null, null).also {
loadAddressOfArrayLabel(resultRegister)
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
if (eltSize > 1 && !identifier.type.isSplitWordArray) {
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1 = indexWordReg, immediate = eltSize)
}
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
}
}
} else {
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
} else if(expr.identifier!=null ) {
val resultRegister = codeGen.registers.next(vmDt)
loadAddressOfArrayLabel(resultRegister)
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
} else {
require(vmDt==IRDataType.WORD)
val pointerTr = translateExpression(expr.dereference!!.startpointer)
result += pointerTr.chunks
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
result += instructions
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
}
private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
@@ -220,31 +325,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
val ptrWithOffset = mem.address as? PtBinaryExpression
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
// LOADIX only works with byte index.
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
if(ptrWithOffset!=null) {
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
if(constOffset in 0..255) {
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val pointerReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg, immediate = constOffset)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
val tr = translateExpression(mem.address)
addToResult(result, tr, tr.resultReg, -1)
@@ -343,11 +449,30 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
if(arrayIx.type.isStructInstance)
throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance; this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name
val arrayVar = arrayIx.variable
if(arrayVar==null) {
val pointerTr = translateExpression(arrayIx.pointerderef!!)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
}
if(arrayVar.type.isPointer) {
val pointerTr = translateExpression(arrayVar)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
}
require(!arrayVar.type.isPointer) { "only regular array indexing here ${arrayIx.position}" }
var resultRegister = -1
var resultFpRegister = -1
val arrayVarSymbol = arrayVar.name
if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD)
@@ -374,9 +499,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
}
var resultFpRegister = -1
if(arrayIx.index is PtNumber) {
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
fun indexByNumber(index: Int) {
val memOffset = index * eltSize
if(vmDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
@@ -385,23 +509,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
resultRegister = codeGen.registers.next(vmDt)
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
}
} else {
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1)
if(eltSize>1)
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize)
}
fun indexByExpression() {
val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
result += code
if(vmDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null)
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
}
else {
resultRegister = codeGen.registers.next(vmDt)
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null)
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, labelSymbol = arrayVarSymbol), null)
}
}
if(arrayIx.index is PtNumber)
indexByNumber((arrayIx.index as PtNumber).number.toInt())
else
indexByExpression()
return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
}
private fun translatePointerIndexing(
result: MutableList<IRCodeChunkBase>,
pointerReg: Int,
index: PtExpression,
eltSize: Int,
resultDt: IRDataType
): ExpressionCodeResult {
var resultRegister = -1
var resultFpRegister = -1
if(index is PtNumber) {
val memOffset = eltSize * index.number.toInt()
if(memOffset>0)
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
}
else {
val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
}
if(resultDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
}
else {
resultRegister = codeGen.registers.next(resultDt)
addInstr(result, IRInstruction(Opcode.LOADI, resultDt, reg1=resultRegister, reg2=pointerReg), null)
}
return ExpressionCodeResult(result, resultDt, resultRegister, resultFpRegister)
}
private fun translate(expr: PtPrefix): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(expr.value)
@@ -442,9 +604,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
}
valueDt.isWord -> {
valueDt.isWord || valueDt.isPointer -> {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result)
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
}
valueDt.isFloat -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
@@ -507,6 +669,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
BaseDataType.POINTER -> {
actualResultReg2 = tr.resultReg
}
else -> throw AssemblyError("weird cast value type")
}
}
@@ -550,6 +715,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast value type")
}
}
BaseDataType.POINTER -> {
require(valueDt.isUnsignedWord || valueDt.isPointer)
actualResultReg2 = tr.resultReg
// no further conversion required, pointers are all just uwords
}
BaseDataType.ARRAY_POINTER -> {
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
}
else -> throw AssemblyError("weird cast type")
}
@@ -578,6 +751,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
"." -> operatorDereference(binExpr, vmDt) // eww, nasty, would rather not have any such expressions anymore
else -> throw AssemblyError("weird operator ${binExpr.operator}")
}
}
@@ -585,11 +759,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
if(callTarget.scopedNameString in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
return translateStackFunctions(fcall, callTarget)
}
when(callTarget.scopedName) {
when(callTarget.scopedNameString) {
"sys.clear_carry" -> {
val chunk = mutableListOf<IRCodeChunkBase>()
addInstr(chunk, IRInstruction(Opcode.CLC), null)
@@ -638,7 +812,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
// return value(s)
// TODO: for current implemenation of the call convention in case of multiple return values,
// TODO: for current implementation of the call convention in case of multiple return values,
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
// So we use an empty list to avoid confusion here. This may change in a future version.
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
@@ -769,6 +943,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
}
is StStruct -> {
throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
}
else -> {
if(callTarget.type == StNodeType.LABEL) {
require(fcall.void)
@@ -778,7 +955,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
else {
throw AssemblyError("invalid node type")
throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
}
}
}
@@ -811,7 +988,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
val chunk = mutableListOf<IRCodeChunkBase>()
when(callTarget.scopedName) {
when(callTarget.scopedNameString) {
"sys.push" -> {
// push byte
val tr = translateExpression(fcall.args.single())
@@ -1238,10 +1415,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg)
addInstr(result, if(dt.isSigned)
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
else
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
, null)
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
else
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
, null)
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
}
} else {
@@ -1256,10 +1433,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
addInstr(result, if (dt.isSigned)
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
else
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
, null)
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
else
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
, null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
} else {
val leftTr = translateExpression(binExpr.left)
@@ -1267,10 +1444,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1)
addInstr(result, if (dt.isSigned)
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
else
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
, null)
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
else
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
, null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
}
}
@@ -1438,6 +1615,83 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
private fun operatorDereference(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult {
// the only case we support here is: a.b.c[i] . value
val left = binExpr.left as? PtArrayIndexer
val right = binExpr.right as? PtIdentifier
require(binExpr.operator=="." && left!=null && right!=null) {"invalid dereference expression ${binExpr.position}"}
val result = mutableListOf<IRCodeChunkBase>()
val indexedTr = translateExpression(left)
result += indexedTr.chunks
val pointerReg = indexedTr.resultReg
val struct = left.type.dereference().subType as? StStruct
require(indexedTr.dt== IRDataType.WORD && struct!=null)
val field = struct.getField(right.name, this.codeGen.program.memsizer)
var resultFpReg = -1
var resultReg = -1
if(vmDt==IRDataType.FLOAT)
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
else
resultReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = field.second.toInt())
it += if(vmDt==IRDataType.FLOAT)
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
else
IRInstruction(Opcode.LOADI, vmDt, reg1 = resultReg, reg2 = pointerReg)
}
return ExpressionCodeResult(result, vmDt, resultReg, resultFpReg)
}
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UInt> {
// returns instructions to calculate the pointer address, and the offset into the struct it points to
// so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
val result = mutableListOf<IRCodeChunkBase>()
if(targetPointerDeref.chain.isEmpty())
return result to 0u // nothing to do; there's no deref chain
var struct: StStruct? = null
if(targetPointerDeref.startpointer.type.subType!=null)
struct = targetPointerDeref.startpointer.type.subType as StStruct
if(targetPointerDeref.chain.isNotEmpty()) {
// traverse deref chain
for(deref in targetPointerDeref.chain.dropLast(1)) {
val fieldinfo = struct!!.getField(deref, codeGen.program.memsizer)
struct = fieldinfo.first.subType as StStruct
// get new pointer from field
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt())
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
}
}
}
val field = targetPointerDeref.chain.last()
val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
if(fieldinfo.second>0u) {
if(targetPointerDeref.derefLast) {
require(fieldinfo.first.isPointer)
// add the field offset
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
return result to 0u
} else {
return result to fieldinfo.second
}
}
if(targetPointerDeref.derefLast) {
require(fieldinfo.first.isPointer)
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
}
return result to 0u
}
}

View File

@@ -56,6 +56,8 @@ class IRCodeGen(
return irProg
}
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
// Normally, block level (global) variables that have a numeric initialization value
// are initialized via an assignment statement.
@@ -65,7 +67,7 @@ class IRCodeGen(
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
val block = variable.parent.astNode as PtBlock
val initialization = (block.children.firstOrNull {
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
} as PtAssignment?)
val initValue = initialization?.value
when(initValue){
@@ -104,6 +106,15 @@ class IRCodeGen(
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
is PtIdentifier -> {
if('.' !in node.name) {
// there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
val expr = node.parent as? PtBinaryExpression
if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || !expr.left.type.isPointer)
require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
}
}
else -> { /* node has no name or is ok to have no dots in the name */ }
}
node.children.forEach { verifyPtNode(it) }
@@ -259,6 +270,8 @@ class IRCodeGen(
is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
is PtStructDecl -> emptyList()
is PtSubSignature -> emptyList()
else -> TODO("missing codegen for $node")
}
@@ -477,7 +490,7 @@ class IRCodeGen(
translateForInNonConstantRange(forLoop, loopvar)
}
is PtIdentifier -> {
require(forLoop.variable.name == loopvar.scopedName)
require(forLoop.variable.name == loopvar.scopedNameString)
val elementDt = irType(iterable.type.elementType())
val iterableLength = symbolTable.getLength(iterable.name)
val loopvarSymbol = forLoop.variable.name
@@ -501,7 +514,7 @@ class IRCodeGen(
result += jumpChunk
result += IRCodeChunk(endLabel, null)
}
iterable.type.isSplitWordArray -> {
iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
// iterate over lsb/msb split word array
if(elementDt!=IRDataType.WORD)
throw AssemblyError("weird dt")
@@ -557,7 +570,7 @@ class IRCodeGen(
val step = iterable.step.number.toInt()
if (step==0)
throw AssemblyError("step 0")
require(forLoop.variable.name == loopvar.scopedName)
require(forLoop.variable.name == loopvar.scopedNameString)
val loopvarSymbol = forLoop.variable.name
val loopvarDt = when(loopvar) {
is StMemVar -> loopvar.dt
@@ -643,7 +656,7 @@ class IRCodeGen(
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
val loopLabel = createLabelName()
require(forLoop.variable.name == loopvar.scopedName)
require(forLoop.variable.name == loopvar.scopedNameString)
val loopvarSymbol = forLoop.variable.name
val loopvarDt = when(loopvar) {
is StMemVar -> loopvar.dt
@@ -1665,7 +1678,7 @@ class IRCodeGen(
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtTypeCast -> {
require(cond.type.isBool && cond.value.type.isNumeric)
require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
@@ -1848,7 +1861,7 @@ class IRCodeGen(
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
is PtAlign -> TODO("ir support for inline %align")
is PtSub -> {
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
for (subchild in child.children) {
translateNode(subchild).forEach { sub += it }
}
@@ -1897,21 +1910,23 @@ class IRCodeGen(
}
}
}
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
else -> TODO("weird block child node $child")
}
}
return irBlock
}
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
val result = mutableListOf<IRSubroutine.IRParam>()
parameters.forEach {
it as PtSubroutineParameter
if(it.register==null) {
val flattenedName = it.definingISub()!!.name + "." + it.name
if (symbolTable.lookup(flattenedName) == null)
TODO("fix missing lookup for: $flattenedName parameter")
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
result += IRSubroutine.IRParam(flattenedName, orig.dt)
require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
val orig = symbolTable.lookup(it.name) as? StStaticVariable
if (orig == null)
TODO("fix missing lookup for: ${it.name} parameter")
result += IRSubroutine.IRParam(it.name, orig.dt)
} else {
val reg = it.register
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
@@ -1936,7 +1951,7 @@ class IRCodeGen(
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
return IRCodeChunk(label, null).also {
val args = params.map { (dt, reg)->
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
@@ -1947,9 +1962,7 @@ class IRCodeGen(
}
}
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
val chunk = IRCodeChunk(null, null)
when(registerOrFlag.registerOrPair) {
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
@@ -1972,4 +1985,77 @@ class IRCodeGen(
}
return chunk
}
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UInt> {
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
val pointerTr = expressionEval.translateExpression(deref.startpointer)
result += pointerTr.chunks
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
result += instructions
return pointerTr.resultReg to offset
}
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UInt, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
if(offset==0u) {
val irdt = irType(type)
val instr = if(type.isFloat) {
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
} else {
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
}
addInstr(result, instr, null)
return
}
// store with field offset
var valueRegister = existingValueRegister
val irdt = irType(type)
if(valueIsZero && valueRegister<0) {
if(type.isFloat) {
valueRegister = registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
} else {
valueRegister = registers.next(irdt)
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
}
}
val instr = if (type.isFloat)
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
else
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
addInstr(result, instr, null)
}
internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also returned.
require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
val result = mutableListOf<IRCodeChunkBase>()
if(wordIndex) {
val tr = expressionEval.translateExpression(index)
addToResult(result, tr, tr.resultReg, -1)
var indexReg = tr.resultReg
if(tr.dt==IRDataType.BYTE) {
indexReg = registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
}
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
return Pair(result, indexReg)
}
// regular byte size index value.
val byteIndexTr = expressionEval.translateExpression(index)
addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
if(itemsize==1 || arrayIsSplitWords)
return Pair(result, byteIndexTr.resultReg)
result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
return Pair(result, byteIndexTr.resultReg)
}
}

View File

@@ -27,6 +27,9 @@ class IRUnusedCodeRemover(
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
val prefix = "$blockLabel."
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
// check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
blockVars.forEach { stVar ->
irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.forEach { ins ->
@@ -47,6 +50,19 @@ class IRUnusedCodeRemover(
}
}
val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
blockStructs.forEach { struct ->
irprog.st.allStructInstances().forEach { instance ->
if(instance.structName == struct.name)
return // a struct instance is declared using this struct type
}
irprog.st.allVariables().forEach { variable ->
if(variable.dt.isPointer || variable.dt.isStructInstance)
if(struct.name == variable.dt.subType!!.scopedNameString)
return // a variable exists with the struct as (pointer) type
}
}
irprog.st.removeTree(blockLabel)
removeBlockInits(irprog, blockLabel)
}

View File

@@ -14,6 +14,12 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
StNodeType.STRUCTINSTANCE -> {
val instance = it.value as StStructInstance
val struct = sourceSt.lookup(instance.structName) as StStruct
st.add(convert(instance, struct.fields))
}
StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
else -> { }
}
}
@@ -37,12 +43,17 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
}
private fun convert(variable: StStaticVariable): IRStStaticVariable {
private fun convert(struct: StStruct): IRStStructDef =
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
else
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
else
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
private fun convert(variable: StStaticVariable): IRStStaticVariable {
if('.' in variable.name) {
return IRStStaticVariable(variable.name,
@@ -62,14 +73,14 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
array.forEach {
if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
newArray.add(IRStArrayElement(null, null, target.scopedName))
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
} else {
newArray.add(convertArrayElt(it))
}
}
return newArray
}
val scopedName = variable.scopedName
val scopedName = variable.scopedNameString
return IRStStaticVariable(scopedName,
variable.dt,
variable.initializationNumericValue,
@@ -94,7 +105,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
)
} else {
val scopedName = try {
variable.scopedName
variable.scopedNameString
} catch (_: UninitializedPropertyAccessException) {
variable.name
}
@@ -109,7 +120,7 @@ private fun convert(constant: StConstant): IRStConstant {
constant.name
} else {
try {
constant.scopedName
constant.scopedNameString
} catch (_: UninitializedPropertyAccessException) {
constant.name
}
@@ -122,12 +133,17 @@ private fun convert(variable: StMemorySlab): IRStMemorySlab {
return if('.' in variable.name)
IRStMemorySlab(variable.name, variable.size, variable.align)
else
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.align)
}
/*
private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType, String>>): IRStStructInstance {
val values = fields.zip(instance.initialValues).map { (field, value) ->
val elt = convertArrayElt(value)
IRStructInitValue(field.first.base, elt)
}
return IRStStructInstance(instance.name, instance.structName, values, instance.size)
}
*/
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?

View File

@@ -3,7 +3,9 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray || dt.isSplitWordArray) {
if(dt.isPointerArray)
return 2 * numElements!!
else if(dt.isArray || dt.isSplitWordArray) {
require(numElements!=null)
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements

View File

@@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
// @( &thing ) --> thing (but only if thing is a byte type!)
val addrOf = memread.addressExpression as? AddressOf
if(addrOf!=null) {
if(addrOf.identifier.inferType(program).isBytes)
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
if(addrOf.identifier?.inferType(program)?.isBytes==true)
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
}
return noModifications
}
@@ -332,15 +332,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val constIndex = arrayIndexedExpression.indexer.constIndex()
if (constIndex != null) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
val value = array.value[constIndex].constValue(program)
if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
if(arrayIndexedExpression.plainarrayvar!=null) {
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
val value = array.value[constIndex].constValue(program)
if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
}
}
}
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("constant fold pointer[i]")
}
}
}
@@ -390,7 +394,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral
require(loopvar.datatype.sub == null)
require(loopvar.datatype.isBasic)
val loopvarSimpleDt = loopvar.datatype.base
when(loopvarSimpleDt) {
BaseDataType.UBYTE -> {

View File

@@ -323,7 +323,7 @@ internal class ConstantIdentifierReplacer(
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
return if(arrayIdx.parent is AssignTarget) {
val memwrite = DirectMemoryWrite(add, identifier.position)
val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
} else {
val memread = DirectMemoryRead(add, identifier.position)

View File

@@ -18,8 +18,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// try to statically convert a literal value into one of the desired type
val literal = typecast.expression as? NumericLiteral
if (literal != null) {
val newLiteral = literal.cast(typecast.type, typecast.implicit)
if (literal != null && typecast.type.isBasic) {
val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
}
@@ -33,7 +33,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
}
} else {
if (typecast.expression.inferType(program) issimpletype typecast.type) {
if (typecast.expression.inferType(program) istype typecast.type) {
// remove duplicate cast
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
}
@@ -423,6 +423,29 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(arrayIndexedExpression.indexer.constIndex()==0) {
if(arrayIndexedExpression.plainarrayvar!=null) {
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
if(dt.isPointer) {
// pointer[0] --> pointer^^
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
}
}
val ptrDeref = arrayIndexedExpression.pointerderef
if(ptrDeref!=null) {
val dt = ptrDeref.inferType(program).getOrUndef()
if(dt.isPointer) {
// ptr1.ptr2[0] --> ptr1.ptr2^^
val deref = PtrDereference(ptrDeref.chain, true, ptrDeref.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
}
}
}
return noModifications
}
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
// NOTE: only when the terms are not function calls!!!
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
@@ -499,7 +522,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
else if (valueDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
} else {
@@ -516,7 +539,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
else if (argDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
@@ -555,7 +578,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
// just cast the lsb to uword
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
@@ -711,9 +734,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// just use: msb(value) as type
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt.isSignedWord)
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
TypecastExpression(msb, DataType.BYTE, true, expr.position)
else
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
TypecastExpression(msb, DataType.UWORD, true, expr.position)
}
else -> return null
}
@@ -839,14 +862,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
}
}
else -> {
@@ -887,12 +910,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount==8) {
// shift right by 8 bits is just a byte operation: msb(X) as uword
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
}
}
BaseDataType.WORD -> {
@@ -903,12 +926,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount == 8) {
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
}
else if(amount > 8) {
// same as above but with residual shifts. Take care to do signed shift.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
}
}

View File

@@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
private fun makeFullyScoped(identifier: IdentifierReference) {
identifier.targetStatement(program)?.let { target ->
identifier.targetStatement()?.let { target ->
val scoped = (target as INamedStatement).scopedName
val scopedIdent = IdentifierReference(scoped, identifier.position)
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
@@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
when (it) {
is NumericLiteral -> it.copy()
is IdentifierReference -> {
val target = it.targetStatement(program) ?: return emptyList()
val target = it.targetStatement() ?: return emptyList()
val scoped = (target as INamedStatement).scopedName
IdentifierReference(scoped, it.position)
}
@@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
return if(sub==null || !canInline(sub, functionCallStatement))
noModifications
else
@@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
"invalid inline sub at ${sub.position}"
@@ -249,7 +249,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
val stmt = sub.statements.single()
if (stmt is IFunctionCall) {
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
return existing !is VarDecl
return existing !is VarDecl && existing !is StructFieldRef
}
}
return true

View File

@@ -29,7 +29,14 @@ class StatementOptimizer(private val program: Program,
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
val arg = functionCallStatement.args.single()
val stringVar: IdentifierReference? = if(arg is AddressOf) {
if(arg.arrayIndex==null) arg.identifier else null
if(arg.arrayIndex==null) {
if(arg.identifier!=null)
arg.identifier
else
null // struct can't have string fields so nothing to look at here
} else {
null
}
} else {
arg as? IdentifierReference
}
@@ -146,7 +153,7 @@ class StatementOptimizer(private val program: Program,
// for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block
val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
@@ -161,7 +168,7 @@ class StatementOptimizer(private val program: Program,
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
@@ -174,7 +181,7 @@ class StatementOptimizer(private val program: Program,
if(av!=null) {
val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
AssignTarget(forLoop.loopVar, null, null, null, false, position = forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))

View File

@@ -181,14 +181,20 @@ class UnusedCodeRemover(private val program: Program,
val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
if(declIndex==singleUseIndex-1) {
if("ignore_unused" !in decl.definingBlock.options())
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
val fcall = assignment.value as IFunctionCall
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
return listOf(
IAstModification.ReplaceNode(decl, voidCall, parent),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
)
val callStruct = (assignment.value as IFunctionCall).target.targetStructDecl()
if(callStruct!=null) {
// don't turn a struct instance allocation call to a void call, instead, remove everything
return listOf(IAstModification.Remove(assignment, assignment.parent as IStatementContainer))
} else {
if("ignore_unused" !in decl.definingBlock.options())
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
val fcall = assignment.value as IFunctionCall
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
return listOf(
IAstModification.ReplaceNode(decl, voidCall, parent),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
)
}
}
}
} else {

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("application")
kotlin("jvm")
@@ -18,7 +16,6 @@ dependencies {
implementation(project(":codeGenExperimental"))
implementation(project(":virtualmachine"))
// implementation(project(":beanshell"))
implementation("org.antlr:antlr4-runtime:4.13.2")
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")

View File

@@ -25,6 +25,5 @@
<orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="antlr.antlr4" level="project" />
</component>
</module>

View File

@@ -440,6 +440,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -445,6 +445,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -1560,6 +1560,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -119,6 +119,7 @@ verafx {
; Returns the 16 bits unsigned result of R0*R1 in AY.
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
; Verafx doesn't support unsigned values like this for full 32 bit result.
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
%asm {{
lda cx16.r0
sta P8ZP_SCRATCH_W1
@@ -136,6 +137,7 @@ verafx {
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
; Vera Fx multiplication support only works on signed values!
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
%asm {{
lda #(2 << 1)
sta cx16.VERA_CTRL ; $9F25

View File

@@ -104,6 +104,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -13,6 +13,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -94,16 +94,16 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
// 1 arg, type = anything, result type = ubyte or uword
if(args.size!=1)
throw SyntaxError("sizeof requires one argument", position)
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral)
throw SyntaxError("sizeof argument should be an identifier or number", position)
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral && args[0] !is AddressOf)
throw CannotEvaluateException("sizeof","argument should be an identifier, number, or type name")
val dt = args[0].inferType(program)
if(dt.isKnown) {
if(args[0] is NumericLiteral)
if(args[0] is NumericLiteral || args[0] is AddressOf)
return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position)
val target = (args[0] as IdentifierReference).targetStatement(program)
?: throw CannotEvaluateException("sizeof", "no target")
val target = (args[0] as? IdentifierReference)?.targetStatement()
?: throw SyntaxError("wrong argument type", position)
return when {
dt.isArray -> {
@@ -112,7 +112,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position)
}
dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position)
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
}
} else {
val identifier = args[0] as? IdentifierReference
@@ -128,7 +128,14 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
}
}
throw SyntaxError("sizeof invalid argument type", position)
// the argument could refer to a struct declaration
val struct = (args[0] as? IdentifierReference)?.targetStructDecl()
if(struct!=null) {
val size = struct.memsize(program.memsizer)
return NumericLiteral(BaseDataType.UBYTE, size.toDouble(), position)
}
throw SyntaxError("sizeof argument should be an identifier, number, or type name", position)
}
}

View File

@@ -181,8 +181,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
val symbolTable = stMaker.make()
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors)
args.errors.report()
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
args.errors.report()
if (compilationOptions.optimize) {
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
@@ -299,7 +299,7 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO
if(loadAddress>options.memtopAddress) {
errors.warn("program load address ${loadAddress.toHex()} is beyond default memtop address ${options.memtopAddress.toHex()}. " +
"Memtop has been adjusted to ${'$'}ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position)
$$"Memtop has been adjusted to $ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position)
options.memtopAddress = 0xffffu
}
}
@@ -456,7 +456,6 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
program.preprocessAst(errors, compilerOptions)
if(errors.noErrors() && compilerOptions.dumpSymbols) {
printSymbols(program)
exitProcess(0)

View File

@@ -64,6 +64,12 @@ internal class AstChecker(private val program: Program,
}
override fun visit(identifier: IdentifierReference) {
val parentExpr = identifier.parent as? BinaryExpression
if(parentExpr?.operator==".") {
return // identifiers will be checked over at the BinaryExpression itself
}
if(identifier.nameInSource.any { it.startsWith('_') }) {
errors.err("identifiers cannot start with an underscore", identifier.position)
}
@@ -76,10 +82,16 @@ internal class AstChecker(private val program: Program,
}
}
checkLongType(identifier)
val stmt = identifier.targetStatement(program)
if(stmt==null)
val stmt = identifier.targetStatement(program.builtinFunctions)
if(stmt==null) {
if(identifier.parent is ArrayIndexedExpression) {
// might be a pointer dereference chain
val ppExpr = identifier.parent.parent as? BinaryExpression
if(ppExpr?.operator==".")
return // identifiers will be checked over at the BinaryExpression itself
}
errors.undefined(identifier.nameInSource, identifier.position)
}
else {
val target = stmt as? VarDecl
if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) {
@@ -92,7 +104,7 @@ internal class AstChecker(private val program: Program,
if(identifier.nameInSource.size>1) {
val lookupModule = identifier.definingScope.lookup(identifier.nameInSource.take(1))
if(lookupModule is VarDecl) {
if(lookupModule is VarDecl && !lookupModule.datatype.isPointer) {
errors.err("ambiguous symbol name, block name expected but found variable", identifier.position)
}
}
@@ -131,12 +143,14 @@ internal class AstChecker(private val program: Program,
if(valueDt.isBool && expectedDt.isUnsignedByte) {
// if the return value is a bool and the return type is ubyte, allow this. But give a warning.
errors.info("return type of the subroutine should probably be bool instead of ubyte", actual.position)
} else if(valueDt.isIterable && expectedDt.isUnsignedWord) {
// you can return a string or array when an uword (pointer) is returned
} else if(expectedDt.isUnsignedWord && (valueDt.isIterable || valueDt.isPointer)) {
// you can return a string or array or pointer when an uword (pointer) is returned
} else if(valueDt issimpletype BaseDataType.UWORD && expectedDt.isString) {
// you can return an uword pointer when the return type is a string
} else if(valueDt.isUnsignedWord && expectedDt.isPointer) {
// you can return an uword value when a pointer is required
} else {
errors.err("return value's type $valueDt doesn't match subroutine's return type $expectedDt", actual.position)
errors.err("return value type $valueDt doesn't match subroutine return type $expectedDt", actual.position)
}
}
}
@@ -193,43 +207,64 @@ internal class AstChecker(private val program: Program,
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position)
} else {
require(loopvar.datatype.isNumericOrBool)
when (loopvar.datatype.base) {
BaseDataType.UBYTE -> {
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedByteArray && !iterableDt.isString)
if (!iterableDt.isUnsignedByte && !iterableDt.isUnsignedByteArray && !iterableDt.isString)
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
}
BaseDataType.BOOL -> {
if(!iterableDt.isBoolArray)
if (!iterableDt.isBoolArray)
errors.err("bool loop variable can only loop over boolean array", forLoop.position)
}
BaseDataType.UWORD -> {
if(!iterableDt.isUnsignedByte && !iterableDt.isUnsignedWord && !iterableDt.isString &&
!iterableDt.isUnsignedByteArray && !iterableDt.isUnsignedWordArray &&
!iterableDt.isSplitWordArray)
if (!iterableDt.isUnsignedByte && !iterableDt.isUnsignedWord && !iterableDt.isString &&
!iterableDt.isUnsignedByteArray && !iterableDt.isUnsignedWordArray &&
!iterableDt.isSplitWordArray
)
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
}
BaseDataType.BYTE -> {
if(!iterableDt.isSignedByte && !iterableDt.isSignedByteArray)
if (!iterableDt.isSignedByte && !iterableDt.isSignedByteArray)
errors.err("byte loop variable can only loop over bytes", forLoop.position)
}
BaseDataType.WORD -> {
if(!iterableDt.isSignedByte && !iterableDt.isSignedWord &&
!iterableDt.isSignedByteArray && !iterableDt.isUnsignedByteArray &&
!iterableDt.isSignedWordArray && !iterableDt.isUnsignedWordArray)
if (!iterableDt.isSignedByte && !iterableDt.isSignedWord &&
!iterableDt.isSignedByteArray && !iterableDt.isUnsignedByteArray &&
!iterableDt.isSignedWordArray && !iterableDt.isUnsignedWordArray
)
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
}
BaseDataType.FLOAT -> {
// Looping over float variables is very inefficient because the loopvar is going to
// get copied over with new values all the time. We don't support this for now.
// Loop with an integer index variable if you really need to... or write different code.
errors.err("for loop only supports integers", forLoop.position)
}
else -> errors.err("loop variable must be numeric type", forLoop.position)
BaseDataType.POINTER -> {
if (!iterableDt.isUnsignedWord) {
if (iterableDt.isPointerArray) {
val elementDt = iterableDt.elementType()
if(loopvar.datatype != elementDt)
errors.err("loopvar type differs from the pointer types in the collection", forLoop.position)
} else
errors.err("pointer loop variable can only loop over pointers or unsigned words", forLoop.position)
}
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
}
else -> errors.err("loop variable must be numeric or pointer type", forLoop.position)
}
if(errors.noErrors()) {
// check loop range values
val range = forLoop.iterable as? RangeExpression
@@ -282,7 +317,7 @@ internal class AstChecker(private val program: Program,
val addr = block.address
if (addr!=null) {
if (addr > 65535u)
errors.err("block address must be valid integer 0..\$ffff", block.position)
errors.err($$"block address must be valid integer 0..$ffff", block.position)
if(compilerOptions.loadAddress!=0u) {
val gapsize = compilerOptions.compTarget.STARTUP_CODE_RESERVED_SIZE
if (addr < compilerOptions.loadAddress + gapsize)
@@ -297,10 +332,11 @@ internal class AstChecker(private val program: Program,
is Directive,
is Label,
is VarDecl,
is StructDecl,
is InlineAssembly,
is IStatementContainer -> true
is Assignment -> {
val target = statement.target.identifier!!.targetStatement(program)
val target = statement.target.identifier!!.targetStatement()
target === statement.previousSibling() // an initializer assignment is okay
}
else -> false
@@ -325,10 +361,6 @@ internal class AstChecker(private val program: Program,
super.visit(label)
}
override fun visit(numLiteral: NumericLiteral) {
checkLongType(numLiteral)
}
private fun hasReturnOrExternalJumpOrRts(scope: IStatementContainer): Boolean {
class Searcher: IAstVisitor
{
@@ -338,7 +370,7 @@ internal class AstChecker(private val program: Program,
count++
}
override fun visit(jump: Jump) {
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement(program)
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement()
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
@@ -559,8 +591,23 @@ internal class AstChecker(private val program: Program,
errors.err("identifiers cannot start with an underscore", p.position)
if(p.type.isPassByRef && !p.type.isString && !p.type.isUnsignedByteArray) {
errors.err("this pass-by-reference type can't be used as a parameter type. Instead, use just 'uword' to receive the address, or maybe don't pass the value via a parameter but access it directly.", p.position)
errors.err("this pass-by-reference type can't be used as a parameter type.", p.position)
}
if (p.type.isPointer) {
if (p.type.subType == null && p.type.subTypeFromAntlr!=null) errors.err("cannot find struct type ${p.type.subTypeFromAntlr?.joinToString(".")}", p.position)
}
if(p.type.isStructInstance)
errors.err("structs can only be passed via a pointer", p.position)
}
for((index, r) in subroutine.returntypes.withIndex()) {
if(r.isPointer && r.subType==null && r.subTypeFromAntlr!=null)
err("return type #${index+1}: cannot find struct type ${r.subTypeFromAntlr?.joinToString(".")}")
if(r.isStructInstance)
err("structs can only be returned via a pointer")
}
}
@@ -589,9 +636,9 @@ internal class AstChecker(private val program: Program,
val ident = repeatLoop.iterations as? IdentifierReference
if(ident!=null) {
val targetVar = ident.targetVarDecl()
if(targetVar==null)
errors.err("invalid assignment value, maybe forgot '&' (address-of)", ident.position)
val target = ident.targetStatement()
if(target !is VarDecl && target !is StructFieldRef)
errors.err("invalid assignment value", ident.position)
}
super.visit(repeatLoop)
}
@@ -603,7 +650,7 @@ internal class AstChecker(private val program: Program,
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt) && !targetDt.isIterable) {
if(!(valueDt issimpletype BaseDataType.STR && targetDt issimpletype BaseDataType.UWORD)) {
if(targetDt.isUnknown) {
if(assignment.target.identifier?.targetStatement(program)!=null)
if(assignment.target.identifier?.targetStatement(program.builtinFunctions)!=null)
errors.err("target datatype is unknown", assignment.target.position)
// otherwise, another error about missing symbol is already reported.
}
@@ -638,6 +685,20 @@ internal class AstChecker(private val program: Program,
}
}
if(fcall?.target?.targetStructDecl()!=null)
// Struct(...) initializer
return super.visit(assignment)
// unfortunately the AST regarding pointer dereferencing is a bit of a mess, and we cannot do precise type checking on elements inside such expressions yet.
if(assignment.value.inferType(program).isUnknown) {
val binexpr = assignment.value as? BinaryExpression
if(binexpr?.operator == ".") {
errors.err("invalid pointer dereference (can't determine type)", assignment.value.position)
}
else if(assignment.value !is PtrDereference && assignment.target.multi==null)
errors.err("invalid assignment value", assignment.value.position)
}
super.visit(assignment)
}
@@ -691,6 +752,9 @@ internal class AstChecker(private val program: Program,
errors.undefined(targetIdentifier.nameInSource, targetIdentifier.position)
return
}
is StructFieldRef -> {
// all is well
}
!is VarDecl -> {
errors.err("assignment LHS must be register or variable", assignment.position)
return
@@ -708,10 +772,7 @@ internal class AstChecker(private val program: Program,
val targetDatatype = assignTarget.inferType(program)
if (targetDatatype.isKnown) {
val sourceDatatype = assignment.value.inferType(program)
if (sourceDatatype.isUnknown) {
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck && assignment.value !is IfExpression)
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
} else {
if (!sourceDatatype.isUnknown) {
checkAssignmentCompatible(targetDatatype.getOrUndef(),sourceDatatype.getOrUndef(), assignment.value, assignment.value.position)
}
}
@@ -721,10 +782,13 @@ internal class AstChecker(private val program: Program,
fun checkRomTarget(target: AssignTarget) {
val idx=target.arrayindexed
if(idx!=null) {
val decl = idx.arrayvar.targetVarDecl()!!
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
// cannot check pointer deref for rom target, assume no there.
if(idx.plainarrayvar!=null) {
val decl = idx.plainarrayvar!!.targetVarDecl()!!
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
}
}
}
}
@@ -738,8 +802,7 @@ internal class AstChecker(private val program: Program,
}
override fun visit(addressOf: AddressOf) {
checkLongType(addressOf)
val variable=addressOf.identifier.targetVarDecl()
val variable=addressOf.identifier?.targetVarDecl()
if (variable!=null) {
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
errors.err("invalid pointer-of operand type", addressOf.position)
@@ -771,8 +834,6 @@ internal class AstChecker(private val program: Program,
if(decl.names.size>1)
throw InternalCompilerException("vardecls with multiple names should have been converted into individual vardecls")
if(decl.datatype.isLong && decl.type!=VarDeclType.CONST)
errors.err("cannot use long type for variables; only for constants", decl.position)
if(decl.type==VarDeclType.MEMORY) {
if (decl.datatype.isString)
errors.err("strings cannot be memory-mapped", decl.position)
@@ -862,7 +923,7 @@ internal class AstChecker(private val program: Program,
val numvalue = decl.value as? NumericLiteral
if(numvalue!=null) {
if (!numvalue.type.isInteger || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
valueerr("memory address must be valid integer 0..\$ffff")
valueerr($$"memory address must be valid integer 0..$ffff")
}
} else {
valueerr("value of memory mapped variable can only be a constant, maybe use an address pointer type instead?")
@@ -874,13 +935,24 @@ internal class AstChecker(private val program: Program,
if(declValue!=null && decl.type==VarDeclType.VAR) {
val iDt = declValue.inferType(program)
if (!(iDt istype decl.datatype)) {
if(decl.isArray) {
if(decl.datatype.isPointerArray) {
if(!iDt.getOrUndef().isWordArray)
valueerr("initialization value for pointer array must be a word array")
}
else if(decl.isArray) {
val eltDt = decl.datatype.elementType()
if(!(iDt istype eltDt))
valueerr("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
if(!(iDt istype eltDt) && iDt.isKnown)
valueerr("initialization value has incompatible type ($iDt) for the variable (${decl.datatype})")
} else if(!decl.datatype.isString) {
if(!(iDt.isBool && decl.datatype.isUnsignedByte || iDt issimpletype BaseDataType.UBYTE && decl.datatype.isBool))
valueerr("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
if(!(iDt.isBool && decl.datatype.isUnsignedByte || iDt issimpletype BaseDataType.UBYTE && decl.datatype.isBool)) {
// pointer variables can be initialized with a compatible pointer or with a uword
if(decl.datatype.isPointer) {
if (!iDt.isAssignableTo(decl.datatype))
valueerr("initialization value has incompatible type ($iDt) for the variable (${decl.datatype})")
}
else
valueerr("initialization value has incompatible type ($iDt) for the variable (${decl.datatype})")
}
}
}
}
@@ -956,8 +1028,8 @@ internal class AstChecker(private val program: Program,
}
if(decl.datatype.isSplitWordArray) {
if (!decl.datatype.isWordArray) {
errors.err("split can only be used on word arrays", decl.position)
if (!decl.datatype.isWordArray && !decl.datatype.isPointerArray) {
errors.err("split can only be used on word and pointer arrays", decl.position)
}
}
@@ -975,6 +1047,14 @@ internal class AstChecker(private val program: Program,
}
}
if(decl.datatype.isPointerArray) {
if(decl.splitwordarray!= SplitWish.SPLIT)
errors.err("pointer arrays can only be @split", decl.position)
}
if(decl.datatype.isStructInstance) {
errors.err("struct instances cannot be declared directly, use pointer and allocation call instead", decl.position)
}
if (decl.dirty) {
if(decl.datatype.isString)
@@ -1147,11 +1227,13 @@ internal class AstChecker(private val program: Program,
}
val arrayspec = ArrayIndex.forArray(array)
checkValueTypeAndRangeArray(array.type.getOrUndef(), arrayspec, array)
} else {
errors.err("undefined array type (multiple element types?)", array.position)
}
if(array.parent is VarDecl) {
if (!array.value.all { it is NumericLiteral || it is AddressOf })
errors.err("array literal for variable initialization contains non-constant elements", array.position)
errors.err("initialization value contains non-constant elements", array.value[0].position)
} else if(array.parent is ForLoop) {
if (!array.value.all { it.constValue(program) != null })
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
@@ -1193,9 +1275,14 @@ internal class AstChecker(private val program: Program,
}
}
checkLongType(expr)
val dt = expr.expression.inferType(program).getOrUndef()
if(!dt.isUndefined) {
if(dt.isPointerArray || dt.isPointer) {
errors.err("pointers don't support prefix operators", expr.position)
return
}
when (expr.operator) {
"-" -> {
if (!(dt.isSigned && dt.isNumeric)) {
@@ -1227,7 +1314,7 @@ internal class AstChecker(private val program: Program,
count++
}
override fun visit(jump: Jump) {
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement(program)
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement()
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
@@ -1250,7 +1337,77 @@ internal class AstChecker(private val program: Program,
override fun visit(expr: BinaryExpression) {
super.visit(expr)
checkLongType(expr)
if(expr.operator==".") {
val leftIdentfier = expr.left as? IdentifierReference
val leftIndexer = expr.left as? ArrayIndexedExpression
val rightIdentifier = expr.right as? IdentifierReference
val rightIndexer = expr.right as? ArrayIndexedExpression
if(rightIdentifier!=null) {
val struct: StructDecl? =
if (leftIdentfier != null) {
// PTR.FIELD
leftIdentfier.targetVarDecl()?.datatype?.subType as? StructDecl
} else if(leftIndexer!=null) {
// ARRAY[x].NAME --> maybe it's a pointer dereference
val indexerType = leftIndexer.inferType(program).getOrUndef()
if(indexerType.isPointer)
indexerType.subType as? StructDecl
else
null
}
else null
if (struct != null) {
val fieldDt = if(rightIdentifier.nameInSource.size==1)
struct.getFieldType(rightIdentifier.nameInSource.single())
else
rightIdentifier.traverseDerefChainForDt(struct)
if (fieldDt == null)
errors.err("no such field '${rightIdentifier.nameInSource.single()}' in struct '${struct.name}'", rightIdentifier.position)
} else {
val leftDt = expr.left.inferType(program)
if(leftDt.isPointer) {
val struct = (leftDt.getOrUndef().subType as? StructDecl)
if(struct!=null) {
if (rightIdentifier.nameInSource.size == 1) {
val fieldDt = struct.getFieldType(rightIdentifier.nameInSource.single())
if (fieldDt == null)
errors.err(
"no such field '${rightIdentifier.nameInSource.single()}' in struct '${struct.name}'",
rightIdentifier.position
)
}
}
} else
errors.err("cannot find struct type", expr.left.position)
}
} else if(rightIndexer!=null) {
val leftDt = expr.left.inferType(program)
if(leftDt.isStructInstance) {
TODO("pointer[x].field[y] ??")
// // pointer[x].field[y] --> type is the dt of 'field'
// var struct = leftDt.getOrUndef().subType as? StructDecl
// if (struct==null) {
// errors.err("cannot find struct type", expr.position)
// } else {
// var fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
// if (fieldDt == null)
// errors.err("no such field '${rightIndexer.arrayvar.nameInSource.single()}' in struct '${(leftDt.getOrUndef().subType as? StructDecl)?.name}'", expr.position)
// else {
// struct = fieldDt.subType as StructDecl
// fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
// if(fieldDt==null)
// errors.err("no such field '${rightIndexer.arrayvar.nameInSource.single()}' in struct '${struct.name}'", expr.position)
// }
// }
} else {
errors.err("at the moment it is not possible to chain array syntax on pointers like ...p1[x].p2[y]... use separate expressions for the time being", expr.right.position) // TODO add support for chained array syntax on pointers (rewrite ast?)
// TODO I don't think we can evaluate this because it could end up in as a struct instance, which we don't support yet... rewrite or just give an error?
}
} else
throw FatalAstException("expected identifier or arrayindexer after dereference operator at ${expr.position})")
return
}
val leftIDt = expr.left.inferType(program)
val rightIDt = expr.right.inferType(program)
@@ -1267,6 +1424,13 @@ internal class AstChecker(private val program: Program,
val leftDt = leftIDt.getOrUndef()
val rightDt = rightIDt.getOrUndef()
// gate off nonsensical pointer arithmetic
if (expr.operator !in arrayOf("+", "-") + ComparisonOperators) {
if (leftDt.isPointer || leftDt.isPointerArray || rightDt.isPointer || rightDt.isPointerArray) {
errors.err("pointer arithmetic only supported for + and - operators, maybe cast to uword?", expr.right.position)
}
}
if(expr.operator=="+" || expr.operator=="-") {
if(leftDt.isString || rightDt.isString || leftDt.isArray || rightDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.position)
@@ -1296,12 +1460,22 @@ internal class AstChecker(private val program: Program,
}
}
if(!leftDt.isNumeric && !leftDt.isString && !leftDt.isBool)
errors.err("left operand is not numeric or str", expr.left.position)
if(!rightDt.isNumeric && !rightDt.isString && !rightDt.isBool)
errors.err("right operand is not numeric or str", expr.right.position)
if(!leftDt.isNumeric && !leftDt.isString && !leftDt.isBool && !leftDt.isPointer)
errors.err("invalid left operand type", expr.left.position)
if(!rightDt.isNumeric && !rightDt.isString && !rightDt.isBool && !rightDt.isPointer)
errors.err("invalid right operand type", expr.right.position)
if(leftDt!=rightDt) {
if(leftDt.isString && rightDt.isInteger && expr.operator=="*") {
if(leftDt.isPointer) {
if(!rightDt.isUnsignedWord) {
errors.err("pointer arithmetic requires unsigned word operand", expr.right.position)
}
}
else if(rightDt.isPointer) {
if(!leftDt.isUnsignedWord) {
errors.err("pointer arithmetic requires unsigned word operand", expr.left.position)
}
}
else if(leftDt.isString && rightDt.isInteger && expr.operator=="*") {
// exception allowed: str * constvalue
if(expr.right.constValue(program)==null)
errors.err("can only use string repeat with a constant number value", expr.left.position)
@@ -1362,18 +1536,23 @@ internal class AstChecker(private val program: Program,
}
override fun visit(typecast: TypecastExpression) {
checkLongType(typecast)
if(typecast.type.isIterable)
if(typecast.type.isPassByRef)
errors.err("cannot type cast to string or array type", typecast.position)
if(!typecast.expression.inferType(program).isKnown)
errors.err("this expression doesn't return a value", typecast.expression.position)
if(typecast.expression is NumericLiteral) {
val castResult = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
if(castResult.isValid)
throw FatalAstException("cast should have been performed in const eval already")
errors.err(castResult.whyFailed!!, typecast.expression.position)
if(typecast.type.isBasic) {
val castResult = (typecast.expression as NumericLiteral).cast(typecast.type.base, typecast.implicit)
if (castResult.isValid)
throw FatalAstException("cast should have been performed in const eval already")
errors.err(castResult.whyFailed!!, typecast.expression.position)
} else if (typecast.type.isPointer) {
if(!(typecast.expression.inferType(program).isUnsignedWord))
errors.err("can only cast uword to pointer", typecast.position)
} else
errors.err("invalid type cast", typecast.position)
}
super.visit(typecast)
@@ -1413,7 +1592,6 @@ internal class AstChecker(private val program: Program,
}
override fun visit(functionCallExpr: FunctionCallExpression) {
checkLongType(functionCallExpr)
// this function call is (part of) an expression, which should be in a statement somewhere.
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
@@ -1448,6 +1626,10 @@ internal class AstChecker(private val program: Program,
errors.err("function doesn't return a value", functionCallExpr.position)
}
}
else if(targetStatement is StructDecl) {
if(functionCallExpr.parent is IStatementContainer)
errors.err("static struct instance allocation can only occur as an initializer for a pointer variable", functionCallExpr.position)
}
if(builtinFunctionName in listOf("peek", "peekw")) {
val pointervar = functionCallExpr.args[0] as? IdentifierReference
@@ -1477,6 +1659,10 @@ internal class AstChecker(private val program: Program,
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
if(targetStatement is StructDecl) {
errors.err("static struct instance allocation can only occur as an initializer for a pointer variable", functionCallStatement.position)
}
if(functionCallStatement.void) {
when(targetStatement) {
is BuiltinFunctionPlaceholder -> {
@@ -1558,7 +1744,7 @@ internal class AstChecker(private val program: Program,
if(args[0] is AddressOf)
errors.err("can't call this indirectly, just use normal function call syntax", args[0].position)
else if(args[0] is IdentifierReference) {
val callTarget = (args[0] as IdentifierReference).targetStatement(program)
val callTarget = (args[0] as IdentifierReference).targetStatement(program.builtinFunctions)
if(callTarget !is VarDecl)
errors.err("can't call this indirectly, just use normal function call syntax", args[0].position)
}
@@ -1629,8 +1815,37 @@ internal class AstChecker(private val program: Program,
}
}
if(target is StructDecl) {
// it's a static struct inializer, check the values
if(args.isNotEmpty()) {
args.forEach {
if(it is IdentifierReference) {
val target = it.targetVarDecl()
if(target!=null && target.datatype.isPointer) {
errors.err("a pointer variable cannot be used in a static initialization value because its value is only known at runtime (use 0 here, and assign it later manually)", it.position)
}
}
}
if (!args.all { it is NumericLiteral || it is AddressOf || (it is TypecastExpression && it.expression is NumericLiteral)})
errors.err("initialization value contains non-constant elements", args[0].position)
if (target.fields.size != args.size)
errors.err("initialization value needs to have same number of values as the struct has fields, or be empty: expected ${target.fields.size} or 0, got ${args.size}", args[0].position)
else
target.fields.zip(args).withIndex().forEach { (index, fv) ->
val (field, value) = fv
val valueDt = value.inferType(program)
if(valueDt isNotAssignableTo field.first) {
errors.err("value #${index+1} has incompatible type $valueDt for field '${field.second}' (${field.first})", value.position)
}
}
}
// TODO rest?
}
args.forEach{
checkLongType(it)
if(it.inferType(program).isStructInstance)
errors.err("structs can only be passed via a pointer", it.position)
}
}
@@ -1641,40 +1856,70 @@ internal class AstChecker(private val program: Program,
}
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
checkLongType(arrayIndexedExpression)
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
val target = arrayIndexedExpression.plainarrayvar?.targetStatement(program.builtinFunctions)
if(target is VarDecl) {
if(!target.datatype.isIterable && !target.datatype.isUnsignedWord)
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
if (!target.datatype.isIterable && !target.datatype.isUnsignedWord && !target.datatype.isPointer)
errors.err(
"indexing requires an iterable, address uword, or pointer variable",
arrayIndexedExpression.position
)
val indexVariable = arrayIndexedExpression.indexer.indexExpr as? IdentifierReference
if(indexVariable!=null) {
if(indexVariable.targetVarDecl()?.datatype?.isSigned==true) {
errors.err("variable array indexing can't be performed with signed variables", indexVariable.position)
if (indexVariable != null) {
if (indexVariable.targetVarDecl()?.datatype?.isSigned == true) {
errors.err(
"variable array indexing can't be performed with signed variables",
indexVariable.position
)
return
}
}
val arraysize = target.arraysize?.constIndex()
val index = arrayIndexedExpression.indexer.constIndex()
if(arraysize!=null) {
if(index!=null && (index<0 || index>=arraysize))
if (arraysize != null) {
if (index != null && (index < 0 || index >= arraysize))
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
} else if(target.datatype.isString) {
if(target.value is StringLiteral) {
} else if (target.datatype.isString) {
if (target.value is StringLiteral) {
// check string lengths for non-memory mapped strings
val stringLen = (target.value as StringLiteral).value.length
if (index != null && (index < 0 || index >= stringLen))
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else if(index!=null && index<0) {
} else if (index != null && index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
} else if(target!=null) {
throw FatalAstException("target is not a variable")
}
// check index value 0..255
if(arrayIndexedExpression.pointerderef!=null) {
val dt = arrayIndexedExpression.pointerderef!!.inferType(program)
if(!dt.isPointer && !dt.isUnsignedWord && !dt.isIterable) {
errors.err("cannot array index on this field type", arrayIndexedExpression.indexer.position)
}
// else if(target is StructFieldRef) {
// if(!target.type.isPointer && !target.type.isUnsignedWord)
// errors.err("cannot array index on this field type", arrayIndexedExpression.indexer.position)
// } else {
// val parentExpr = arrayIndexedExpression.parent
// if(parentExpr is BinaryExpression) {
// if (parentExpr.operator != ".")
// errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
// } else if(parentExpr is PtrIndexedDereference) {
// // all is fine
// } else {
// errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
// }
// }
}
// check index value 0..255 if the index variable is not a pointer
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum.isKnown && !(dtxNum issimpletype BaseDataType.UBYTE) && !(dtxNum issimpletype BaseDataType.BYTE))
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
if(dtxNum.isKnown) {
val arrayVarDt = arrayIndexedExpression.plainarrayvar?.inferType(program)
if (arrayVarDt!=null && !arrayVarDt.isPointer && !(dtxNum issimpletype BaseDataType.UBYTE) && !(dtxNum issimpletype BaseDataType.BYTE))
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
}
super.visit(arrayIndexedExpression)
}
@@ -1762,15 +2007,8 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memread: DirectMemoryRead) {
if(!(memread.addressExpression.inferType(program) issimpletype BaseDataType.UWORD)) {
errors.err("address for memory access isn't uword", memread.position)
}
val tc = memread.addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
if(!(tc.expression.inferType(program) issimpletype BaseDataType.UWORD)) {
errors.err("address for memory access isn't uword", memread.position)
}
}
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
val pointervar = memread.addressExpression as? IdentifierReference
if(pointervar!=null)
@@ -1782,16 +2020,22 @@ internal class AstChecker(private val program: Program,
super.visit(memread)
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!(memwrite.addressExpression.inferType(program) issimpletype BaseDataType.UWORD)) {
errors.err("address for memory access isn't uword", memwrite.position)
}
val tc = memwrite.addressExpression as? TypecastExpression
private fun allowedMemoryAccessAddressExpression(addressExpression: Expression, program: Program): Boolean {
val dt = addressExpression.inferType(program)
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub?.isByteOrBool==true))
return true
val tc = addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
if(!(tc.expression.inferType(program) issimpletype BaseDataType.UWORD)) {
errors.err("address for memory access isn't uword", memwrite.position)
}
val dt = tc.expression.inferType(program)
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub?.isByteOrBool==true))
return true
}
return false
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
val pointervar = memwrite.addressExpression as? IdentifierReference
if(pointervar!=null)
@@ -1808,17 +2052,31 @@ internal class AstChecker(private val program: Program,
errors.err("%asm containing IR code cannot be translated to 6502 assembly", inlineAssembly.position)
}
private fun checkLongType(expression: Expression) {
if(expression.inferType(program) issimpletype BaseDataType.LONG) {
if((expression.parent as? VarDecl)?.type!=VarDeclType.CONST) {
if (expression.parent !is RepeatLoop) {
if (errors.noErrorForLine(expression.position))
errors.err("integer overflow", expression.position)
}
}
override fun visit(struct: StructDecl) {
val uniqueFields = struct.fields.map { it.second }.toSet()
if(uniqueFields.size!=struct.fields.size)
errors.err("duplicate field names in struct", struct.position)
val memsize = struct.memsize(program.memsizer)
if(memsize>256)
errors.err("struct contains too many fields, max struct size is 256 bytes (actual: $memsize)", struct.position)
if(uniqueFields.isEmpty())
errors.err("struct must contain at least one field", struct.position)
struct.fields.forEach {
if(!it.first.isBasic && !it.first.base.isNumericOrBool && !it.first.isPointer)
errors.err("only booleans, numeric and pointer fields allowed in a struct: '${it.second}'", struct.position)
}
}
override fun visit(deref: PtrDereference) {
if((deref.parent as? BinaryExpression)?.operator==".") {
throw FatalAstException("binexpr with '.' operator should have been converted into PtrDereference or something else ${deref.position}")
}
if(deref.inferType(program).isUnknown)
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
}
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
return if (targetDt.isString) {
when {
@@ -1944,10 +2202,10 @@ internal class AstChecker(private val program: Program,
}
when {
targetDt.isFloat -> {
val number=value.number
if (number > compilerOptions.compTarget.FLOAT_MAX_POSITIVE || number < compilerOptions.compTarget.FLOAT_MAX_NEGATIVE)
return err("value '$number' out of range")
targetDt.isBool -> {
if (value.type!=BaseDataType.BOOL) {
err("value type ${value.type.toString().lowercase()} doesn't match target type $targetDt")
}
}
targetDt.isUnsignedByte -> {
if(value.type==BaseDataType.FLOAT)
@@ -1977,6 +2235,11 @@ internal class AstChecker(private val program: Program,
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
}
targetDt.isFloat -> {
val number=value.number
if (number > compilerOptions.compTarget.FLOAT_MAX_POSITIVE || number < compilerOptions.compTarget.FLOAT_MAX_NEGATIVE)
return err("value '$number' out of range")
}
targetDt.isLong -> {
if(value.type==BaseDataType.FLOAT)
err("integer value expected instead of float; possible loss of precision")
@@ -1984,15 +2247,16 @@ internal class AstChecker(private val program: Program,
if (number < -2147483647 || number > 2147483647)
return err("value '$number' out of range for long")
}
targetDt.isBool -> {
if (value.type!=BaseDataType.BOOL) {
err("type of value ${value.type.toString().lowercase()} doesn't match target $targetDt")
}
}
targetDt.isArray -> {
return checkValueTypeAndRange(targetDt.elementType(), value)
}
else -> return err("type of value ${value.type.toString().lowercase()} doesn't match target $targetDt")
targetDt.isPointer -> {
return value.type==BaseDataType.UWORD
}
targetDt.isStructInstance -> {
return err("assigning to struct instance not supported (use pointers)")
}
else -> return err("value type ${value.type.toString().lowercase()} doesn't match target type $targetDt")
}
return true
}
@@ -2001,11 +2265,17 @@ internal class AstChecker(private val program: Program,
val array = value.value.map {
when (it) {
is NumericLiteral -> it.number.toInt()
is AddressOf -> it.identifier.nameInSource.hashCode() and 0xffff
is AddressOf -> {
if(it.identifier!=null)
it.identifier!!.nameInSource.hashCode() and 0xffff
else if(it.dereference!=null)
it.dereference!!.chain.hashCode() and 0xffff
else 9999999
}
is IdentifierReference -> it.nameInSource.hashCode() and 0xffff
is TypecastExpression -> {
is TypecastExpression if it.type.isBasic -> {
val constVal = it.expression.constValue(program)
val cast = constVal?.cast(it.type, true)
val cast = constVal?.cast(it.type.base, true)
if(cast==null || !cast.isValid)
-9999999
else
@@ -2036,7 +2306,7 @@ internal class AstChecker(private val program: Program,
}
if (!correct) {
if (value.parent is VarDecl && !value.value.all { it is NumericLiteral || it is AddressOf })
errors.err("array literal for variable initialization contains non-constant elements", value.position)
errors.err("initialization value contains non-constant elements", value.value[0].position)
else
errors.err("array element out of range for type $targetDt", value.position)
}
@@ -2057,7 +2327,7 @@ internal class AstChecker(private val program: Program,
}
if (sourceDatatype.isArray) {
if(targetDatatype.isUnsignedWord)
errors.err("invalid assignment value, maybe forgot '&' (address-of)", position)
errors.err("invalid assignment value", position)
else
errors.err("cannot assign array", position) // also includes range expressions
return false
@@ -2087,16 +2357,31 @@ internal class AstChecker(private val program: Program,
errors.err("cannot assign word to byte, maybe use msb() or lsb()", position)
else if(sourceDatatype.isFloat&& targetDatatype.isInteger)
errors.err("cannot assign float to ${targetDatatype}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else if(targetDatatype.isUnsignedWord && sourceDatatype.isPassByRef) {
// this is allowed: a pass-by-reference datatype into an uword (pointer value).
else if(targetDatatype.isUnsignedWord && (sourceDatatype.isPassByRef || sourceDatatype.isPointer)) {
// this is allowed: a pass-by-reference or pointer datatype into an uword (untyped pointer value).
}
else if(sourceIsBitwiseOperatorExpression && targetDatatype.equalsSize(sourceDatatype)) {
// this is allowed: bitwise operation between different types as long as they're the same size.
}
else if (targetDatatype.isPointer) {
if(sourceDatatype.isPointer) {
if(!(sourceDatatype isAssignableTo targetDatatype))
errors.err("cannot assign different pointer type", position)
} else if(sourceDatatype.isString && targetDatatype.sub?.isByte==true) {
// assigning a string to a byte pointer is allowed.
} else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance)
errors.err("incompatible value type, can only assign uword or correct pointer type", position)
}
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)
errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
else if(targetDatatype.isStructInstance) {
if(sourceDatatype.isStructInstance && sourceDatatype != targetDatatype)
errors.err("value type $sourceDatatype doesn't match target type $targetDatatype", position)
else
errors.err("assigning to struct instance not supported (use pointers)", position)
}
else
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
errors.err("value type $sourceDatatype doesn't match target type $targetDatatype", position)
return false
}
@@ -2106,6 +2391,10 @@ internal class AstChecker(private val program: Program,
errors.err("on..goto index must be an unsigned byte", onGoto.index.position)
}
}
override fun visit(deref: ArrayIndexedPtrDereference) {
errors.err("no support for dereferencing after array indexing here yet. (Split the expression by using an intermediate variable?)", deref.position)
}
}
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {

View File

@@ -119,7 +119,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
checker2.visit(this)
if(errors.noErrors()) {
val lit2decl = LiteralsToAutoVars(this, errors)
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
lit2decl.visit(this)
while(errors.noErrors() && lit2decl.applyModifications()>0)
lit2decl.visit(this)
@@ -202,7 +202,7 @@ internal fun Subroutine.hasRtsInAsm(checkOnlyLastInstruction: Boolean): Boolean
}
internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, statement: Statement, errors: IErrorReporter): Statement? {
when (val targetStatement = this.targetStatement(program)) {
when (val targetStatement = targetStatement(program.builtinFunctions)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
is VarDecl -> {
if(statement is Jump) {
@@ -214,7 +214,7 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st
else
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
}
is Alias -> {
is Alias, is StructDecl, is StructFieldRef -> {
return targetStatement
}
null -> {

View File

@@ -40,7 +40,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
}
override fun visit(alias: Alias) {
if(alias.target.targetStatement(program)==null)
if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
}
@@ -94,7 +94,6 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
super.visit(decl)
}
override fun visit(subroutine: Subroutine) {
if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined
@@ -169,13 +168,13 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
private fun visitFunctionCall(call: IFunctionCall) {
if(call.target.nameInSource==listOf("rnd") || call.target.nameInSource==listOf("rndw")) {
val target = call.target.targetStatement(program)
val target = call.target.targetStatement(program.builtinFunctions)
if(target==null) {
errors.err("rnd() and rndw() builtin functions have been moved into the math module", call.position)
return
}
}
when (val target = call.target.targetStatement(program)) {
when (val target = call.target.targetStatement(program.builtinFunctions)) {
is Subroutine -> {
val expectedNumberOfArgs: Int = target.parameters.size
if(call.args.size != expectedNumberOfArgs) {
@@ -215,7 +214,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
if(target.type!=VarDeclType.VAR || !target.datatype.isUnsignedWord)
errors.err("wrong address variable datatype, expected uword", call.target.position)
}
is Alias -> {}
is Alias, is StructDecl, is StructFieldRef -> {}
null -> {}
else -> errors.err("cannot call this as a subroutine or function", call.target.position)
}

View File

@@ -124,7 +124,14 @@ class AstPreprocessor(val program: Program,
// we need to handle multi-decl here too, the desugarer maybe has not processed it here yet...
if(decl.value!=null) {
decl.names.forEach { name ->
val target = AssignTarget(IdentifierReference(listOf(name), decl.position), null, null, null, false, decl.position)
val target = AssignTarget(
IdentifierReference(listOf(name), decl.position),
null,
null,
null,
false,
position = decl.position
)
val assign = Assignment(target.copy(), decl.value!!.copy(), AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.InsertAfter(decl, assign, scope))
}
@@ -140,7 +147,14 @@ class AstPreprocessor(val program: Program,
} else {
// handle declaration of a single variable
if(decl.value!=null && !decl.datatype.isIterable) {
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, null, false, decl.position)
val target = AssignTarget(
IdentifierReference(listOf(decl.name), decl.position),
null,
null,
null,
false,
position = decl.position
)
val assign = Assignment(target, decl.value!!, AssignmentOrigin.VARINIT, decl.position)
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
decl.value = null
@@ -186,6 +200,17 @@ class AstPreprocessor(val program: Program,
return makeUnSplitArray(decl)
}
// convert all antlr names to structs
val antlrTypeName = decl.datatype.subTypeFromAntlr
if(antlrTypeName!=null) {
val node = decl.definingScope.lookup(antlrTypeName)
if(node==null) {
errors.err("cannot find struct type ${antlrTypeName.joinToString(".")}", decl.position)
} else {
decl.datatype.setActualSubType(node as StructDecl)
}
}
return noModifications
}
@@ -236,7 +261,23 @@ class AstPreprocessor(val program: Program,
errors.err("can only use R0-R15 as register param for normal subroutines", it.position)
}
}
return mods
if(mods.isNotEmpty())
return mods
}
}
subroutine.returntypes.forEach {
if(it.subTypeFromAntlr!=null) {
val struct = subroutine.lookup(it.subTypeFromAntlr!!) as? ISubType
if(struct!=null)
it.setActualSubType(struct)
}
}
subroutine.parameters.forEach {
if(it.type.subTypeFromAntlr!=null) {
val struct = subroutine.lookup(it.type.subTypeFromAntlr!!) as? ISubType
if(struct!=null)
it.type.setActualSubType(struct)
}
}
@@ -248,6 +289,19 @@ class AstPreprocessor(val program: Program,
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
checkStringParam(functionCallExpr as IFunctionCall, stmtOfExpression)
if(functionCallExpr.target.nameInSource==listOf("sizeof")) {
val arg = functionCallExpr.args.firstOrNull()
if (arg is PtrDereference) {
// replace sizeof(ptr^^) with sizeof(type that ptr points to)
val dt = arg.inferType(program)
if(dt.isKnown) {
val dtName = dt.getOrUndef().toString()
val newArg = IdentifierReference(dtName.split("."), arg.position)
return listOf(IAstModification.ReplaceNode(arg, newArg, functionCallExpr))
}
}
}
return noModifications
}
@@ -257,13 +311,57 @@ class AstPreprocessor(val program: Program,
}
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
val tgt = alias.target.targetStatement(program)
val tgt = alias.target.targetStatement(program.builtinFunctions)
if(tgt is Block) {
errors.err("cannot alias blocks", alias.target.position)
}
return noModifications
}
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
// convert all antlr names to structs
if(typecast.type.subTypeFromAntlr!=null) {
val struct = typecast.definingScope.lookup(typecast.type.subTypeFromAntlr!!) as? ISubType
if(struct!=null)
typecast.type.setActualSubType(struct)
}
return noModifications
}
override fun after(field: StructFieldRef, parent: Node): Iterable<IAstModification> {
if(field.type.subTypeFromAntlr!=null) {
val struct = field.definingScope.lookup(field.type.subTypeFromAntlr!!) as? ISubType
if(struct!=null)
field.type.setActualSubType(struct)
}
return noModifications
}
override fun after(struct: StructDecl, parent: Node): Iterable<IAstModification> {
// convert all antlr names to structs
struct.fields.forEach {
if(it.first.subTypeFromAntlr!=null) {
val struct = struct.definingScope.lookup(it.first.subTypeFromAntlr!!) as? ISubType
if(struct!=null)
it.first.setActualSubType(struct)
}
}
// convert str fields to ^^ubyte
val convertedFields = struct.fields.map {
if(it.first.isString)
DataType.pointer(BaseDataType.UBYTE) to it.second // replace str field with ^^ubyte field
else
it.first to it.second
}.toTypedArray()
if(!convertedFields.contentEquals(struct.fields))
convertedFields.copyInto(struct.fields)
return noModifications
}
private fun checkStringParam(call: IFunctionCall, stmt: Statement) {
val targetStatement = call.target.checkFunctionOrLabelExists(program, stmt, errors)
if(targetStatement!=null) {

View File

@@ -9,7 +9,6 @@ import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.BaseDataType
import prog8.code.core.IErrorReporter
import prog8.code.core.isByte
import prog8.code.core.isWord
@@ -29,39 +28,26 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
}
}
if(typecast.type==sourceDt.base)
if(typecast.type==sourceDt)
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
if(sourceDt.isPassByRef) {
if(typecast.type == BaseDataType.UWORD) {
if(typecast.type.isUnsignedWord) {
val identifier = typecast.expression as? IdentifierReference
if(identifier!=null) {
return if(identifier.isSubroutineParameter()) {
listOf(
IAstModification.ReplaceNode(
typecast,
typecast.expression,
parent
)
)
if (identifier != null) {
return if (identifier.isSubroutineParameter()) {
listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
} else {
listOf(
IAstModification.ReplaceNode(
typecast,
AddressOf(identifier, null, false, typecast.position),
parent
)
)
listOf(IAstModification.ReplaceNode(typecast,
AddressOf(identifier, null, null, false, false,typecast.position), parent))
}
} else if(typecast.expression is IFunctionCall) {
return listOf(
IAstModification.ReplaceNode(
typecast,
typecast.expression,
parent
)
)
} else if (typecast.expression is IFunctionCall) {
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
}
} else if(sourceDt.isString && typecast.type.isPointer && typecast.type.sub==BaseDataType.UBYTE) {
// casting a string to a ^^ubyte is just taking the address of the string.
val addr = AddressOf(typecast.expression as IdentifierReference, null, null, false, true, typecast.position)
return listOf(IAstModification.ReplaceNode(typecast, addr, parent))
} else {
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
}

View File

@@ -25,11 +25,23 @@ internal class CodeDesugarer(val program: Program, private val target: ICompilat
// - flatten chained assignments
// - remove alias nodes
// - convert on..goto/call to jumpaddr array and separate goto/call
// - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d)
// - replace ptr^^ by @(ptr) if ptr is just an uword.
// - replace p1^^ = p2^^ by memcopy.
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.datatype.isPointer && decl.datatype.sub==BaseDataType.STR) {
errors.info("^^str replaced by ^^ubyte", decl.position)
val decl2 = decl.copy(DataType.pointer(BaseDataType.UBYTE))
return listOf(IAstModification.ReplaceNode(decl, decl2, parent))
}
return noModifications
}
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
fun jumpAfter(stmt: Statement): Iterable<IAstModification> {
val label = program.makeLabel("after", breakStmt.position)
@@ -112,10 +124,6 @@ if not CONDITION
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
if(!whileLoop.condition.inferType(program).isBool) {
errors.err("condition should be a boolean", whileLoop.condition.position)
}
/*
while true -> repeat
while false -> discard
@@ -171,7 +179,14 @@ _after:
}
if(functionCall.target.nameInSource==listOf("poke")) {
// poke(a, v) is synonymous with @(a) = v
val tgt = AssignTarget(null, null, DirectMemoryWrite(functionCall.args[0], position), null, false, position)
val tgt = AssignTarget(
null,
null,
DirectMemoryWrite(functionCall.args[0], position),
null,
false,
position = position
)
val assign = Assignment(tgt, functionCall.args[1], AssignmentOrigin.OPTIMIZER, position)
return listOf(IAstModification.ReplaceNode(functionCall as Node, assign, parent))
}
@@ -202,21 +217,56 @@ _after:
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
// replace pointervar[word] by @(pointervar+word) to avoid the
// "array indexing is limited to byte size 0..255" error for pointervariables.
if(arrayIndexedExpression.pointerderef!=null) {
return noModifications
}
val indexExpr = arrayIndexedExpression.indexer.indexExpr
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
if(arrayVar!=null && arrayVar.datatype.isUnsignedWord) {
val wordIndex = TypecastExpression(indexExpr, BaseDataType.UWORD, true, indexExpr.position)
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
return if(parent is AssignTarget) {
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
if(arrayVar!=null && (arrayVar.datatype.isUnsignedWord || (arrayVar.datatype.isPointer && arrayVar.datatype.sub==BaseDataType.UBYTE))) {
val wordIndex = TypecastExpression(indexExpr, DataType.UWORD, true, indexExpr.position)
val address = BinaryExpression(
arrayIndexedExpression.plainarrayvar!!.copy(),
"+",
wordIndex,
arrayIndexedExpression.position
)
return if (parent is AssignTarget) {
// assignment to array
val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
val newtarget = AssignTarget(null, null, memwrite, null, false, arrayIndexedExpression.position)
val newtarget = AssignTarget(
null,
null,
memwrite,
null,
false,
position = arrayIndexedExpression.position
)
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
} else {
// read from array
val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
}
} else if(arrayVar!=null && (arrayVar.type==VarDeclType.MEMORY || arrayVar.datatype.isString || arrayVar.datatype.isPointer || arrayVar.datatype.isArray)) {
return noModifications
} else if(arrayVar!=null) {
// it could be a pointer dereference instead of a simple array variable
TODO("deref[word] rewrite ???? $arrayIndexedExpression")
// val dt = arrayIndexedExpression.plainarrayvar!!.traverseDerefChainForDt(null)
// if(dt.isUnsignedWord) {
// // ptr.field[index] --> @(ptr.field + index)
// val index = arrayIndexedExpression.indexer.indexExpr
// val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", index, arrayIndexedExpression.position)
// if(parent is AssignTarget) {
// val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
// return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memwrite, parent))
// } else {
// val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
// return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
// }
// }
}
return noModifications
}
@@ -257,6 +307,63 @@ _after:
return listOf(IAstModification.ReplaceNode(expr, squareCall, parent))
}
if(expr.operator==".") {
val left = expr.left as? ArrayIndexedExpression
val right = expr.right as? PtrDereference
if(left!=null && right!=null) {
if(parent is BinaryExpression && parent.operator=="." && parent.right===expr) {
val parentLeft = parent.left as? IdentifierReference
if(parentLeft!=null) {
// parent is:
// BinaryExpression "."
// / \
// IdRef (this BinExpr)
// x.y / \
// ArrayIdx PtrDeref
// z[i] field
//
// transform this into this so it can be processed further:
//
// BinaryExpression "."
// / \
// ArrayIdx IdentifierRef
// x.y.z[i] field
val combinedIdentifier = IdentifierReference(parentLeft.nameInSource+left.plainarrayvar!!.nameInSource, parentLeft.position)
val newleft = ArrayIndexedExpression(combinedIdentifier, null, left.indexer, left.position)
val newright = IdentifierReference(listOf(right.chain.single()), right.position)
return listOf(
IAstModification.ReplaceNode(parent.left, newleft, parent),
IAstModification.ReplaceNode(parent.right, newright, parent)
)
}
}
}
if(expr.left is ArrayIndexedExpression && right!=null) {
// replace replace x.y.listarray[2]^^.value with just x.y.listarray[2] . value
// this will be further modified elsewhere
val ident = IdentifierReference(right.chain, right.position)
return listOf(IAstModification.ReplaceNode(expr.right, ident, expr))
// TODO hmmm , replace cx16.r1 = listarray[2]^^.value with a temp pointer var to contain the indexed value
// val assign = expr.parent as? Assignment
// if(assign!=null) {
// val ptrDt = expr.left.inferType(program).getOrUndef()
// val pointerVar = VarDecl.createAuto(ptrDt)
// val pointerIdent = IdentifierReference(pointerVar.name.split("."), expr.position)
// val tgt = AssignTarget(pointerIdent, null, null, null, false, null, position = expr.position)
// val assignPtr = Assignment(tgt, expr.left, AssignmentOrigin.USERCODE, expr.position)
// val derefValue = PtrDereference(pointerIdent.nameInSource + right.chain, false, expr.position)
// return listOf(
// IAstModification.InsertBefore(assign, assignPtr, assign.parent as IStatementContainer),
// IAstModification.ReplaceNode(assign.value, derefValue, assign),
// IAstModification.InsertFirst(pointerVar, expr.definingSubroutine!!)
// )
// }
}
}
return noModifications
}
@@ -268,8 +375,8 @@ _after:
val addrOf = memread.addressExpression as? AddressOf
if(addrOf?.arrayIndex!=null)
return noModifications
if(addrOf!=null && addrOf.identifier.inferType(program).isWords) {
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier), memread.position)
if(addrOf!=null && addrOf.identifier?.inferType(program)?.isWords==true) {
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), memread.position), mutableListOf(addrOf.identifier!!), memread.position)
return listOf(IAstModification.ReplaceNode(memread, lsb, parent))
}
val expr = memread.addressExpression as? BinaryExpression
@@ -277,9 +384,11 @@ _after:
val addressOf = expr.left as? AddressOf
val offset = (expr.right as? NumericLiteral)?.number?.toInt()
if(addressOf!=null && offset==1) {
val variable = addressOf.identifier.targetVarDecl()
val variable = addressOf.identifier?.targetVarDecl()
if(variable!=null && variable.datatype.isWord) {
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(
addressOf.identifier!!
), memread.position)
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
}
}
@@ -349,6 +458,41 @@ _after:
return noModifications
}
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
if (identifier.nameInSource.size>1) {
val firstTarget = (identifier.firstTarget() as? VarDecl)
val firstDt = firstTarget?.datatype
if (firstDt?.isPointer == true) {
// the a.b.c.d can be a pointer dereference chain ending in a struct field; a^^.b^^.c^^.d
val chain = mutableListOf(identifier.nameInSource[0])
var struct = firstDt.subType as? StructDecl
for(name in identifier.nameInSource.drop(1)) {
if(struct==null) {
errors.err("unknown field '${name}", position = identifier.position)
return noModifications
}
val fieldDt = struct.getFieldType(name)
if(fieldDt==null) {
errors.err("unknown field '${name}' in struct '${struct.name}'", identifier.position)
return noModifications
}
if(fieldDt.isPointer) {
chain.add(name)
struct = fieldDt.subType as? StructDecl
} else {
chain.add(name)
struct = null
}
}
val deref = PtrDereference(chain, false, identifier.position)
return listOf(IAstModification.ReplaceNode(identifier, deref, parent))
}
}
return noModifications
}
override fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> {
val indexDt = ongoto.index.inferType(program).getOrUndef()
if(!indexDt.isUnsignedByte)
@@ -375,11 +519,11 @@ _after:
} else {
conditionVar = VarDecl.createAuto(indexDt)
indexValue = IdentifierReference(listOf(conditionVar.name), conditionVar.position)
val varTarget = AssignTarget(indexValue, null, null, null, false, conditionVar.position)
val varTarget = AssignTarget(indexValue, null, null, null, false, position=conditionVar.position)
assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
}
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), null, ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
val callIndexed = AnonymousScope.empty(ongoto.position)
if(ongoto.isCall) {
callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position))
@@ -404,4 +548,187 @@ _after:
, ongoto.position)
return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent))
}
override fun after(deref: PtrDereference, parent: Node): Iterable<IAstModification> {
val isLHS = parent is AssignTarget
val varDt = (deref.firstTarget() as? VarDecl)?.datatype
if(varDt?.isUnsignedWord==true || (varDt?.isPointer==true && varDt.sub?.isByte==true)) {
// replace ptr^^ by @(ptr) when ptr is uword or ^^byte
val identifier = IdentifierReference(deref.chain, deref.position)
if(isLHS && varDt.sub==BaseDataType.UBYTE) {
val memwrite = DirectMemoryWrite(identifier, deref.position)
return listOf(IAstModification.ReplaceNode(deref, memwrite, parent))
} else if(!isLHS) {
val memread = DirectMemoryRead(identifier, deref.position)
val replacement = if (varDt.sub == BaseDataType.BYTE)
TypecastExpression(memread, DataType.BYTE, true, memread.position)
else
memread
return listOf(IAstModification.ReplaceNode(deref, replacement, parent))
}
}
val expr = deref.parent as? BinaryExpression
if (expr != null && expr.operator == ".") {
if (expr.left is IdentifierReference && expr.right === deref) {
// replace (a) . (b^^) by (a.b)^^
val name = (expr.left as IdentifierReference).nameInSource + deref.chain
val replacement = PtrDereference(name, deref.derefLast, deref.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, expr.parent))
} else if(expr.left===deref && expr.right is ArrayIndexedExpression) {
// replace (a^^) . ( s[b] ) by (a^^.s^^)[b]
val idx = expr.right as ArrayIndexedExpression
if(idx.plainarrayvar!=null) {
val name = deref.chain + idx.plainarrayvar!!.nameInSource
val ptrDeref = PtrDereference(name, false, deref.position)
val indexer = ArrayIndexedExpression(null, ptrDeref, idx.indexer, idx.position)
return listOf(IAstModification.ReplaceNode(expr, indexer, expr.parent))
} else {
TODO("convert ptr.p[idx]")
}
}
}
return noModifications
}
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
// get rid of the ArrayIndexedPtrDereference AST node, replace it with other AST nodes that are equivalent
if(deref.chain.last().second!=null && deref.derefLast && deref.chain.dropLast(1).all { it.second==null } ) {
// parent could be Assigment directly, or a binexpr chained pointer expression (with '.' operator)_
if(parent is Assignment) {
val dt = deref.inferType(program).getOrUndef()
require(dt.isNumericOrBool)
if (parent.value isSameAs deref) {
// x = z[i]^^ --> peekX(z[i])
val (peekFunc, cast) =
if(dt.isBool) "peekbool" to null
else if (dt.isUnsignedByte) "peek" to null
else if (dt.isSignedByte) "peek" to DataType.BYTE
else if (dt.isUnsignedWord) "peekw" to null
else if (dt.isSignedWord) "peekw" to DataType.WORD
else if (dt.isLong) "peekl" to null
else if (dt.isFloat) "peekf" to null
else throw FatalAstException("can only deref a numeric or boolean pointer here")
val indexer = deref.chain.last().second!!
val identifier = IdentifierReference(deref.chain.map { it.first }, deref.position)
val indexed = ArrayIndexedExpression(identifier, null, indexer, deref.position)
val peekIdent = IdentifierReference(listOf(peekFunc), deref.position)
val peekCall = FunctionCallExpression(peekIdent, mutableListOf(indexed), deref.position)
if(cast==null)
return listOf(IAstModification.ReplaceNode(parent.value, peekCall, parent))
else {
val casted = TypecastExpression(peekCall, cast, true, deref.position)
return listOf(IAstModification.ReplaceNode(parent.value, casted, parent))
}
}
} else if(parent is BinaryExpression && parent.operator==".") {
val left = parent.left as? IdentifierReference
val right = parent.right as? ArrayIndexedPtrDereference
if(left!=null && right!=null) {
if(right.chain.last().second!=null && right.derefLast && right.chain.dropLast(1).all { it.second!=null }) {
// (a.b.c) . (d[i]^^) --> a.b.c.d[i]^^
val combinedIdentifier = left.nameInSource+right.chain.map { it.first }
val chain: List<Pair<String, ArrayIndex?>> = combinedIdentifier.dropLast(1).map { it to null } + (combinedIdentifier.last() to right.chain.last().second)
val deref = ArrayIndexedPtrDereference(chain,true, right.position)
return listOf(IAstModification.ReplaceNode(parent, deref, parent.parent))
}
}
//val dt = parent.inferType(program).getOrUndef()
TODO("translate deref $deref here ${deref.position}")
}
else if(parent is AssignTarget) {
// z[i]^^ = value --> pokeX(z[i], value)
val dt = deref.inferType(program).getOrUndef()
require(dt.isNumericOrBool)
val (pokeFunc, cast) =
if(dt.isBool) "pokebool" to null
else if (dt.isUnsignedByte) "poke" to null
else if (dt.isSignedByte) "poke" to DataType.UBYTE
else if (dt.isUnsignedWord) "pokew" to null
else if (dt.isSignedWord) "pokew" to DataType.UWORD
else if (dt.isLong) "pokel" to null
else if (dt.isFloat) "pokef" to null
else throw FatalAstException("can only deref a numeric or boolean pointer here")
val indexer = deref.chain.last().second!!
val identifier = IdentifierReference(deref.chain.map { it.first }, deref.position)
val indexed = ArrayIndexedExpression(identifier, null, indexer, deref.position)
val pokeIdent = IdentifierReference(listOf(pokeFunc), deref.position)
val assignment = parent.parent as Assignment
val pokeCall: FunctionCallStatement
if(cast==null) {
pokeCall = FunctionCallStatement(pokeIdent, mutableListOf(indexed, assignment.value), false, deref.position)
}
else {
val casted = TypecastExpression(assignment.value, cast, true, deref.position)
pokeCall = FunctionCallStatement(pokeIdent, mutableListOf(indexed, casted), false, deref.position)
}
return listOf(IAstModification.ReplaceNode(assignment, pokeCall, assignment.parent))
}
else {
TODO("cannot translate $deref here ${deref.position}")
}
}
val firstIndexed = deref.chain.indexOfFirst { it.second!=null }
if(firstIndexed == 0 && deref.chain.size>1) {
// z[i]^^.field --> (z[i]) . (field)
val index = deref.chain.first()
val tail = deref.chain.drop(1)
if (tail.any { it.second != null }) {
TODO("support multiple array indexed dereferencings ${deref.position}")
} else if (parent !is AssignTarget) {
val pointer = IdentifierReference(listOf(index.first), deref.position)
val left = ArrayIndexedExpression(pointer, null, index.second!!, deref.position)
val right = PtrDereference(tail.map { it.first }, deref.derefLast, deref.position)
val derefExpr = BinaryExpression(left, ".", right, deref.position)
return listOf(IAstModification.ReplaceNode(deref, derefExpr, parent))
}
}
return noModifications
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
subroutine.returntypes.withIndex().forEach { (idx, rt) ->
if(rt.isPointer && rt.sub==BaseDataType.STR) {
errors.info("^^str replaced by ^^ubyte in return type(s)", subroutine.position)
subroutine.returntypes[idx] = DataType.pointer(BaseDataType.UBYTE)
}
}
subroutine.parameters.withIndex().forEach { (idx, param) ->
if(param.type.isPointer && param.type.sub==BaseDataType.STR) {
errors.info("^^str replaced by ^^ubyte", param.position)
subroutine.parameters[idx] = SubroutineParameter(param.name, DataType.pointer(BaseDataType.UBYTE), param.zp, param.registerOrPair, param.position)
}
}
return noModifications
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val targetDt = assignment.target.inferType(program)
val sourceDt = assignment.value.inferType(program)
if(targetDt.isStructInstance && sourceDt.isStructInstance) {
if(targetDt == sourceDt) {
val size = program.memsizer.memorySize(sourceDt.getOrUndef(), null)
require(program.memsizer.memorySize(targetDt.getOrUndef(), null)==size)
val sourcePtr = IdentifierReference((assignment.value as PtrDereference).chain, assignment.position)
val targetPtr = IdentifierReference(assignment.target.pointerDereference!!.chain, assignment.position)
val numBytes = NumericLiteral.optimalInteger(size, assignment.position)
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assignment.position),
mutableListOf(sourcePtr, targetPtr, numBytes),
false, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, memcopy, parent))
}
}
return noModifications
}
}

View File

@@ -1,9 +1,6 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
@@ -12,7 +9,7 @@ import prog8.code.ast.PtContainmentCheck
import prog8.code.core.IErrorReporter
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
@@ -88,9 +85,23 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
// ubyte a,b,c = multi() --> ubyte a,b,c / a,b,c = multi()
val modifications = mutableListOf<IAstModification>()
val variables = decl.names.map {
AssignTarget(IdentifierReference(listOf(it), decl.position), null, null, null, false, decl.position)
AssignTarget(
IdentifierReference(listOf(it), decl.position),
null,
null,
null,
false,
position = decl.position
)
}
val multiassignFuncCall = Assignment(AssignTarget(null, null, null, variables, false, decl.position), decl.value!!, AssignmentOrigin.VARINIT, decl.position)
val multiassignFuncCall = Assignment(AssignTarget(
null,
null,
null,
variables,
false,
position = decl.position
), decl.value!!, AssignmentOrigin.VARINIT, decl.position)
decl.value = null
modifications += IAstModification.InsertAfter(decl, multiassignFuncCall, parent as IStatementContainer)
return modifications
@@ -98,8 +109,8 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
// note: the desugaring of a multi-variable vardecl has to be done here
// and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise)
if(!decl.datatype.isNumericOrBool)
errors.err("can only multi declare numeric and boolean variables", decl.position)
if(!decl.datatype.isNumericOrBool && !decl.datatype.isPointer && !decl.datatype.isStructInstance)
errors.err("cannot multi declare variables of this type", decl.position)
if(decl.alignment != 0u) {
errors.err("only single variable declarations can have alignment", decl.position)
return noModifications
@@ -116,7 +127,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
}
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
val target = alias.target.targetStatement(program)
val target = alias.target.targetStatement()
if(target is Alias) {
val newAlias = Alias(alias.alias, target.target, alias.position)
return listOf(IAstModification.ReplaceNode(alias, newAlias, parent))
@@ -125,11 +136,21 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
}
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
val target = identifier.targetStatement(program)
val target = identifier.targetStatement()
if(target is StructFieldRef) {
// replace a.b.c.d by a^^.b^^.c^^.d
// but only if we're not part of a binary expression with '.' operator (those are handled elsewhere)
if(parent !is BinaryExpression || parent.operator != ".") {
val chain = identifier.nameInSource
val deref = PtrDereference(chain, false, identifier.position)
return listOf(IAstModification.ReplaceNode(identifier, deref, parent))
}
}
// don't replace an identifier in an Alias or when the alias points to another alias (that will be resolved first elsewhere)
if(target is Alias && parent !is Alias) {
if(target.target.targetStatement(program) !is Alias)
if(target.target.targetStatement() !is Alias)
return listOf(IAstModification.ReplaceNode(identifier, target.target.copy(position = identifier.position), parent))
}
@@ -146,4 +167,26 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
// }
return noModifications
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator==".") {
val leftIdent = expr.left as? IdentifierReference
val rightIndex = expr.right as? ArrayIndexedExpression
if (leftIdent != null && rightIndex != null) {
// maybe recombine IDENTIFIER . ARRAY[IDX] --> COMBINEDIDENTIFIER[IDX]
val leftTarget = leftIdent.targetStatement()
if(leftTarget==null || leftTarget !is StructDecl) {
if(rightIndex.plainarrayvar!=null) {
val combinedName = leftIdent.nameInSource + rightIndex.plainarrayvar!!.nameInSource
val combined = IdentifierReference(combinedName, leftIdent.position)
val indexer = ArrayIndexedExpression(combined, null, rightIndex.indexer, leftIdent.position)
return listOf(IAstModification.ReplaceNode(expr, indexer, parent))
} else {
throw FatalAstException("didn't expect pointer[idx] in this phase already")
}
}
}
}
return noModifications
}
}

View File

@@ -17,6 +17,7 @@ import prog8.compiler.builtinFunctionReturnType
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
import kotlin.math.log2
/**
@@ -74,9 +75,11 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
is UntilLoop -> throw FatalAstException("until loops must have been converted to jumps")
is VarDecl -> transform(statement)
is StructDecl -> transform(statement)
is When -> transform(statement)
is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
is OnGoto -> throw FatalAstException("ongoto must have been converted to array and separate call/goto")
is StructFieldRef -> throw FatalAstException("should not occur as part of the actual AST")
}
}
@@ -97,9 +100,37 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
is StringLiteral -> transform(expr)
is TypecastExpression -> transform(expr)
is IfExpression -> transform(expr)
is PtrDereference -> transform(expr)
is ArrayIndexedPtrDereference -> throw FatalAstException("this should have been converted to some other ast nodes")
}
}
private fun transform(deref: PtrDereference): PtPointerDeref {
val type = deref.inferType(program).getOrElse {
throw FatalAstException("unknown dt")
}
val targetVar = deref.definingScope.lookup(deref.chain) as? VarDecl
if(targetVar!=null) {
val startpointer = PtIdentifier(targetVar.scopedName.joinToString("."), targetVar.datatype, deref.position)
val result = PtPointerDeref(type, emptyList(), deref.derefLast, deref.position)
result.add(startpointer)
return result
} else {
for(skip in 1..deref.chain.size) {
val targetVar = deref.definingScope.lookup(deref.chain.take(skip)) as? VarDecl
if(targetVar!=null) {
val startpointer = PtIdentifier(targetVar.scopedName.joinToString("."), targetVar.datatype, deref.position)
val chain = deref.chain.takeLast(deref.chain.size-skip)
val result = PtPointerDeref(type, chain, deref.derefLast, deref.position)
result.add(startpointer)
return result
}
}
}
throw FatalAstException("cannot find startpointer from ${deref.chain} ${deref.position}")
}
private fun transform(ifExpr: IfExpression): PtIfExpression {
val type = ifExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val ifexpr = PtIfExpression(type, ifExpr.position)
@@ -158,6 +189,38 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
else -> Pair("", null)
}
if(augmentedValue!=null) {
if(srcAssign.target.inferType(program).isPointer) {
val expr = srcExpr as BinaryExpression
if(expr.operator=="+") {
// pointer arithmetic: add the size of the struct times the argument
val leftDt = expr.left.inferType(program).getOrUndef()
require(leftDt.isPointer && !expr.right.inferType(program).isPointer)
val structSize = leftDt.size(program.memsizer)
val constValue = augmentedValue.constValue(program)
if(constValue!=null) {
val total = constValue.number*structSize
if (total == 0.0)
return PtNop(srcAssign.position)
else {
val assign = PtAugmentedAssign(operator, srcAssign.position)
assign.add(transform(srcAssign.target))
assign.add(PtNumber(BaseDataType.UWORD, total, srcAssign.position))
return assign
}
} else {
val multiplication = PtBinaryExpression("*", DataType.UWORD, srcAssign.position)
multiplication.add(transformExpression(augmentedValue))
multiplication.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), srcAssign.position))
val assign = PtAugmentedAssign(operator, srcAssign.position)
assign.add(transform(srcAssign.target))
assign.add(multiplication)
return assign
}
} else
throw FatalAstException("unexpected augmented assignment operator on pointer ${expr.operator}")
}
val assign = PtAugmentedAssign(operator, srcAssign.position)
assign.add(transform(srcAssign.target))
assign.add(transformExpression(augmentedValue))
@@ -181,18 +244,18 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
private fun transform(srcTarget: AssignTarget): PtAssignTarget {
val target = PtAssignTarget(srcTarget.void, srcTarget.position)
if(srcTarget.identifier!=null)
target.add(transform(srcTarget.identifier!!))
else if(srcTarget.arrayindexed!=null)
target.add(transform(srcTarget.arrayindexed!!))
else if(srcTarget.memoryAddress!=null)
target.add(transform(srcTarget.memoryAddress!!))
else if(!srcTarget.void)
throw FatalAstException("invalid AssignTarget")
when {
srcTarget.identifier!=null -> target.add(transform(srcTarget.identifier!!))
srcTarget.arrayindexed!=null -> target.add(transform(srcTarget.arrayindexed!!))
srcTarget.memoryAddress!=null -> target.add(transform(srcTarget.memoryAddress!!))
srcTarget.pointerDereference!=null -> target.add(transform(srcTarget.pointerDereference!!))
srcTarget.arrayIndexedDereference!=null -> TODO("this array indexed dereference should have been converted to some other ast nodes ${srcTarget.position}")
!srcTarget.void -> throw FatalAstException("invalid AssignTarget")
}
return target
}
private fun transform(identifier: IdentifierReference): PtIdentifier {
private fun transform(identifier: IdentifierReference): PtExpression {
val (target, type) = identifier.targetNameAndType(program)
return PtIdentifier(target, type, identifier.position)
}
@@ -340,9 +403,18 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
}
val (target, _) = srcCall.target.targetNameAndType(program)
val iType = srcCall.inferType(program)
val call = PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
val targetStruct = srcCall.target.targetStructDecl()
val call =
if(targetStruct!=null) {
// a call to a struct yields a pointer to a struct instance and means: allocate a statically initialized struct instance of that type
PtBuiltinFunctionCall("structalloc", false, true, DataType.pointer(targetStruct), srcCall.position)
} else {
// regular function call
val (target, _) = srcCall.target.targetNameAndType(program)
val iType = srcCall.inferType(program)
PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
}
for (arg in srcCall.args)
call.add(transformExpression(arg))
return call
@@ -506,6 +578,8 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position) })
val varbank = if(srcSub.asmAddress?.varbank==null) null else transform(srcSub.asmAddress!!.varbank!!)
if(varbank!=null && varbank !is PtIdentifier)
throw FatalAstException("varbank must be a regular variable")
val asmAddr = if(srcSub.asmAddress==null) null else {
val constAddr = srcSub.asmAddress!!.address.constValue(program)
if(constAddr==null) throw FatalAstException("extsub address should be a constant")
@@ -555,11 +629,10 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
if(it.isString) DataType.UWORD else it
}
// do not bother about the 'inline' hint of the source subroutine.
val sub = PtSub(srcSub.name,
srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position) },
returnTypes,
srcSub.position)
sub.parameters.forEach { it.parent=sub }
val sub = PtSub(srcSub.name,srcSub.position)
val signature = PtSubSignature( returnTypes, srcSub.position)
srcSub.parameters.forEach { signature.add(PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position)) }
sub.add(signature)
makeScopeVarsDecls(vardecls).forEach { sub.add(it) }
for (statement in statements)
sub.add(transformStatement(statement))
@@ -590,6 +663,10 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
}
private fun transform(struct: StructDecl): PtStructDecl {
return PtStructDecl(struct.name, struct.fields.toList(), struct.position)
}
private fun transform(srcWhen: When): PtWhen {
val w = PtWhen(srcWhen.position)
w.add(transformExpression(srcWhen.condition))
@@ -616,22 +693,44 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
private fun transform(src: AddressOf): PtAddressOf {
val addr = PtAddressOf(src.position, src.msb)
addr.add(transform(src.identifier))
if(src.arrayIndex!=null)
val addr = PtAddressOf(src.inferType(program).getOrUndef(), src.position, src.msb)
if(src.identifier!=null)
addr.add(transform(src.identifier!!))
if (src.arrayIndex != null)
addr.add(transformExpression(src.arrayIndex!!.indexExpr))
if (src.dereference!=null)
addr.add(transformExpression(src.dereference!!))
return addr
}
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
val dt = srcArr.arrayvar.targetVarDecl()!!.datatype
if(!dt.isArray && !dt.isString)
throw FatalAstException("array indexing can only be used on array or string variables ${srcArr.position}")
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(eltType, srcArr.position)
array.add(transform(srcArr.arrayvar))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array
if(srcArr.plainarrayvar!=null) {
val dt = srcArr.plainarrayvar!!.inferType(program)
if (!dt.isArray && !dt.isString && !dt.isPointer)
throw FatalAstException("array indexing can only be used on array, string or pointer variables ${srcArr.position}")
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(eltType, srcArr.position)
array.add(transform(srcArr.plainarrayvar!!))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array
}
if(srcArr.pointerderef!=null) {
val dt = srcArr.pointerderef!!.inferType(program)
if(dt.isUnsignedWord) {
val array = PtArrayIndexer(DataType.UBYTE, srcArr.position)
array.add(transform(srcArr.pointerderef!!))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array
} else if(dt.isPointer) {
val eltType = dt.getOrUndef().dereference()
val array = PtArrayIndexer(eltType, srcArr.position)
array.add(transform(srcArr.pointerderef!!))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array
} else
TODO("transform pointer index $dt ${srcArr.position}")
}
throw FatalAstException("expected plain array variable or pointer dereference")
}
private fun transform(srcArr: ArrayLiteral): PtArray {
@@ -644,12 +743,159 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
return arr
}
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
expr.add(transformExpression(srcExpr.left))
expr.add(transformExpression(srcExpr.right))
return expr
private fun transform(srcExpr: BinaryExpression): PtExpression {
val type = srcExpr.inferType(program).getOrElse {
throw FatalAstException("unknown dt $srcExpr")
}
if(srcExpr.operator==".") {
when (srcExpr.right) {
is IdentifierReference -> {
val arrayIndexer = srcExpr.left as? ArrayIndexedExpression
val valueName = srcExpr.right as? IdentifierReference
if (arrayIndexer == null) {
TODO("unexpected deref expression ${srcExpr.position}")
} else if(valueName?.nameInSource?.size!=1) {
TODO("unexpected deref expression ${srcExpr.position}")
} else {
// the expression is: a.b.c[i] . value
val result = PtBinaryExpression(".", type, srcExpr.position)
result.add(transformExpression(arrayIndexer))
result.add(PtIdentifier(valueName.nameInSource[0], type, srcExpr.position))
return result
}
}
is ArrayIndexedExpression -> {
errors.err("at the moment it is not possible to chain array syntax on pointers like ...p1[x].p2[y]... use separate expressions for the time being", srcExpr.right.position) // TODO add support for chained array syntax on pointers (rewrite ast?)
return PtIdentifier("<dummy>", DataType.UNDEFINED, Position.DUMMY)
}
else -> throw FatalAstException("unknown deref at ${srcExpr.position}")
}
} else {
if(srcExpr.left.inferType(program).isPointer || srcExpr.right.inferType(program).isPointer) {
if (srcExpr.operator == "+") return transformWithPointerArithmetic(srcExpr)
else if (srcExpr.operator in ComparisonOperators) return transformWithPointerComparison(srcExpr)
} else if(srcExpr.left.inferType(program).isPointer) {
return when (srcExpr.operator) {
"-" -> transformWithPointerArithmetic(srcExpr) // '+' is handled above
in ComparisonOperators -> transformWithPointerComparison(srcExpr)
else -> throw FatalAstException("unsupported operator on pointer: ${srcExpr.operator} at ${srcExpr.position}")
}
}
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
expr.add(transformExpression(srcExpr.left))
expr.add(transformExpression(srcExpr.right))
return expr
}
}
private fun transformWithPointerComparison(expr: BinaryExpression): PtBinaryExpression {
val leftDt = expr.left.inferType(program)
val rightDt = expr.right.inferType(program)
require(leftDt.isPointer || leftDt.isWords)
require(rightDt.isPointer || rightDt.isWords)
val comparison = PtBinaryExpression(expr.operator, DataType.BOOL, expr.position)
comparison.add(transformExpression(expr.left))
comparison.add(transformExpression(expr.right))
return comparison
}
private fun transformWithPointerArithmetic(expr: BinaryExpression): PtExpression {
val operator = expr.operator
require(operator=="+" || operator=="-")
// below where '+' is used, you can substitute '-'.
// pointer arithmetic: ptr + value
val leftDt = expr.left.inferType(program).getOrUndef()
val rightDt = expr.right.inferType(program).getOrUndef()
if(leftDt.isPointer && !rightDt.isPointer) {
val resultDt = leftDt
val structSize = leftDt.size(program.memsizer)
val constValue = expr.right.constValue(program)
if(constValue!=null) {
// ptr + constvalue * structsize
val total = constValue.number*structSize
if (total == 0.0)
return transformExpression(expr.left)
else {
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
plusorminus.add(transformExpression(expr.left))
plusorminus.add(PtNumber(BaseDataType.UWORD, total, expr.position))
return plusorminus
}
} else {
if(structSize==1) {
// ptr + right, just keep it as it is
val plus = PtBinaryExpression("+", DataType.UWORD, expr.position)
plus.add(transformExpression(expr.left))
plus.add(transformExpression(expr.right))
return plus
} else {
// ptr + right * structSize
val offset: PtExpression
if(structSize in powersOfTwoInt) {
// don't multiply simply shift
offset = PtBinaryExpression("<<", DataType.UWORD, expr.position)
offset.add(transformExpression(expr.right))
offset.add(PtNumber(BaseDataType.UWORD, log2(structSize.toDouble()), expr.position))
}
else {
offset = PtBinaryExpression("*", DataType.UWORD, expr.position)
offset.add(transformExpression(expr.right))
offset.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), expr.position))
}
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
plusorminus.add(transformExpression(expr.left))
plusorminus.add(offset)
return plusorminus
}
}
} else if(!leftDt.isPointer && rightDt.isPointer) {
val resultDt = rightDt
val structSize = rightDt.size(program.memsizer)
val constValue = expr.left.constValue(program)
if(constValue!=null) {
// ptr + constvalue * structsize
val total = constValue.number*structSize
if (total == 0.0)
return transformExpression(expr.right)
else {
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
plusorminus.add(transformExpression(expr.right))
plusorminus.add(PtNumber(BaseDataType.UWORD, total, expr.position))
return plusorminus
}
} else {
if(structSize==1) {
// ptr + left, just keep it as it is
val plus = PtBinaryExpression("+", DataType.UWORD, expr.position)
plus.add(transformExpression(expr.left))
plus.add(transformExpression(expr.right))
return plus
} else {
// ptr + left * structSize
val offset: PtExpression
if(structSize in powersOfTwoInt) {
// don't multiply simply shift
offset = PtBinaryExpression("<<", DataType.UWORD, expr.position)
offset.add(transformExpression(expr.left))
offset.add(PtNumber(BaseDataType.UWORD, log2(structSize.toDouble()), expr.position))
}
else {
offset = PtBinaryExpression("*", DataType.UWORD, expr.position)
offset.add(transformExpression(expr.left))
offset.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), expr.position))
}
val plusorminus = PtBinaryExpression(operator, resultDt, expr.position)
plusorminus.add(offset)
plusorminus.add(transformExpression(expr.right))
return plusorminus
}
}
} else {
throw FatalAstException("weird pointer arithmetic ${expr.position}")
}
}
private fun transform(srcCheck: ContainmentCheck): PtExpression {
@@ -671,14 +917,14 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
expr.add(high)
} else {
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
val lowFloat = PtTypeCast(BaseDataType.FLOAT, range.from.position)
val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position)
lowFloat.add(transformExpression(range.from))
low.add(lowFloat)
low.add(x1)
expr.add(low)
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
high.add(x2)
val highFLoat = PtTypeCast(BaseDataType.FLOAT, range.to.position)
val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position)
highFLoat.add(transformExpression(range.to))
high.add(highFLoat)
expr.add(high)
@@ -769,7 +1015,8 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
private fun transform(srcCast: TypecastExpression): PtTypeCast {
val cast = PtTypeCast(srcCast.type, srcCast.position)
cast.add(transformExpression(srcCast.expression))
require(cast.type!=cast.value.type)
require(cast.type!=cast.value.type) {
"bogus typecast shouldn't occur at ${srcCast.position}" }
return cast
}

View File

@@ -1,12 +1,76 @@
package prog8.compiler.astprocessing
import prog8.ast.FatalAstException
import prog8.code.StStruct
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
internal fun postprocessSimplifiedAst(program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
internal fun postprocessSimplifiedAst(
program: PtProgram,
st: SymbolTable,
option: CompilationOptions,
errors: IErrorReporter
) {
processDefers(program, st, errors)
processSubtypesIntoStReferences(program, st)
if(option.compTarget.cpu!=CpuType.VIRTUAL)
checkForPointerTypesOn6502(program, errors) // TODO remove this once the 6502 codegen can deal with pointer types and structs
}
private fun checkForPointerTypesOn6502(program: PtProgram, errors: IErrorReporter) {
fun check(node: PtNode) {
when(node) {
//is PtAddressOf -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtAssignTarget -> if(!node.void && node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
//is PtBinaryExpression -> if(node.left.type.isPointer || node.right.type.isPointer) errors.err("cannot do pointer arithmetic yet on 6502 target $node", node.position)
is PtIdentifier -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtPointerDeref -> errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtPrefix -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtTypeCast -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtStructDecl -> errors.err("cannot use struct type yet on 6502 target $node", node.position)
is PtSubroutineParameter -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtVariable -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtSubSignature -> if(node.returns.any{it.isPointer}) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
else -> {}
}
}
walkAst(program) { node, _ -> check(node) }
}
private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable) {
fun getStStruct(subType: ISubType): StStruct {
val stNode = st.lookup(subType.scopedNameString) as? StStruct
if(stNode != null)
return stNode
else
throw FatalAstException("cannot find in ST: ${subType.scopedNameString} $subType")
}
fun fixSubtype(type: DataType) {
if(type.subType!=null && type.subType !is StStruct) {
type.subType = getStStruct(type.subType!!)
}
}
fun fixSubtypes(node: PtNode) {
when(node) {
is IPtVariable -> fixSubtype(node.type)
is PtPointerDeref -> fixSubtype(node.type)
is PtStructDecl -> node.fields.forEach { fixSubtype(it.first) }
is PtAsmSub -> node.returns.forEach { fixSubtype(it.second) }
is PtExpression -> fixSubtype(node.type)
is PtSubSignature -> node.returns.forEach { fixSubtype(it) }
is PtSubroutineParameter -> fixSubtype(node.type)
else -> { /* has no datatype */ }
}
}
walkAst(program) { node, _ -> fixSubtypes(node) }
}
@@ -56,8 +120,9 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
it.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, sub.position))
})
assignZero.add(PtNumber(BaseDataType.UBYTE, 0.0, sub.position))
sub.add(0, assignZero)
sub.add(0, deferVariable)
val firstIndex = sub.children.indexOfFirst { it !is PtSubSignature } // first child node is the sub's signature so add below that one
sub.add(firstIndex, assignZero)
sub.add(firstIndex, deferVariable)
for((deferIndex, defer) in defers.withIndex()) {
// replace the defer statement with one that enables the bit in the mask for this defer
@@ -150,7 +215,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
}
// complex return value, need to store it before calling the defer block
errors.warn("using defer with complex return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
errors.warn("using defer with nontrivial return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) }
val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order
val popCalls = pushAndPopCalls.map { it.second }
@@ -160,9 +225,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
popCalls.forEach { newRet.add(it) }
group.add(PtFunctionCall(ret.definingSub()!!.scopedName+"."+invokeDefersRoutineName, true,DataType.UNDEFINED, ret.position))
group.add(newRet)
group.parent = ret.parent
val idx = ret.parent.children.indexOf(ret)
ret.parent.children[idx] = group
replaceNode(ret, group)
}
// subroutine ends
@@ -198,9 +261,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
})
branchcc.add(PtNodeGroup())
defersRoutine.add(branchcc)
for(c in defer.children) {
defersRoutine.add(c)
}
transferChildren(defer, defersRoutine, true)
defersRoutine.add(PtLabel(skiplabel, Position.DUMMY))
}
// val printMask = PtFunctionCall("txt.print_ubbin", true, DataType.UNDEFINED, Position.DUMMY)
@@ -212,3 +273,18 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
sub.add(defersRoutine)
}
}
private fun transferChildren(source: PtNode, target: PtNode, keepExisting: Boolean = false) {
if(!keepExisting)
target.children.clear()
for(c in source.children)
target.add(c)
}
private fun replaceNode(oldNode: PtNode, newNode: PtNode) {
newNode.parent = oldNode.parent
val idx = oldNode.parent.children.indexOf(oldNode)
oldNode.parent.children[idx] = newNode
}

View File

@@ -49,7 +49,7 @@ internal class StatementReorderer(
if(decl.dirty && decl.value!=null)
errors.err("dirty variable can't have initialization value", decl.position)
if (decl.datatype.isNumericOrBool) {
if (decl.datatype.isNumericOrBool || decl.datatype.isPointer) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null || decl.value?.constValue(program)?.number==0.0) {
@@ -67,7 +67,14 @@ internal class StatementReorderer(
if (!canSkipInitializationWith0(decl)) {
// Add assignment to initialize with zero
val identifier = IdentifierReference(listOf(decl.name), decl.position)
val assignzero = Assignment(AssignTarget(identifier, null, null, null, false, decl.position),
val assignzero = Assignment(AssignTarget(
identifier,
null,
null,
null,
false,
position = decl.position
),
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
return listOf(IAstModification.InsertAfter(
decl, assignzero, parent as IStatementContainer
@@ -80,7 +87,7 @@ internal class StatementReorderer(
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos),
val assign = Assignment(AssignTarget(identifier, null, null, null, false, position = pos),
decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
return listOf(IAstModification.InsertAfter(
@@ -99,7 +106,7 @@ internal class StatementReorderer(
if(target!=null && target.isArray) {
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos),
val assign = Assignment(AssignTarget(identifier, null, null, null, false, position = pos),
decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
return listOf(IAstModification.InsertAfter(

View File

@@ -66,8 +66,12 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(valueDt isNotAssignableTo decl.datatype)
return noModifications
// uwords are allowed to be assigned to pointers as initialization value without a cast
if(decl.datatype.isPointer && valueDt.isUnsignedWord)
return noModifications
val modifications = mutableListOf<IAstModification>()
addTypecastOrCastedValueModification(modifications, declValue, decl.datatype.base, decl)
addTypecastOrCastedValueModification(modifications, declValue, decl.datatype, decl)
return modifications
}
}
@@ -98,7 +102,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
// if(rightDt.isBytes)
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
}
} else if (parent is TypecastExpression && parent.type == BaseDataType.UWORD && parent.parent is Assignment) {
} else if (parent is TypecastExpression && parent.type.isUnsignedWord && parent.parent is Assignment) {
val assign = parent.parent as Assignment
if (assign.target.inferType(program).isWords) {
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
@@ -131,34 +135,34 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(leftDt issimpletype BaseDataType.BYTE && (rightDt issimpletype BaseDataType.UBYTE || rightDt issimpletype BaseDataType.UWORD)) {
// cast left to unsigned
val cast = TypecastExpression(expr.left, rightDt.getOrUndef().base, true, expr.left.position)
val cast = TypecastExpression(expr.left, rightDt.getOrUndef(), true, expr.left.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
if(leftDt issimpletype BaseDataType.WORD && (rightDt issimpletype BaseDataType.UBYTE || rightDt issimpletype BaseDataType.UWORD)) {
// cast left to unsigned word. Cast right to unsigned word if it is ubyte
val mods = mutableListOf<IAstModification>()
val cast = TypecastExpression(expr.left, BaseDataType.UWORD, true, expr.left.position)
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
mods += IAstModification.ReplaceNode(expr.left, cast, expr)
if(rightDt issimpletype BaseDataType.UBYTE) {
mods += IAstModification.ReplaceNode(expr.right,
TypecastExpression(expr.right, BaseDataType.UWORD, true, expr.right.position),
TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position),
expr)
}
return mods
}
if(rightDt issimpletype BaseDataType.BYTE && (leftDt issimpletype BaseDataType.UBYTE || leftDt issimpletype BaseDataType.UWORD)) {
// cast right to unsigned
val cast = TypecastExpression(expr.right, leftDt.getOrUndef().base, true, expr.right.position)
val cast = TypecastExpression(expr.right, leftDt.getOrUndef(), true, expr.right.position)
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
}
if(rightDt issimpletype BaseDataType.WORD && (leftDt issimpletype BaseDataType.UBYTE || leftDt issimpletype BaseDataType.UWORD)) {
// cast right to unsigned word. Cast left to unsigned word if it is ubyte
val mods = mutableListOf<IAstModification>()
val cast = TypecastExpression(expr.right, BaseDataType.UWORD, true, expr.right.position)
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
mods += IAstModification.ReplaceNode(expr.right, cast, expr)
if(leftDt issimpletype BaseDataType.UBYTE) {
mods += IAstModification.ReplaceNode(expr.left,
TypecastExpression(expr.left, BaseDataType.UWORD, true, expr.left.position),
TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position),
expr)
}
return mods
@@ -176,14 +180,47 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
} else {
val modifications = mutableListOf<IAstModification>()
when {
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt.base, expr)
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt.base, expr)
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr)
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr)
else -> throw FatalAstException("confused binary expression side")
}
return modifications
}
}
}
// comparison of a pointer with a number will simply treat the pointer as the uword that it is
// this may require casting the other operand to uword as well
if(expr.operator in ComparisonOperators) {
val modifications = mutableListOf<IAstModification>()
if(leftDt.isNumeric && rightDt.isPointer) {
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
modifications += IAstModification.ReplaceNode(expr.right, cast, expr)
if(!leftDt.isUnsignedWord && leftDt isAssignableTo InferredTypes.knownFor(BaseDataType.UWORD)) {
val cast2 = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
modifications += IAstModification.ReplaceNode(expr.left, cast2, expr)
}
}
else if(leftDt.isPointer && rightDt.isNumeric) {
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
modifications += IAstModification.ReplaceNode(expr.left, cast, expr)
if(!rightDt.isUnsignedWord && rightDt isAssignableTo InferredTypes.knownFor(BaseDataType.UWORD)) {
val cast2 = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
modifications += IAstModification.ReplaceNode(expr.right, cast2, expr)
}
}
return modifications
}
// pointer arithmetic
if(leftDt.isPointer) {
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
} else if(rightDt.isPointer) {
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
// check if shifts have a positive integer shift type
@@ -213,7 +250,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
return noModifications
val modifications = mutableListOf<IAstModification>()
addTypecastOrCastedValueModification(modifications, assignment.value, targettype.base, assignment)
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
return modifications
} else {
fun castLiteral(cvalue2: NumericLiteral): List<IAstModification.ReplaceNode> {
@@ -243,51 +280,77 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
private fun afterFunctionCallArgs(call: IFunctionCall): Iterable<IAstModification> {
// see if a typecast is needed to convert the arguments into the required parameter type
val modifications = mutableListOf<IAstModification>()
val params = when(val sub = call.target.targetStatement(program)) {
is BuiltinFunctionPlaceholder -> BuiltinFunctions.getValue(sub.name).parameters.toList()
is Subroutine -> sub.parameters.map { FParam(it.name, it.type.base) }
val paramsPossibleDatatypes = when(val sub = call.target.targetStatement(program.builtinFunctions)) {
is BuiltinFunctionPlaceholder -> {
BuiltinFunctions.getValue(sub.name).parameters.map {
it.possibleDatatypes.map { dt ->
if(dt.isArray)
DataType.arrayFor(BaseDataType.BOOL, false) // the builtin function signature doesn't tell us the element type....
else if(dt.isPointer)
DataType.pointer(BaseDataType.UBYTE)
else
DataType.forDt(dt)
}
}
}
is Subroutine -> sub.parameters.map { listOf(it.type) }
is StructDecl -> sub.fields.map { listOf(it.first) }
else -> emptyList()
}
params.zip(call.args).forEach {
val targetDt = it.first.possibleDatatypes.first()
paramsPossibleDatatypes.zip(call.args).forEach {
val possibleTargetDt = it.first.first()
val targetDt = if(possibleTargetDt.isPointer) DataType.UWORD else possibleTargetDt // use UWORD instead of a pointer type (using words for pointers is allowed without further casting)
val argIdt = it.second.inferType(program)
if (argIdt.isKnown) {
if (argIdt.isKnown && !targetDt.isStructInstance) {
val argDt = argIdt.getOrUndef()
if (argDt.base !in it.first.possibleDatatypes) {
if (argDt !in it.first) {
val identifier = it.second as? IdentifierReference
val number = it.second as? NumericLiteral
if(number!=null) {
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
} else if(identifier!=null && targetDt==BaseDataType.UWORD && argDt.isPassByRef) {
} else if(identifier!=null && targetDt.isUnsignedWord && argDt.isPassByRef) {
if(!identifier.isSubroutineParameter()) {
// We allow STR/ARRAY values for UWORD parameters.
// If it's an array (not STR), take the address.
if(!argDt.isString) {
// We allow STR/ARRAY values for UWORD or ^^UBYTE parameters.
if(!argDt.isString || it.second is IdentifierReference) {
modifications += IAstModification.ReplaceNode(
identifier,
AddressOf(identifier, null, false, it.second.position),
AddressOf(identifier, null, null, false, true, it.second.position),
call as Node
)
}
}
} else if(targetDt==BaseDataType.BOOL) {
addTypecastOrCastedValueModification(modifications, it.second, BaseDataType.BOOL, call as Node)
} else if(!targetDt.isIterable && argDt isAssignableTo DataType.forDt(targetDt)) {
if(!argDt.isString || targetDt!=BaseDataType.UWORD)
} else if(targetDt.isBool) {
addTypecastOrCastedValueModification(modifications, it.second, DataType.BOOL, call as Node)
} else if(!targetDt.isIterable && argDt isAssignableTo targetDt) {
if(!argDt.isString || !targetDt.isUnsignedWord)
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
}
}
} else {
val identifier = it.second as? IdentifierReference
if(identifier!=null && targetDt==BaseDataType.UWORD) {
// take the address of the identifier
modifications += IAstModification.ReplaceNode(
identifier,
AddressOf(identifier, null, false, it.second.position),
call as Node
)
if(identifier!=null && targetDt.isUnsignedWord) {
val dt = identifier.inferType(program)
if(dt.isArray || dt.isString) {
// take the address of the identifier
modifications += IAstModification.ReplaceNode(
identifier,
AddressOf(identifier, null, null, false, false,it.second.position),
call as Node
)
} else if(dt.isUnknown) {
val subOrLabel = identifier.targetStatement()
if(subOrLabel is Subroutine || subOrLabel is Label) {
// take the address of the subroutine or label
modifications += IAstModification.ReplaceNode(
identifier,
AddressOf(identifier, null, null, false, false, it.second.position),
call as Node
)
}
}
}
}
}
@@ -296,7 +359,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
// warn about any implicit type casts to Float, because that may not be intended
if(typecast.implicit && typecast.type==BaseDataType.FLOAT) {
if(typecast.implicit && typecast.type.isFloat) {
if(options.floats)
errors.info("integer implicitly converted to float. Suggestion: use float literals, add an explicit cast, or revert to integer arithmetic", typecast.position)
else
@@ -307,30 +370,32 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
val modifications = mutableListOf<IAstModification>()
val dt = memread.addressExpression.inferType(program)
if(dt.isKnown && !dt.getOr(DataType.UWORD).isUnsignedWord) {
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
if(castedValue!=null)
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
else
addTypecastOrCastedValueModification(modifications, memread.addressExpression, BaseDataType.UWORD, memread)
}
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
if(castedValue!=null)
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
else
addTypecastOrCastedValueModification(modifications, memread.addressExpression, DataType.UWORD, memread)
return modifications
}
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
val modifications = mutableListOf<IAstModification>()
val dt = memwrite.addressExpression.inferType(program)
if(dt.isKnown && !dt.getOr(DataType.UWORD).isUnsignedWord) {
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
if(castedValue!=null)
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
else
addTypecastOrCastedValueModification(modifications, memwrite.addressExpression, BaseDataType.UWORD, memwrite)
}
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(BaseDataType.UWORD, true)?.valueOrZero()
if(castedValue!=null)
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
else
addTypecastOrCastedValueModification(modifications, memwrite.addressExpression, DataType.UWORD, memwrite)
return modifications
}
@@ -358,12 +423,18 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
continue
if (returnValue is NumericLiteral) {
val cast = returnValue.cast(subReturnType.base, true)
if(cast.isValid) {
returnStmt.values[index] = cast.valueOrZero()
if((returnValue.type == BaseDataType.UWORD || returnValue.type == BaseDataType.UBYTE) && subReturnType.isPointer) {
// cast unsigned integer to the number type
val cast = TypecastExpression(returnValue, subReturnType, true, returnValue.position)
modifications += IAstModification.ReplaceNode(returnValue, cast, returnStmt)
} else {
val cast = returnValue.cast(subReturnType.base, true)
if (cast.isValid) {
returnStmt.values[index] = cast.valueOrZero()
}
}
} else {
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType.base, returnStmt)
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType, returnStmt)
continue
}
}
@@ -396,7 +467,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val varDt = when (parent) {
is ContainmentCheck -> parent.element.inferType(program)
is ForLoop -> parent.loopVarDt(program)
else -> InferredTypes.InferredType.unknown()
else -> InferredTypes.unknown()
}
return adjustRangeDts(range, fromConst, fromDt, toConst, toDt, varDt.getOrUndef(), parent)
}
@@ -408,9 +479,9 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
for((index, elt) in array.value.withIndex()) {
if (elt is IdentifierReference) {
val eltType = elt.inferType(program)
val tgt = elt.targetStatement(program)
val tgt = elt.targetStatement()
if(eltType.isIterable || tgt is Subroutine || tgt is Label || tgt is Block) {
val addressof = AddressOf(elt, null, false, elt.position)
val addressof = AddressOf(elt, null, null, false, false, elt.position)
addressof.linkParents(array)
array.value[index] = addressof
}
@@ -421,6 +492,12 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(ifExpr: IfExpression, parent: Node): Iterable<IAstModification> {
val ct = ifExpr.condition.inferType(program)
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
val cast = TypecastExpression(ifExpr.condition, DataType.BOOL, true, ifExpr.condition.position)
return listOf(IAstModification.ReplaceNode(ifExpr.condition, cast, ifExpr))
}
val trueDt = ifExpr.truevalue.inferType(program)
val falseDt = ifExpr.falsevalue.inferType(program)
if (trueDt != falseDt) {
@@ -432,13 +509,40 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
)
if (toFix != null) {
val modifications = mutableListOf<IAstModification>()
addTypecastOrCastedValueModification(modifications, toFix, commonDt.base, ifExpr)
addTypecastOrCastedValueModification(modifications, toFix, commonDt, ifExpr)
return modifications
}
}
return noModifications
}
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
val ct = whileLoop.condition.inferType(program)
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
val cast = TypecastExpression(whileLoop.condition, DataType.BOOL, true, whileLoop.condition.position)
return listOf(IAstModification.ReplaceNode(whileLoop.condition, cast, whileLoop))
}
return noModifications
}
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
val ct = ifElse.condition.inferType(program)
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
val cast = TypecastExpression(ifElse.condition, DataType.BOOL, true, ifElse.condition.position)
return listOf(IAstModification.ReplaceNode(ifElse.condition, cast, ifElse))
}
return noModifications
}
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val ct = untilLoop.condition.inferType(program)
if(!ct.isBool && (ct.isNumeric || ct.isPointer)) {
val cast = TypecastExpression(untilLoop.condition, DataType.BOOL, true, untilLoop.condition.position)
return listOf(IAstModification.ReplaceNode(untilLoop.condition, cast, untilLoop))
}
return noModifications
}
private fun adjustRangeDts(
range: RangeExpression,
fromConst: NumericLiteral?,
@@ -534,7 +638,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val (commonDt, toChange) = BinaryExpression.commonDatatype(fromDt, toDt, range.from, range.to)
if(toChange!=null)
addTypecastOrCastedValueModification(modifications, toChange, commonDt.base, range)
addTypecastOrCastedValueModification(modifications, toChange, commonDt, range)
return modifications
}
@@ -546,7 +650,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val idxDt = arrayIndexedExpression.indexer.indexExpr.inferType(program).getOrUndef()
if(idxDt.base.largerSizeThan(smaller.type)) {
val newIdx = ArrayIndex(smaller, smaller.position)
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.arrayvar, newIdx, arrayIndexedExpression.position)
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.plainarrayvar, arrayIndexedExpression.pointerderef, newIdx, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, newIndexer, parent))
}
}
@@ -556,16 +660,33 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
private fun addTypecastOrCastedValueModification(
modifications: MutableList<IAstModification>,
expressionToCast: Expression,
requiredType: BaseDataType,
requiredType: DataType,
parent: Node
) {
val sourceDt = expressionToCast.inferType(program).getOrUndef()
if(sourceDt.base == requiredType)
if(sourceDt.base == requiredType.base)
return
if(requiredType == BaseDataType.BOOL)
if(requiredType.isBool) {
if(sourceDt.isNumeric || sourceDt.isPointer) {
// only allow numerics and pointers to be implicitly cast to bool
val cast = TypecastExpression(expressionToCast, DataType.BOOL, true, expressionToCast.position)
modifications += IAstModification.ReplaceNode(expressionToCast, cast, parent)
}
return
if(expressionToCast is NumericLiteral && expressionToCast.type!=BaseDataType.FLOAT) { // refuse to automatically truncate floats
val castedValue = expressionToCast.cast(requiredType, true)
}
// uwords are allowed to be assigned to pointers without a cast
if(requiredType.isPointer && sourceDt.isUnsignedWord)
return
if (requiredType.isUnsignedWord) {
// & (address-of) is allowed to be assigned to an uword without a cast
if (expressionToCast is AddressOf) return
// casting a pointer to an uword is not needed
if (expressionToCast.inferType(program).isPointer) return
}
if(expressionToCast is NumericLiteral && expressionToCast.type!=BaseDataType.FLOAT && requiredType.isNumericOrBool) { // refuse to automatically truncate floats
val castedValue = expressionToCast.cast(requiredType.base, true)
if (castedValue.isValid) {
val signOriginal = sign(expressionToCast.number)
val signCasted = sign(castedValue.valueOrZero().number)
@@ -575,6 +696,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
return
}
}
val cast = TypecastExpression(expressionToCast, requiredType, true, expressionToCast.position)
modifications += IAstModification.ReplaceNode(expressionToCast, cast, parent)
}

View File

@@ -4,29 +4,28 @@ import prog8.ast.FatalAstException
import prog8.code.ast.PtExpression
import prog8.code.ast.PtFunctionCall
import prog8.code.ast.PtTypeCast
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall, PtExpression> {
var popTypecast: BaseDataType? = null
var pushTypecast: BaseDataType? = null
var popTypecast: DataType? = null
var pushTypecast: DataType? = null
var pushWord = false
var pushFloat = false
when {
value.type.isBool -> {
pushTypecast = BaseDataType.UBYTE
popTypecast = BaseDataType.BOOL
pushTypecast = DataType.UBYTE
popTypecast = DataType.BOOL
}
value.type.isSignedByte -> {
pushTypecast = BaseDataType.UBYTE
popTypecast = BaseDataType.BYTE
pushTypecast = DataType.UBYTE
popTypecast = DataType.BYTE
}
value.type.isSignedWord -> {
pushWord = true
pushTypecast = BaseDataType.UWORD
popTypecast = BaseDataType.WORD
pushTypecast = DataType.UWORD
popTypecast = DataType.WORD
}
value.type.isUnsignedByte -> {}
value.type.isUnsignedWord -> pushWord = true

View File

@@ -24,15 +24,14 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
// check and possibly adjust value datatype vs decl datatype
val valueType = decl.value?.inferType(program)
if(valueType!=null && !valueType.istype(decl.datatype)) {
if(valueType.isUnknown) {
errors.err("value has incompatible type for ${decl.datatype}", decl.value!!.position)
if(valueType.isUnknown)
return noModifications
}
val valueDt = valueType.getOrUndef()
when(decl.type) {
VarDeclType.VAR -> {
if(decl.isArray) {
if(decl.datatype.isSplitWordArray)
// using a array of words as initializer to a pointer array is fine
if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray)
errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position)
} else if(!decl.datatype.isString) {
if (valueDt.largerSizeThan(decl.datatype)) {
@@ -41,8 +40,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
else
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
} else {
throw FatalAstException("value dt differs from decl dt ${decl.position}")
}
}
}
@@ -70,11 +67,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
// check splitting of word arrays
if(!decl.datatype.isWordArray && decl.splitwordarray != SplitWish.DONTCARE) {
if(decl.splitwordarray != SplitWish.DONTCARE && !decl.datatype.isWordArray && !decl.datatype.isPointerArray) {
if(decl.origin != VarDeclOrigin.ARRAYLITERAL)
errors.err("@split and @nosplit are for word arrays only", decl.position)
errors.err("@split and @nosplit are for word or pointer arrays only", decl.position)
}
else if(decl.datatype.isWordArray) {
if(decl.datatype.isWordArray) {
var changeDataType: DataType?
var changeSplit: SplitWish = decl.splitwordarray
when(decl.splitwordarray) {
@@ -84,15 +82,29 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
changeSplit = SplitWish.NOSPLIT
}
else {
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base)
changeDataType = if(decl.datatype.isSplitWordArray) null else {
val eltDt = decl.datatype.elementType()
if(eltDt.isPointer)
TODO("convert array of pointers to split words array type")
else
DataType.arrayFor(eltDt.base)
}
changeSplit = SplitWish.SPLIT
}
}
SplitWish.SPLIT -> {
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base)
changeDataType = if(decl.datatype.isSplitWordArray) null else {
val eltDt = decl.datatype.elementType()
if(eltDt.isPointer)
TODO("convert array of pointers to split words array type")
else
DataType.arrayFor(eltDt.base)
}
}
SplitWish.NOSPLIT -> {
changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null
changeDataType = if(decl.datatype.isSplitWordArray && !decl.datatype.elementType().isPointer)
DataType.arrayFor(decl.datatype.elementType().base, false)
else null
}
}
if(changeDataType!=null) {
@@ -132,14 +144,17 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(constValue!=null)
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
if(typecast.expression is NumericLiteral) {
val value = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
if(value.isValid)
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
val number = typecast.expression as? NumericLiteral
if(number!=null) {
if(typecast.type.isBasic) {
val value = number.cast(typecast.type.base, typecast.implicit)
if (value.isValid)
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
}
}
val sourceDt = typecast.expression.inferType(program)
if(sourceDt issimpletype typecast.type)
if(sourceDt istype typecast.type)
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
if(parent is Assignment) {
@@ -150,6 +165,26 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
}
// number cast to bool -> number!=0
if (typecast.type.isBool) {
val et = typecast.expression.inferType(program)
if (et.isNumeric) {
if(typecast.expression is NumericLiteral) {
val boolean = NumericLiteral.fromBoolean((typecast.expression as NumericLiteral).asBooleanValue, typecast.expression.position)
return listOf(IAstModification.ReplaceNode(typecast, boolean, parent))
} else {
val zero = defaultZero(et.getOrUndef().base, typecast.position)
val cmp = BinaryExpression(typecast.expression, "!=", zero, typecast.position)
return listOf(IAstModification.ReplaceNode(typecast, cmp, parent))
}
}
else if (et.isPointer) {
val ptrAsUword = TypecastExpression(typecast.expression, DataType.UWORD, true, typecast.position)
val cmp = BinaryExpression(ptrAsUword, "!=", NumericLiteral.optimalNumeric(BaseDataType.UWORD, null, 0.0, typecast.position), typecast.position)
return listOf(IAstModification.ReplaceNode(typecast, cmp, parent))
}
}
return noModifications
}
@@ -394,17 +429,21 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val index = arrayIndexedExpression.indexer.constIndex()
if(index!=null && index<0) {
val target = arrayIndexedExpression.arrayvar.targetVarDecl()
val arraysize = target?.arraysize?.constIndex()
if(arraysize!=null) {
if(arraysize+index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.position)
return noModifications
if(arrayIndexedExpression.plainarrayvar!=null) {
val target = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
val arraysize = target?.arraysize?.constIndex()
if(arraysize!=null) {
if(arraysize+index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.position)
return noModifications
}
// replace the negative index by the normal index
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
}
// replace the negative index by the normal index
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("cleanup pointer indexing")
}
}
return noModifications
@@ -414,32 +453,40 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
val name = functionCallExpr.target.nameInSource
if(name==listOf("msw")) {
val valueDt = functionCallExpr.args[0].inferType(program)
if(valueDt.isWords || valueDt.isBytes) {
if(valueDt.isWords || valueDt.isBytes || valueDt.isPointer) {
val zero = NumericLiteral(BaseDataType.UWORD, 0.0, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, zero, parent))
}
} else if(name==listOf("lsw")) {
val valueDt = functionCallExpr.args[0].inferType(program)
if(valueDt.isWords)
if(valueDt.isWords || valueDt.isPointer)
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
if(valueDt.isBytes) {
val cast = TypecastExpression(functionCallExpr.args[0], BaseDataType.UWORD, true, functionCallExpr.position)
val cast = TypecastExpression(functionCallExpr.args[0], DataType.UWORD, true, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
if(parent is IStatementContainer) {
val targetStruct = functionCallExpr.target.targetStructDecl()
if (targetStruct != null) {
// static struct instance allocation can only occur as an initializer for a pointer variable
return listOf(IAstModification.Remove(functionCallExpr, parent as IStatementContainer))
}
}
return noModifications
}
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
if(addressOf.arrayIndex!=null) {
val tgt = addressOf.identifier.constValue(program)
val tgt = addressOf.identifier?.constValue(program)
if (tgt != null && tgt.type.isWord) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
indexExpr
else
TypecastExpression(indexExpr, tgt.type, true, indexExpr.position)
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(
IAstModification.ReplaceNode(addressOf, add, parent)
@@ -449,4 +496,3 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return noModifications
}
}

View File

@@ -83,11 +83,24 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
if(argDt==paramDt)
return true
// there are some exceptions that are considered compatible, such as STR <> UWORD
if(argDt.isString && paramDt.isUnsignedWord ||
argDt.isUnsignedWord && paramDt.isString ||
argDt.isUnsignedWord && paramDt.isUnsignedByteArray ||
argDt.isString && paramDt.isUnsignedByteArray)
// there are some exceptions that are considered compatible, such as STR <> UWORD, UWORD <> pointer
if(argDt.isUnsignedWord)
if((paramDt.isString || paramDt.isUnsignedByteArray || paramDt.isPointer))
return true
if(argDt.isString) {
if(paramDt.isUnsignedWord || paramDt.isUnsignedByteArray)
return true
if(paramDt.isPointer && paramDt.sub==BaseDataType.UBYTE)
return true
}
// if uword is passed, check if the parameter type is pointer to array element type
if(argDt.isArray && paramDt.isPointer) {
TODO("array vs element pointer check")
}
// if expected is UWORD and actual is any pointer, we allow it (uword is untyped pointer, for backwards compatibility)
if(paramDt.isUnsignedWord && argDt.isPointer)
return true
return false
@@ -97,14 +110,15 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
val argITypes = call.args.map { it.inferType(program) }
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
if(firstUnknownDt>=0) {
// if an uword is expected but a pointer is provided, that is okay without a cast
val identifier = call.args[0] as? IdentifierReference
return if(identifier==null || identifier.targetStatement(program)!=null)
return if(identifier==null || identifier.targetStatement(program.builtinFunctions)!=null)
Pair("argument ${firstUnknownDt + 1} invalid argument type", call.args[firstUnknownDt].position)
else
null
}
val argtypes = argITypes.map { it.getOrUndef() }
val target = call.target.targetStatement(program)
val target = call.target.targetStatement(program.builtinFunctions)
if (target is Subroutine) {
val consideredParamTypes: List<DataType> = target.parameters.map { it.type }
if(argtypes.size != consideredParamTypes.size)
@@ -113,6 +127,14 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
if(mismatch>=0) {
val actual = argtypes[mismatch]
val expected = consideredParamTypes[mismatch]
if(expected.isPointer && expected.sub!!.isWord) {
val arg = call.args[mismatch]
val argArray = if(arg is AddressOf) arg.identifier else arg
return if(argArray?.inferType(program)?.getOrUndef()?.isSplitWordArray==true)
Pair("argument ${mismatch + 1} cannot pass address to a split words array where a word pointer argument is expected, use a @nosplit word array instead", call.args[mismatch].position)
else
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
}
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
}
if(target.isAsmSubroutine) {

View File

@@ -7,6 +7,7 @@ import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.maps.shouldNotContainKey
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.Program
import prog8.ast.statements.Block
@@ -312,4 +313,20 @@ xyz {
val blocks2 = result2.codegenAst!!.allBlocks().toList()
blocks2.any { it.name=="xyz" } shouldBe false
}
test("symbol lookup of pointer fields should mark variable as used in callgraph") {
val src = """
main {
struct List {
^^uword s
ubyte n
}
sub start() {
^^List l1 = List()
l1.s^^ = 2
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
}
})

View File

@@ -241,7 +241,10 @@ class TestCompilerOnExamplesVirtual: FunSpec({
"bouncegfx",
"bsieve",
"pixelshader",
"sincos"
"sincos",
"pointers/animalgame",
"pointers/binarytree", // TODO add to "c64" later as well
"pointers/sortedlist" // TODO add to "c64" later as well
),
listOf(false, true)
)

View File

@@ -7,7 +7,6 @@ import io.kotest.engine.spec.tempdir
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Label
@@ -33,7 +32,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val startSub = program.entrypoint
val strLits = startSub.statements
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] as IdentifierReference }
.map { (it.args[0] as AddressOf).identifier!! }
.map { it.targetVarDecl()!!.value as StringLiteral }
strLits[0].value shouldBe "main.bar"
@@ -57,12 +56,12 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] }
val str0 = (args[0] as IdentifierReference).targetVarDecl()!!.value as StringLiteral
val str0 = (args[0] as AddressOf).identifier!!.targetVarDecl()!!.value as StringLiteral
str0.value shouldBe "main.bar"
str0.definingScope.name shouldBe "main"
val id1 = (args[1] as AddressOf).identifier
val lbl1 = id1.targetStatement(program) as Label
val id1 = (args[1] as AddressOf).identifier!!
val lbl1 = id1.targetStatement() as Label
lbl1.name shouldBe "foo_bar"
lbl1.definingScope.name shouldBe "main"
}

View File

@@ -467,8 +467,8 @@ main {
val result = compileText(Cx16Target(), false, src, outputDir)
val st = result!!.codegenAst!!.entrypoint()!!.children
st.size shouldBe 3
((st[1] as PtIfElse).condition as PtBool).value shouldBe false
st.size shouldBe 4
((st[2] as PtIfElse).condition as PtBool).value shouldBe false
}
test("containmentcheck type casting") {
@@ -483,8 +483,8 @@ main {
val result = compileText(Cx16Target(), false, src, outputDir)
val st = result!!.codegenAst!!.entrypoint()!!.children
st.size shouldBe 4
val cond = (st[2] as PtIfElse).condition as PtBinaryExpression
st.size shouldBe 5
val cond = (st[3] as PtIfElse).condition as PtBinaryExpression
cond.operator shouldBe "and"
val left = cond.left as PtBinaryExpression
val right = cond.right as PtBinaryExpression

View File

@@ -25,6 +25,11 @@ class TestLaunchEmu: FunSpec({
</VARIABLESNOINITDIRTY>
<VARIABLESWITHINIT>
</VARIABLESWITHINIT>
<STRUCTINSTANCESNOINIT>
</STRUCTINSTANCESNOINIT>
<STRUCTINSTANCES>
</STRUCTINSTANCES>
<CONSTANTS>
</CONSTANTS>

View File

@@ -41,49 +41,105 @@ class TestMemory: FunSpec({
test("assignment target not in mapped IO space C64") {
var memexpr = NumericLiteral.optimalInteger(0x0002, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
var target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xa000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xeeee, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = NumericLiteral.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
@@ -92,25 +148,53 @@ class TestMemory: FunSpec({
test("assign target in mapped IO space C64") {
var memexpr = NumericLiteral.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
var target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
memexpr = NumericLiteral.optimalInteger(0x0001, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
memexpr = NumericLiteral.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
memexpr = NumericLiteral.optimalInteger(0xdfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
@@ -120,7 +204,14 @@ class TestMemory: FunSpec({
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
val target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(decl, assignment))
return target
@@ -143,13 +234,27 @@ class TestMemory: FunSpec({
test("memory expression mapped to IO memory on C64") {
var memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
var target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe false
memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0xd020, Position.DUMMY), Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
target = AssignTarget(
null,
null,
DirectMemoryWrite(memexpr, Position.DUMMY),
null,
false,
position = Position.DUMMY
)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(c64target) shouldBe true
@@ -158,7 +263,14 @@ class TestMemory: FunSpec({
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -171,7 +283,14 @@ class TestMemory: FunSpec({
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -184,7 +303,14 @@ class TestMemory: FunSpec({
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -196,8 +322,8 @@ class TestMemory: FunSpec({
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -210,8 +336,8 @@ class TestMemory: FunSpec({
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
@@ -224,8 +350,8 @@ class TestMemory: FunSpec({
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))

View File

@@ -122,7 +122,7 @@ other {
test("generated constvalue from typecast inherits proper parent linkage") {
val number = NumericLiteral(BaseDataType.UBYTE, 11.0, Position.DUMMY)
val tc = TypecastExpression(number, BaseDataType.BYTE, false, Position.DUMMY)
val tc = TypecastExpression(number, DataType.BYTE, false, Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
tc.linkParents(ParentSentinel)
tc.parent shouldNotBe null
@@ -987,13 +987,13 @@ main {
funcarg2 shouldBe NumericLiteral(BaseDataType.UWORD, 8.0, Position.DUMMY)
val answer3ValueTc = (st[15] as Assignment).value as TypecastExpression
answer3ValueTc.type shouldBe BaseDataType.UWORD
answer3ValueTc.type shouldBe DataType.UWORD
val answer3Value = answer3ValueTc.expression as FunctionCallExpression
answer3Value.target.nameInSource shouldBe listOf("msb")
answer3Value.args.single() shouldBe instanceOf<BinaryExpression>()
val funcarg3tc = (st[16] as FunctionCallStatement).args.single() as TypecastExpression
funcarg3tc.type shouldBe BaseDataType.UWORD
funcarg3tc.type shouldBe DataType.UWORD
val funcarg3 = funcarg3tc.expression as FunctionCallExpression
funcarg3.target.nameInSource shouldBe listOf("msb")
funcarg3.args.single() shouldBe instanceOf<BinaryExpression>()
@@ -1058,10 +1058,10 @@ main {
}"""
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = true)!!
val st = result.codegenAst!!.entrypoint()!!.children
st.size shouldBe 9
(st[2] as PtFunctionCall).name shouldBe "cbm.GETIN"
(st[2] as PtFunctionCall).void shouldBe true
val a1 = st[3] as PtAssignment
st.size shouldBe 10
(st[3] as PtFunctionCall).name shouldBe "cbm.GETIN"
(st[3] as PtFunctionCall).void shouldBe true
val a1 = st[4] as PtAssignment
(a1.value as PtFunctionCall).name shouldBe "cbm.GETIN"
a1.multiTarget shouldBe true
a1.children.size shouldBe 3
@@ -1070,9 +1070,9 @@ main {
(a1.children[1] as PtAssignTarget).void shouldBe false
(a1.children[1] as PtAssignTarget).identifier!!.name shouldBe "cx16.r1L"
(st[4] as PtFunctionCall).name shouldBe "p8b_main.p8s_func1"
(st[4] as PtFunctionCall).void shouldBe true
val a2 = st[5] as PtAssignment
(st[5] as PtFunctionCall).name shouldBe "p8b_main.p8s_func1"
(st[5] as PtFunctionCall).void shouldBe true
val a2 = st[6] as PtAssignment
(a2.value as PtFunctionCall).name shouldBe "p8b_main.p8s_func2"
a2.multiTarget shouldBe true
a2.children.size shouldBe 4
@@ -1081,9 +1081,9 @@ main {
(a2.children[1] as PtAssignTarget).identifier!!.name shouldBe "cx16.r2"
(a2.children[2] as PtAssignTarget).void shouldBe true
(st[6] as PtFunctionCall).name shouldBe "p8b_main.p8s_func3"
(st[6] as PtFunctionCall).void shouldBe true
val a3 = st[7] as PtAssignment
(st[7] as PtFunctionCall).name shouldBe "p8b_main.p8s_func3"
(st[7] as PtFunctionCall).void shouldBe true
val a3 = st[8] as PtAssignment
(a3.value as PtFunctionCall).name shouldBe "p8b_main.p8s_func3"
a3.multiTarget shouldBe false
a3.children.size shouldBe 2

File diff suppressed because it is too large Load Diff

View File

@@ -58,7 +58,7 @@ class TestSymbolTable: FunSpec({
val sub1 = st.lookupOrElse("block1.sub1") { fail("should find sub1") }
sub1.name shouldBe "sub1"
sub1.scopedName shouldBe "block1.sub1"
sub1.scopedNameString shouldBe "block1.sub1"
sub1.type shouldBe StNodeType.SUBROUTINE
sub1.children.size shouldBe 4
@@ -80,31 +80,31 @@ class TestSymbolTable: FunSpec({
val st= makeSt()
st.allVariables.size shouldBe 4
st.allMemMappedVariables.single().scopedName shouldBe "block1.sub1.v3"
st.allMemorySlabs.single().scopedName shouldBe "block1.sub1.slab1"
st.allMemMappedVariables.single().scopedNameString shouldBe "block1.sub1.v3"
st.allMemorySlabs.single().scopedNameString shouldBe "block1.sub1.slab1"
}
test("static vars") {
val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY)
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0, false,node)
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false,node)
stVar1.setOnetimeInitNumeric(99.0)
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, node)
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, node)
val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null))
val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null))
val stVar3 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, 0, false, node)
val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, 0, false, node)
val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3, ZeropageWish.DONTCARE, 0, false, node)
val stVar3 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitNonzero, 3u, ZeropageWish.DONTCARE, 0u, false, node)
val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3u, ZeropageWish.DONTCARE, 0u, false, node)
val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3u, ZeropageWish.DONTCARE, 0u, false, node)
stVar1.uninitialized shouldBe false
stVar1.length shouldBe null
stVar2.uninitialized shouldBe true
stVar2.length shouldBe null
stVar3.uninitialized shouldBe false
stVar3.length shouldBe 3
stVar3.length shouldBe 3u
stVar4.uninitialized shouldBe false
stVar4.length shouldBe 3
stVar4.length shouldBe 3u
stVar5.uninitialized shouldBe true
stVar5.length shouldBe 3
stVar5.length shouldBe 3u
}
})
@@ -211,12 +211,12 @@ private fun makeSt(): SymbolTable {
block1.add(sub12)
block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", BaseDataType.UWORD, 999.0, astConstant2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, astSub1v2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v2))
sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, astSub2v1))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0, false, astSub2v2))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub2v1))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub2v2))
val block2 = StNode("block2", StNodeType.BLOCK, astBlock2)
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21)
val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22)

View File

@@ -14,6 +14,7 @@ import prog8.ast.statements.Assignment
import prog8.ast.statements.IfElse
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtSub
import prog8.code.ast.PtSubroutineParameter
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.Position
@@ -28,7 +29,7 @@ class TestTypecasts: FunSpec({
val outputDir = tempdir().toPath()
test("integer args for builtin funcs") {
val text="""
val text = """
%import floats
main {
sub start() {
@@ -37,14 +38,14 @@ class TestTypecasts: FunSpec({
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false, errors=errors)
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false, errors = errors)
result shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "type mismatch, was: float expected one of: [UWORD, WORD, LONG]"
}
test("not casting bool operands to logical operators") {
val text="""
val text = """
main {
sub start() {
bool @shared bb2=true
@@ -70,7 +71,7 @@ class TestTypecasts: FunSpec({
}
test("bool expressions with functioncalls") {
val text="""
val text = """
main {
sub ftrue(ubyte arg) -> ubyte {
arg++
@@ -137,7 +138,7 @@ main {
}
test("correct handling of bool parameters") {
val text="""
val text = """
main {
sub thing(bool b1, bool b2) -> bool {
@@ -171,7 +172,7 @@ main {
}
test("correct evaluation of words in boolean expressions") {
val text= """
val text = """
main {
sub start() {
uword camg
@@ -194,7 +195,7 @@ main {
}
test("word to byte casts") {
val text="""
val text = """
%import textio
main {
sub func(ubyte arg) -> word {
@@ -213,7 +214,7 @@ main {
}
test("ubyte to word casts") {
val src="""
val src = """
main {
sub start() {
ubyte @shared bb = 255
@@ -227,16 +228,16 @@ main {
stmts.size shouldBe 5
val assign1tc = (stmts[2] as Assignment).value as TypecastExpression
val assign2tc = (stmts[3] as Assignment).value as TypecastExpression
assign1tc.type shouldBe BaseDataType.WORD
assign2tc.type shouldBe BaseDataType.WORD
assign1tc.type shouldBe DataType.WORD
assign2tc.type shouldBe DataType.WORD
assign2tc.expression shouldBe instanceOf<IdentifierReference>()
val assign1subtc = (assign1tc.expression as TypecastExpression)
assign1subtc.type shouldBe BaseDataType.BYTE
assign1subtc.type shouldBe DataType.BYTE
assign1subtc.expression shouldBe instanceOf<IdentifierReference>()
}
test("add missing & to function arguments") {
val text="""
val text = """
main {
sub handler(uword fptr) {
@@ -312,7 +313,7 @@ main {
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors=errors) shouldBe null
compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "no cast"
errors.errors[1] shouldContain "no cast"
@@ -343,7 +344,7 @@ main {
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, errors=errors) shouldBe null
compileText(C64Target(), false, text, outputDir, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "in-place makes no sense"
}
@@ -502,7 +503,7 @@ main {
}
test("various floating point casts don't crash the compiler") {
val text="""
val text = """
%import floats
main {
@@ -532,7 +533,7 @@ main {
}
test("byte when choices silently converted to word for convenience") {
val text="""
val text = """
main {
sub start() {
uword z = 3
@@ -547,7 +548,7 @@ main {
}
test("returning smaller dt than returndt is ok") {
val text="""
val text = """
main {
sub start() {
void test()
@@ -561,7 +562,7 @@ main {
}
test("returning bigger dt than returndt is not ok") {
val text="""
val text = """
main {
sub start() {
void test()
@@ -571,13 +572,13 @@ main {
return cx16.r0
}
}"""
val errors=ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = false, errors=errors) shouldBe null
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.single() shouldContain "doesn't match"
}
test("long type okay in const expr but otherwise overflow") {
val src="""
val src = """
main {
sub start() {
const ubyte HEIGHT=240
@@ -593,8 +594,8 @@ main {
value++
}
}"""
val errors=ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain ":9:"
errors.errors[0] shouldContain "no cast"
@@ -605,7 +606,7 @@ main {
}
test("various bool typecasts and type mismatches") {
val src="""
val src = """
%option enable_floats
main {
@@ -645,15 +646,15 @@ main {
bflags = fl as bool
}
}"""
val errors=ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 12
errors.errors.all { "type of value" in it } shouldBe true
errors.errors.all { "value type" in it } shouldBe true
errors.errors.all { "doesn't match" in it } shouldBe true
}
test("bool to byte cast in expression is not allowed") {
val text="""
val text = """
main {
sub start() {
ubyte[3] values
@@ -672,24 +673,26 @@ main {
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain("argument 1 type mismatch")
errors.errors[1] shouldContain("argument 1 type mismatch")
errors.errors[2] shouldContain("type of value bool doesn't match target")
errors.errors[3] shouldContain("type of value bool doesn't match target")
errors.errors[0] shouldContain ("argument 1 type mismatch")
errors.errors[1] shouldContain ("argument 1 type mismatch")
errors.errors[2] shouldContain ("value type bool doesn't match target")
errors.errors[3] shouldContain ("value type bool doesn't match target")
}
test("bool function parameters correct typing") {
test("bool function parameters correct typing and implicit casts to bool") {
val src = """
main {
sub start() {
bool bb = func(true)
^^word @shared pointer
bool @shared bb = func(true)
void func(true)
; all these should fail:
void func(0)
void func(1)
void func(42)
void func(65535)
void func(655.444)
void func(cx16.r0)
void func(pointer)
cx16.r0L = func(true)
}
@@ -700,38 +703,26 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 6
errors.errors[0] shouldContain("type mismatch")
errors.errors[1] shouldContain("type mismatch")
errors.errors[2] shouldContain("type mismatch")
errors.errors[3] shouldContain("type mismatch")
errors.errors[4] shouldContain("type mismatch")
errors.errors[5] shouldContain("type of value bool doesn't match target")
errors.errors.size shouldBe 1
errors.errors[0] shouldContain ("value type bool doesn't match target")
}
test("no implicit bool-to-int cast") {
val src="""
test("no implicit bool-to-int cast in assignment") {
val src = """
main {
sub start() {
func(true)
func(true as ubyte)
cx16.r0L = true
cx16.r0L = true as ubyte
}
sub func(bool b) {
cx16.r0++
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain(":5:14: argument 1 type mismatch")
errors.errors[1] shouldContain(":6:20: type of value bool doesn't match target")
errors.errors.size shouldBe 1
errors.errors[0] shouldContain ("value type bool doesn't match target")
}
test("no implicit int-to-bool cast") {
val src="""
val src = """
main {
sub start() {
func1(true)
@@ -755,14 +746,14 @@ main {
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain(":4:15: no implicit cast")
errors.errors[1] shouldContain(":5:15: no implicit cast")
errors.errors[2] shouldContain(":8:28: type of value ubyte doesn't match target")
errors.errors[3] shouldContain(":9:28: type of value uword doesn't match target")
errors.errors[0] shouldContain (":4:15: no implicit cast")
errors.errors[1] shouldContain (":5:15: no implicit cast")
errors.errors[2] shouldContain (":8:28: value type ubyte doesn't match target")
errors.errors[3] shouldContain (":9:28: value type uword doesn't match target")
}
test("str replaced with uword in subroutine params and return types") {
val src="""
val src = """
main {
sub start() {
derp("hello")
@@ -785,16 +776,16 @@ main {
compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
val main = result.codegenAst!!.allBlocks().first()
val derp = main.children.single { it is PtSub && it.name=="main.derp"} as PtSub
derp.returns shouldBe listOf(DataType.UWORD)
derp.parameters.single().type shouldBe DataType.UWORD
val mult3 = main.children.single { it is PtAsmSub && it.name=="main.mult3"} as PtAsmSub
val derp = main.children.single { it is PtSub && it.name == "main.derp" } as PtSub
derp.signature.returns shouldBe listOf(DataType.UWORD)
(derp.signature.children.single() as PtSubroutineParameter).type shouldBe DataType.UWORD
val mult3 = main.children.single { it is PtAsmSub && it.name == "main.mult3" } as PtAsmSub
mult3.parameters.single().second.type shouldBe DataType.UWORD
mult3.returns.single().second shouldBe DataType.UWORD
}
test("return 0 for str converted to uword") {
val src="""
val src = """
main {
sub start() {
cx16.r0 = test()
@@ -812,7 +803,7 @@ main {
}
test("bool to word cast") {
val src="""
val src = """
main {
sub start() {
bool @shared flag, flag2
@@ -826,7 +817,7 @@ main {
}
test("undefined symbol error instead of type cast error") {
val src="""
val src = """
main {
const ubyte foo = 0
ubyte bar = 0
@@ -869,12 +860,12 @@ main {
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "17:16: return value's type ubyte doesn't match subroutine's return type byte"
errors.errors[1] shouldContain "20:16: return value's type uword doesn't match subroutine's return type word"
errors.errors[0] shouldContain "17:16: return value type ubyte doesn't match subroutine return type byte"
errors.errors[1] shouldContain "20:16: return value type uword doesn't match subroutine return type word"
}
test("if-expression adjusts different value types to common type") {
val src="""
val src = """
main {
sub start() {
cx16.r0sL = if cx16.r0L < cx16.r1L -1 else 1
@@ -912,29 +903,29 @@ main {
st.size shouldBe 7
val v1 = (st[2] as Assignment).value as BinaryExpression
v1.operator shouldBe "+"
(v1.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0")
(v1.left as IdentifierReference).nameInSource shouldBe listOf("cx16", "r0")
(v1.right as NumericLiteral).type shouldBe BaseDataType.UWORD
(v1.right as NumericLiteral).number shouldBe 39
val v2 = (st[3] as Assignment).value as BinaryExpression
v2.operator shouldBe "+"
(v2.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0")
(v2.left as IdentifierReference).nameInSource shouldBe listOf("cx16", "r0")
(v2.right as NumericLiteral).type shouldBe BaseDataType.UWORD
(v2.right as NumericLiteral).number shouldBe 399
val v3 = (st[4] as Assignment).value as TypecastExpression
v3.type shouldBe BaseDataType.UWORD
v3.type shouldBe DataType.UWORD
val v3e = v3.expression as BinaryExpression
v3e.operator shouldBe "*"
(v3e.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0L")
(v3e.left as IdentifierReference).nameInSource shouldBe listOf("cx16", "r0L")
(v3e.right as NumericLiteral).type shouldBe BaseDataType.UBYTE
(v3e.right as NumericLiteral).number shouldBe 5
val v4 = (st[5] as Assignment).value as BinaryExpression
v4.operator shouldBe "*"
val v4t = v4.left as TypecastExpression
v4t.type shouldBe BaseDataType.UWORD
(v4t.expression as IdentifierReference).nameInSource shouldBe listOf("cx16","r0L")
v4t.type shouldBe DataType.UWORD
(v4t.expression as IdentifierReference).nameInSource shouldBe listOf("cx16", "r0L")
(v4.right as NumericLiteral).type shouldBe BaseDataType.UWORD
(v4.right as NumericLiteral).number shouldBe 5
}
@@ -952,6 +943,20 @@ main {
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("common subtypes") {
BinaryExpression.commonDatatype(DataType.WORD, DataType.UWORD, null, null).first shouldBe DataType.WORD
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.UBYTE, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.UWORD, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.BYTE, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.WORD, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.UBYTE, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.UWORD, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.BYTE, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.WORD, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
}
test("bitwise operator on signed values") {
val src = """
main {

View File

@@ -57,8 +57,8 @@ class TestAstChecks: FunSpec({
compileText(C64Target(), true, text, outputDir, writeAssembly = true, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain ":7:28: invalid assignment value, maybe forgot '&'"
errors.errors[1] shouldContain ":8:28: invalid assignment value, maybe forgot '&'"
errors.errors[0] shouldContain ":7:28: invalid assignment value"
errors.errors[1] shouldContain ":8:28: invalid assignment value"
}
test("can't do str or array expression without using address-of") {
@@ -124,16 +124,17 @@ class TestAstChecks: FunSpec({
main {
sub start() {
&ubyte a = 10000
uword @shared z = 500
a[4] = (z % 3) as ubyte
cx16.r0L = a[4]
a[4] = cx16.r1L
}
}
"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), true, text, outputDir, writeAssembly = true, errors=errors)
errors.errors.size shouldBe 1
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "indexing requires"
errors.errors[1] shouldContain "indexing requires"
}
test("unicode in identifier names is working") {
@@ -480,7 +481,7 @@ main {
val errors = ErrorReporterForTests()
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "type of value uword doesn't match target str"
errors.errors[0] shouldContain "value type uword doesn't match target type str"
errors.errors[1] shouldContain "string var must be initialized with a string literal"
errors.errors[2] shouldContain "string var must be initialized with a string literal"
}

View File

@@ -60,14 +60,14 @@ class TestIdentifierRef: FunSpec({
val mstmts = (module.statements.single() as Block).statements
val stmts = mstmts.filterIsInstance<Subroutine>().single().statements
val wwref = (stmts[0] as Assignment).target.identifier!!
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier!!
wwref.nameInSource shouldBe listOf("ww")
wwref.wasStringLiteral() shouldBe false
wwref.targetStatement(program) shouldBe instanceOf<VarDecl>()
wwref.targetStatement() shouldBe instanceOf<VarDecl>()
wwref.targetVarDecl()!!.name shouldBe "ww"
wwref.targetVarDecl()!!.parent shouldBe instanceOf<Block>()
mainref.nameInSource shouldBe listOf("main")
mainref.wasStringLiteral() shouldBe false
mainref.targetStatement(program) shouldBe instanceOf<Block>()
mainref.targetStatement() shouldBe instanceOf<Block>()
}
})

View File

@@ -328,7 +328,7 @@ class TestProg8Parser: FunSpec( {
val mpf = module.position.file
val targetDirective = module.statements.filterIsInstance<Directive>()[0]
assertPositionOf(targetDirective, mpf, 1, 1, 9)
assertPositionOf(targetDirective, mpf, 1, 1, 8)
val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 2, 1, 4)
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
@@ -1073,11 +1073,22 @@ main {
word @shared w
const long ll = 9999999
ubyte @shared ub1, ub2
struct List {
bool b
word w
float f
^^List next
}
^^List @shared lptr
^^float @shared fptr
ub1 = sys.SIZEOF_BOOL
ub2 = sys.SIZEOF_WORD
ub1 = sys.SIZEOF_LONG
ub2 = sys.SIZEOF_FLOAT
ub1 = sys.SIZEOF_POINTER
ub2 = sys.SIZEOF_UBYTE
ub1 = sizeof(true)
ub2 = sizeof(1234)
@@ -1088,18 +1099,30 @@ main {
ub2 = sizeof(w)
ub1 = sizeof(ll)
ub2 = sizeof(f)
ub1 = sizeof(lptr)
ub2 = sizeof(fptr)
ub1 = sizeof(lptr^^)
ub2 = sizeof(fptr^^)
ub1 = sizeof(bool)
ub2 = sizeof(word)
ub1 = sizeof(long)
ub2 = sizeof(float)
ub1 = sizeof(List)
ub2 = sizeof(main.start.List)
ub1 = sizeof(&w)
;; TODO ub1 = sizeof(^^float)
;; TODO ub2 = sizeof(^^List)
}
}"""
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 27
val assignments = st.dropLast(1).takeLast(16)
st.size shouldBe 41
val assignments = st.drop(14).dropLast(1)
assignments.all { it is Assignment } shouldBe true
assignments.forEach { a ->
(a as Assignment).value shouldBe instanceOf<NumericLiteral>()
}

View File

@@ -73,7 +73,7 @@ class TestSubroutines: FunSpec({
func.statements.isEmpty() shouldBe true
}
test("cannot call a subroutine via pointer") {
test("cannot call a subroutine via a pointer") {
val src="""
main {
sub start() {
@@ -148,11 +148,11 @@ main {
val result = compileText(Cx16Target(), false, src, outputDir, errors, true)!!
errors.errors.size shouldBe 0
val start = result.codegenAst!!.entrypoint()!!
start.children.size shouldBe 8
val a1_1 = start.children[3] as PtAssignment
val a1_2 = start.children[4] as PtAssignment
val a1_3 = start.children[5] as PtAssignment
val a1_4 = start.children[6] as PtAssignment
start.children.size shouldBe 9
val a1_1 = start.children[4] as PtAssignment
val a1_2 = start.children[5] as PtAssignment
val a1_3 = start.children[6] as PtAssignment
val a1_4 = start.children[7] as PtAssignment
a1_1.multiTarget shouldBe true
a1_2.multiTarget shouldBe true
a1_3.multiTarget shouldBe true
@@ -198,11 +198,11 @@ main {
val result = compileText(Cx16Target(), false, src, outputDir, errors, true)!!
errors.errors.size shouldBe 0
val start = result.codegenAst!!.entrypoint()!!
start.children.size shouldBe 9
val a1_1 = start.children[4] as PtAssignment
val a1_2 = start.children[5] as PtAssignment
val a1_3 = start.children[6] as PtAssignment
val a1_4 = start.children[7] as PtAssignment
start.children.size shouldBe 10
val a1_1 = start.children[5] as PtAssignment
val a1_2 = start.children[6] as PtAssignment
val a1_3 = start.children[7] as PtAssignment
val a1_4 = start.children[8] as PtAssignment
a1_1.multiTarget shouldBe true
a1_2.multiTarget shouldBe true
a1_3.multiTarget shouldBe true

View File

@@ -221,8 +221,8 @@ main {
(rept1.value as StringLiteral).value shouldBe "reprepreprep"
val name2strcopy = stmts[3] as IFunctionCall
val rept2strcopy = stmts[4] as IFunctionCall
val name2 = name2strcopy.args.first() as IdentifierReference
val rept2 = rept2strcopy.args.first() as IdentifierReference
val name2 = (name2strcopy.args.first() as AddressOf).identifier!!
val rept2 = (rept2strcopy.args.first() as AddressOf).identifier!!
(name2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xx1xx2"
(rept2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
}
@@ -820,7 +820,7 @@ main {
val assignVaddr = (st[7] as Assignment).value as FunctionCallExpression
assignVaddr.target.nameInSource shouldBe listOf("mkword")
val tc = assignVaddr.args[0] as TypecastExpression
tc.type shouldBe BaseDataType.UBYTE
tc.type shouldBe DataType.UBYTE
tc.expression shouldBe instanceOf<ArrayIndexedExpression>()
}
@@ -918,10 +918,10 @@ main {
val ast2 = result.codegenAst!!
val st2 = ast2.entrypoint()!!.children
st2.size shouldBe 3
(st2[0] as PtVariable).name shouldBe "p8v_variable"
(st2[1] as PtVariable).name shouldBe "p8v_names"
val array2 = (st2[1] as PtVariable).value as PtArray
st2.size shouldBe 4
(st2[1] as PtVariable).name shouldBe "p8v_variable"
(st2[2] as PtVariable).name shouldBe "p8v_names"
val array2 = (st2[2] as PtVariable).value as PtArray
array2.type shouldBe DataType.arrayFor(BaseDataType.UWORD, true)
}
@@ -954,25 +954,26 @@ main {
sub.scopedName shouldBe "p8b_main.p8s_test"
// check the desugaring of the defer statements
(sub.children[0] as PtVariable).name shouldBe "p8v_prog8_defers_mask"
sub.children[0] shouldBe instanceOf<PtSubSignature>()
(sub.children[1] as PtVariable).name shouldBe "p8v_prog8_defers_mask"
val firstDefer = sub.children[2] as PtAugmentedAssign
val firstDefer = sub.children[3] as PtAugmentedAssign
firstDefer.operator shouldBe "|="
firstDefer.target.identifier?.name shouldBe "p8b_main.p8s_test.p8v_prog8_defers_mask"
firstDefer.value.asConstInteger() shouldBe 4
val firstIf = sub.children[3] as PtIfElse
val firstIf = sub.children[4] as PtIfElse
val deferInIf = firstIf.ifScope.children[0] as PtAugmentedAssign
deferInIf.operator shouldBe "|="
deferInIf.target.identifier?.name shouldBe "p8b_main.p8s_test.p8v_prog8_defers_mask"
deferInIf.value.asConstInteger() shouldBe 2
val lastDefer = sub.children[5] as PtAugmentedAssign
val lastDefer = sub.children[6] as PtAugmentedAssign
lastDefer.operator shouldBe "|="
lastDefer.target.identifier?.name shouldBe "p8b_main.p8s_test.p8v_prog8_defers_mask"
lastDefer.value.asConstInteger() shouldBe 1
val ifelse = sub.children[4] as PtIfElse
val ifelse = sub.children[5] as PtIfElse
val ifscope = ifelse.ifScope.children[0] as PtNodeGroup
val ifscope_push = ifscope.children[0] as PtFunctionCall
val ifscope_defer = ifscope.children[1] as PtFunctionCall
@@ -981,10 +982,10 @@ main {
ifscope_push.name shouldBe "sys.pushw"
(ifscope_return.children.single() as PtFunctionCall).name shouldBe "sys.popw"
val ending = sub.children[6] as PtFunctionCall
val ending = sub.children[7] as PtFunctionCall
ending.name shouldBe "p8b_main.p8s_test.p8s_prog8_invoke_defers"
sub.children[7] shouldBe instanceOf<PtReturn>()
val handler = sub.children[8] as PtSub
sub.children[8] shouldBe instanceOf<PtReturn>()
val handler = sub.children[9] as PtSub
handler.name shouldBe "p8s_prog8_invoke_defers"
}
@@ -1018,23 +1019,23 @@ main {
}"""
val result1 = compileText(VMTarget(), optimize=true, src, outputDir, writeAssembly=true)!!
val st1 = result1.codegenAst!!.entrypoint()!!.children
st1.size shouldBe 5
(st1[0] as PtVariable).name shouldBe "main.start.x"
(st1[1] as PtVariable).name shouldBe "main.start.y"
(st1[2] as PtVariable).name shouldBe "main.start.z"
st1[3].children.size shouldBe 4
st1[3].children.dropLast(1).map { (it as PtAssignTarget).identifier!!.name } shouldBe listOf("main.start.x", "main.start.y", "main.start.z")
((st1[3] as PtAssignment).value as PtFunctionCall).name shouldBe "main.multi"
st1.size shouldBe 6
(st1[1] as PtVariable).name shouldBe "main.start.x"
(st1[2] as PtVariable).name shouldBe "main.start.y"
(st1[3] as PtVariable).name shouldBe "main.start.z"
st1[4].children.size shouldBe 4
st1[4].children.dropLast(1).map { (it as PtAssignTarget).identifier!!.name } shouldBe listOf("main.start.x", "main.start.y", "main.start.z")
((st1[4] as PtAssignment).value as PtFunctionCall).name shouldBe "main.multi"
val result2 = compileText(Cx16Target(), optimize=true, src, outputDir, writeAssembly=true)!!
val st2 = result2.codegenAst!!.entrypoint()!!.children
st2.size shouldBe 5
(st2[0] as PtVariable).name shouldBe "p8v_x"
(st2[1] as PtVariable).name shouldBe "p8v_y"
(st2[2] as PtVariable).name shouldBe "p8v_z"
st2[3].children.size shouldBe 4
st2[3].children.dropLast(1).map { (it as PtAssignTarget).identifier!!.name } shouldBe listOf("p8b_main.p8s_start.p8v_x", "p8b_main.p8s_start.p8v_y", "p8b_main.p8s_start.p8v_z")
((st2[3] as PtAssignment).value as PtFunctionCall).name shouldBe "p8b_main.p8s_multi"
st2.size shouldBe 6
(st2[1] as PtVariable).name shouldBe "p8v_x"
(st2[2] as PtVariable).name shouldBe "p8v_y"
(st2[3] as PtVariable).name shouldBe "p8v_z"
st2[4].children.size shouldBe 4
st2[4].children.dropLast(1).map { (it as PtAssignTarget).identifier!!.name } shouldBe listOf("p8b_main.p8s_start.p8v_x", "p8b_main.p8s_start.p8v_y", "p8b_main.p8s_start.p8v_z")
((st2[4] as PtAssignment).value as PtFunctionCall).name shouldBe "p8b_main.p8s_multi"
}
test("address-of a uword pointer with word index should not overflow") {
@@ -1130,5 +1131,31 @@ thing {
}
}
test("pointer arrays are always split") {
val src="""
%option enable_floats
main {
sub start() {
^^bool[10] @split @shared barray
^^word[10] @split @shared warray
^^float[10] @split @shared farray
^^bool[10] @shared barrayns
^^word[10] @shared warrayns
^^float[10] @shared farrayns
}
}"""
val result = compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 7
val decls = st.filterIsInstance<VarDecl>()
decls.size shouldBe 6
decls.all { it.datatype.sub!=null } shouldBe true
decls.all { it.datatype.isPointerArray } shouldBe true
decls.all { it.datatype.isPointerArray } shouldBe true
}
})

View File

@@ -294,12 +294,12 @@ main {
}"""
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = true)!!
val x = result.codegenAst!!.entrypoint()!!
x.children.size shouldBe 6
((x.children[0] as PtVariable).value as PtString).value shouldBe "xyzxyzxyz"
val array1 = (x.children[1] as PtVariable).value as PtArray
val array2 = (x.children[2] as PtVariable).value as PtArray
val array3 = (x.children[3] as PtVariable).value as PtArray
val array4 = (x.children[4] as PtVariable).value as PtArray
x.children.size shouldBe 7
((x.children[1] as PtVariable).value as PtString).value shouldBe "xyzxyzxyz"
val array1 = (x.children[2] as PtVariable).value as PtArray
val array2 = (x.children[3] as PtVariable).value as PtArray
val array3 = (x.children[4] as PtVariable).value as PtArray
val array4 = (x.children[5] as PtVariable).value as PtArray
array1.children.map { (it as PtBool).value } shouldBe listOf(true, true, true)
array2.children.map { (it as PtNumber).number } shouldBe listOf(42, 42, 42)
array3.children.map { (it as PtNumber).number } shouldBe listOf(5555, 5555, 5555)
@@ -319,10 +319,10 @@ main {
}"""
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = true)!!
val x = result.codegenAst!!.entrypoint()!!
x.children.size shouldBe 4
val array1 = (x.children[0] as PtVariable).value as PtArray
val array2 = (x.children[1] as PtVariable).value as PtArray
val array3 = (x.children[2] as PtVariable).value as PtArray
x.children.size shouldBe 5
val array1 = (x.children[1] as PtVariable).value as PtArray
val array2 = (x.children[2] as PtVariable).value as PtArray
val array3 = (x.children[3] as PtVariable).value as PtArray
array1.children.map { (it as PtNumber).number } shouldBe listOf(10, 11, 12)
array2.children.map { (it as PtNumber).number } shouldBe listOf(5000, 5001, 5002)
array3.children.map { (it as PtNumber).number } shouldBe listOf(100, 101, 102)
@@ -340,9 +340,9 @@ label:
}"""
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = true)!!
val x = result.codegenAst!!.entrypoint()!!
x.children.size shouldBe 5
val array1 = (x.children[1] as PtVariable).value as PtArray
val array2 = (x.children[2] as PtVariable).value as PtArray
x.children.size shouldBe 6
val array1 = (x.children[2] as PtVariable).value as PtArray
val array2 = (x.children[3] as PtVariable).value as PtArray
array1.children.forEach {
it shouldBe instanceOf<PtAddressOf>()
}
@@ -409,5 +409,68 @@ main {
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 0
}
test("various array indexed assignment scenarios") {
val src="""
%import floats
%import textio
%zeropage basicsafe
main {
bool[10] barray
uword[10] @nosplit warrayns
uword[10] warray
float[10] farray
sub start() {
dump()
; ALL OK
barray[2] = true
warrayns[2] = 1234
warray[2] = 5678
farray[2] = 3.1415
dump()
; ALL OK
cx16.r0L=2
barray[cx16.r0L] = false
warrayns[cx16.r0L] = 0
warray[cx16.r0L] = 0
farray[cx16.r0L] = 0
dump()
; ALL OK
cx16.r0L=2
barray[cx16.r0L] = true
warrayns[cx16.r0L] = 1234
warray[cx16.r0L] = 5678
farray[cx16.r0L] = 3.1415
dump()
; ALL OK
barray[2] = false
warrayns[2] = 0
warray[2] = 0
farray[2] = 0.0
dump()
sub dump() {
txt.print_bool(barray[2])
txt.spc()
txt.print_uw(warrayns[2])
txt.spc()
txt.print_uw(warray[2])
txt.spc()
txt.print_f(farray[2])
txt.nl()
}
}
}"""
compileText(C64Target(), optimize=true, src, outputDir) shouldNotBe null
compileText(VMTarget(), optimize=true, src, outputDir) shouldNotBe null
compileText(C64Target(), optimize=false, src, outputDir) shouldNotBe null
compileText(VMTarget(), optimize=false, src, outputDir) shouldNotBe null
}
})

View File

@@ -56,15 +56,22 @@ class TestAsmGenSymbols: StringSpec({
SplitWish.DONTCARE, null, "tgt", emptyList(), null, false, 0u, false, Position.DUMMY)
val labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val tgt = AssignTarget(
IdentifierReference(listOf("tgt"), Position.DUMMY),
null,
null,
null,
false,
position = Position.DUMMY
)
val assign1 = Assignment(tgt, IdentifierReference(listOf("localvar"), Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), null, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign2 = Assignment(tgt, AddressOf(IdentifierReference(listOf("locallabel"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign3 = Assignment(tgt, AddressOf(IdentifierReference(listOf("var_outside"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign4 = Assignment(tgt, AddressOf(IdentifierReference(listOf("label_outside"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign5 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","localvar"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign6 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","start","locallabel"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign7 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","var_outside"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), null, null, false, false, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
@@ -111,14 +118,13 @@ class TestAsmGenSymbols: StringSpec({
val localvarIdent = sub.children.asSequence().filterIsInstance<PtAssignment>().first { it.value is PtIdentifier }.value as PtIdentifier
asmgen.asmSymbolName(localvarIdent) shouldBe "localvar"
asmgen.asmVariableName(localvarIdent) shouldBe "localvar"
val localvarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.localvar" }.value as PtAddressOf).identifier
val localvarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.localvar" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(localvarIdentScoped) shouldBe "localvar"
asmgen.asmVariableName(localvarIdentScoped) shouldBe "localvar"
val scopedVarIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier
val scopedVarIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedVarIdent) shouldBe "main.var_outside"
asmgen.asmVariableName(scopedVarIdent) shouldBe "main.var_outside"
val scopedVarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier
val scopedVarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedVarIdentScoped) shouldBe "main.var_outside"
asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside"
}
@@ -128,17 +134,17 @@ class TestAsmGenSymbols: StringSpec({
val asmgen = createTestAsmGen6502(program)
val sub = asmgen.program.entrypoint()!!
val localLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier
val localLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(localLabelIdent) shouldBe "locallabel"
asmgen.asmVariableName(localLabelIdent) shouldBe "locallabel"
val localLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier
val localLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.locallabel" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(localLabelIdentScoped) shouldBe "locallabel"
asmgen.asmVariableName(localLabelIdentScoped) shouldBe "locallabel"
val scopedLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier
val scopedLabelIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedLabelIdent) shouldBe "main.label_outside"
asmgen.asmVariableName(scopedLabelIdent) shouldBe "main.label_outside"
val scopedLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier
val scopedLabelIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.label_outside" }.value as PtAddressOf).identifier!!
asmgen.asmSymbolName(scopedLabelIdentScoped) shouldBe "main.label_outside"
asmgen.asmVariableName(scopedLabelIdentScoped) shouldBe "main.label_outside"
}

View File

@@ -100,8 +100,8 @@ class TestVariables: FunSpec({
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "value has incompatible type"
errors.errors[1] shouldContain "value has incompatible type"
errors.errors[0] shouldContain "undefined array type"
errors.errors[1] shouldContain "undefined array type"
}
test("global var init with array lookup should sometimes be const") {

View File

@@ -74,11 +74,11 @@ main {
val start = ast.entrypoint()!!
start.name shouldBe "p8s_start"
start.children.size shouldBeGreaterThan 2
val seed = start.children[0] as PtVariable
val seed = start.children[1] as PtVariable
seed.name shouldBe "p8v_seed"
seed.value shouldBe null
seed.type shouldBe DataType.arrayFor(BaseDataType.UWORD)
val assign = start.children[1] as PtAssignment
val assign = start.children[2] as PtAssignment
assign.target.identifier!!.name shouldBe "cx16.r0"
assign.value shouldBe instanceOf<PtBinaryExpression>()
}
@@ -131,7 +131,7 @@ main {
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors = errors)
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "isn't uword"
errors.errors[0] shouldContain "invalid address type"
errors.errors[1] shouldContain "undefined symbol: doesnotexist"
}
@@ -535,8 +535,8 @@ main {
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
val st = result.codegenAst!!.entrypoint()!!.children
st.size shouldBe 2
val ifelse = st[0] as PtIfElse
st.size shouldBe 3
val ifelse = st[1] as PtIfElse
ifelse.hasElse() shouldBe false
(ifelse.condition as PtPrefix).operator shouldBe "not"
}
@@ -559,8 +559,8 @@ main {
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
val st = result.codegenAst!!.entrypoint()!!.children
st.size shouldBe 2
val ifelse = st[0] as PtIfElse
st.size shouldBe 3
val ifelse = st[1] as PtIfElse
ifelse.hasElse() shouldBe true
ifelse.condition shouldBe instanceOf<PtFunctionCall>()
}
@@ -615,4 +615,19 @@ main {
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("word bitshift with byte operand") {
val src="""
main{
sub start() {
cx16.r0 >>= 4
cx16.r1 <<= 4
}
}"""
compileText(C64Target(), false, src, outputDir) shouldNotBe null
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
compileText(C64Target(), true, src, outputDir) shouldNotBe null
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
}
})

View File

@@ -16,12 +16,14 @@ internal object DummyFunctions : IBuiltinFunctions {
position: Position,
): NumericLiteral? = null
override fun returnType(funcName: String) = InferredTypes.InferredType.unknown()
override fun returnType(funcName: String) = InferredTypes.unknown()
}
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray || dt.isSplitWordArray) {
if(dt.isPointerArray)
return 2 * numElements!!
else if(dt.isArray || dt.isSplitWordArray) {
require(numElements!=null)
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements

View File

@@ -405,7 +405,7 @@ main {
}"""
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
val start = result.codegenAst!!.entrypoint()!!
start.children.size shouldBe 11
start.children.size shouldBe 12
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUW(0xff02) shouldBe 3837u // $ff02 = cx16.r0
@@ -458,7 +458,7 @@ main {
}"""
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
val start = result.codegenAst!!.entrypoint()!!
start.children.size shouldBe 22
start.children.size shouldBe 23
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUW(0xff02) shouldBe 3837u // $ff02 = cx16.r0

View File

@@ -92,14 +92,15 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
override fun visit(expr: BinaryExpression) {
output("(")
val isValue = expr.parent is Assignment
if(!isValue) output("(")
expr.left.accept(this)
if(expr.operator.any { it.isLetter() })
output(" ${expr.operator} ")
else
output(expr.operator)
expr.right.accept(this)
output(")")
if(!isValue) output(")")
}
override fun visit(directive: Directive) {
@@ -155,6 +156,14 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
}
override fun visit(struct: StructDecl) {
outputln("struct ${struct.name} {")
for(member in struct.fields) {
outputlni( " ${member.first} ${member.second}")
}
outputlni("}")
}
override fun visit(subroutine: Subroutine) {
output("\n")
outputi("")
@@ -344,9 +353,9 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
override fun visit(assignment: Assignment) {
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null && binExpr.left isSameAs assignment.target
&& binExpr.operator !in ComparisonOperators) {
if(binExpr!=null && binExpr.left isSameAs assignment.target && binExpr.operator !in ComparisonOperators) {
// we only support the inplace assignments of the form A = A <operator> <value>
// don't use assignment.isAugmentable here! That one is a more general check, and not suitable for printing the AST like here
assignment.target.accept(this)
output(" ${binExpr.operator}= ")
binExpr.right.accept(this)
@@ -411,7 +420,8 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
arrayIndexedExpression.arrayvar.accept(this)
arrayIndexedExpression.plainarrayvar?.accept(this)
arrayIndexedExpression.pointerderef?.accept(this)
output("[")
arrayIndexedExpression.indexer.indexExpr.accept(this)
output("]")
@@ -424,6 +434,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
assignTarget.memoryAddress?.accept(this)
assignTarget.identifier?.accept(this)
assignTarget.arrayindexed?.accept(this)
assignTarget.pointerDereference?.accept(this)
val multi = assignTarget.multi
if (multi != null) {
multi.dropLast(1).forEach { target ->
@@ -454,7 +465,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
override fun visit(typecast: TypecastExpression) {
output("(")
typecast.expression.accept(this)
output(" as ${DataType.forDt(typecast.type).sourceString()}) ")
output(" as ${typecast.type}) ")
}
override fun visit(memread: DirectMemoryRead) {
@@ -473,12 +484,13 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
output("&")
if(addressOf.msb)
output(">")
addressOf.identifier.accept(this)
if(addressOf.arrayIndex!=null) {
addressOf.identifier?.accept(this)
if (addressOf.arrayIndex != null) {
output("[")
addressOf.arrayIndex?.accept(this)
output("]")
}
addressOf.dereference?.accept(this)
}
override fun visit(inlineAssembly: InlineAssembly) {
@@ -536,4 +548,18 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
onGoto.elsepart.accept(this)
}
}
override fun visit(deref: PtrDereference) {
output(deref.chain.joinToString("^^."))
if(deref.derefLast)
output("^^")
}
override fun visit(field: StructFieldRef) {
throw FatalAstException("struct field ref shouldn't occur as part of the AST tree ")
}
override fun visit(deref: ArrayIndexedPtrDereference) {
output("???? Array Indexed Ptr Dereference should have been converted to other AST nodes ????")
}
}

View File

@@ -138,20 +138,18 @@ interface IStatementContainer {
}
fun hasReturnStatement(): Boolean {
fun hasReturnStatement(stmt: Statement): Boolean {
when(stmt) {
is AnonymousScope -> return stmt.statements.any { hasReturnStatement(it) }
is ForLoop -> return stmt.body.hasReturnStatement()
is IfElse -> return stmt.truepart.hasReturnStatement() || stmt.elsepart.hasReturnStatement()
is WhileLoop -> return stmt.body.hasReturnStatement()
is RepeatLoop -> return stmt.body.hasReturnStatement()
is UntilLoop -> return stmt.body.hasReturnStatement()
is When -> return stmt.choices.any { it.statements.hasReturnStatement() }
is ConditionalBranch -> return stmt.truepart.hasReturnStatement() || stmt.elsepart.hasReturnStatement()
is UnrollLoop -> return stmt.body.hasReturnStatement()
is Return -> return true
else -> return false
}
fun hasReturnStatement(stmt: Statement): Boolean = when(stmt) {
is AnonymousScope -> stmt.statements.any { hasReturnStatement(it) }
is ForLoop -> stmt.body.hasReturnStatement()
is IfElse -> stmt.truepart.hasReturnStatement() || stmt.elsepart.hasReturnStatement()
is WhileLoop -> stmt.body.hasReturnStatement()
is RepeatLoop -> stmt.body.hasReturnStatement()
is UntilLoop -> stmt.body.hasReturnStatement()
is When -> stmt.choices.any { it.statements.hasReturnStatement() }
is ConditionalBranch -> stmt.truepart.hasReturnStatement() || stmt.elsepart.hasReturnStatement()
is UnrollLoop -> stmt.body.hasReturnStatement()
is Return -> true
else -> false
}
return statements.any { hasReturnStatement(it) }
@@ -175,28 +173,24 @@ interface INameScope: IStatementContainer, INamedStatement {
}
private fun lookupQualified(scopedName: List<String>): Statement? {
// a scoped name refers to a name in another namespace, and stars from the root.
val localSymbol = this.searchSymbol(scopedName[0]) ?: this.lookupUnqualified(scopedName[0])
val fieldRef = searchStructFieldRef(localSymbol, scopedName.drop(1))
if(fieldRef!=null)
return fieldRef
// experimental code to be able to alias blocks too:
// val stmt = this.lookup(listOf(scopedName[0])) ?: return null
// if(stmt is Alias) {
// val block = this.lookup(stmt.target.nameInSource) ?: return null
// var statement = block as Statement?
// for(name in scopedName.drop(1)) {
// statement = (statement as? IStatementContainer)?.searchSymbol(name)
// if(statement==null)
// return null
// }
// return statement
// }
// a scoped name refers to a name in another namespace, and always starts from the root.
for(module in (this as Node).definingModule.program.modules) {
val block = module.searchSymbol(scopedName[0])
if(block!=null) {
var statement = block
for(name in scopedName.drop(1)) {
statement = (statement as? IStatementContainer)?.searchSymbol(name)
if(statement==null)
return null
for((idx, name) in scopedName.drop(1).withIndex()) {
val symbol = (statement as? IStatementContainer)?.searchSymbol(name)
if(symbol==null) {
val fieldRef = searchStructFieldRef(statement, scopedName.drop(idx+1))
if(fieldRef!=null)
return fieldRef
}
statement = symbol
}
return statement
}
@@ -204,6 +198,37 @@ interface INameScope: IStatementContainer, INamedStatement {
return null
}
fun searchStructFieldRef(localSymbol: Statement?, scopedName: List<String>): StructFieldRef? {
if(localSymbol is VarDecl && localSymbol.datatype.isPointer) {
var struct = localSymbol.datatype.subType as? StructDecl
if(struct==null && localSymbol.datatype.subTypeFromAntlr!=null) {
// the antlr-injected type ref wasn't yet translated, do the lookup here
struct = localSymbol.definingScope.lookup(localSymbol.datatype.subTypeFromAntlr!!) as? StructDecl
}
if(struct!=null) {
for ((idx, field) in scopedName.withIndex()) {
val fieldDt = struct!!.getFieldType(field) ?:
return null
if (idx == scopedName.size - 1) {
// was last path element
val pointer = IdentifierReference(scopedName, Position.DUMMY)
val ref = StructFieldRef(pointer, struct, fieldDt, field, Position.DUMMY)
ref.linkParents(this as Node)
return ref
}
struct = fieldDt.subType as? StructDecl
if(struct==null && fieldDt.subTypeFromAntlr!=null) {
// the antlr-injected type ref wasn't yet translated, do the lookup here
struct = localSymbol.definingScope.lookup(fieldDt.subTypeFromAntlr!!) as? StructDecl
}
if(struct==null)
return null
}
}
}
return null
}
private fun lookupUnqualified(name: String): Statement? {
val builtinFunctionsNames = (this as Node).definingModule.program.builtinFunctions.names
if(name in builtinFunctionsNames) {
@@ -403,6 +428,7 @@ fun defaultZero(dt: BaseDataType, position: Position) = when(dt) {
BaseDataType.UWORD, BaseDataType.STR -> NumericLiteral(BaseDataType.UWORD, 0.0, position)
BaseDataType.WORD -> NumericLiteral(BaseDataType.WORD, 0.0, position)
BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, 0.0, position)
BaseDataType.POINTER -> NumericLiteral(BaseDataType.UWORD, 0.0, position)
else -> throw FatalAstException("can only determine default zero value for a numeric type")
}

View File

@@ -34,8 +34,8 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitExpression(ctx: ExpressionContext): Expression {
if(ctx.sizeof_expression!=null) {
val sdt = ctx.sizeof_argument().datatype()
val datatype = if(sdt!=null) baseDatatypeFor(sdt) else null
val sdt = ctx.sizeof_argument().basedatatype()
val datatype = baseDatatypeFor(sdt)
val expression = ctx.sizeof_argument().expression()?.accept(this) as Expression?
val sizeof = IdentifierReference(listOf("sizeof"), ctx.toPosition())
val arg = if (expression != null) expression else {
@@ -69,9 +69,8 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
if(ctx.typecast()!=null) {
// typecast is always to a base datatype
val baseDt = baseDatatypeFor(ctx.typecast().datatype())
return TypecastExpression(ctx.expression(0).accept(this) as Expression, baseDt, false, ctx.toPosition())
val dt = dataTypeFor(ctx.typecast().datatype())!!
return TypecastExpression(ctx.expression(0).accept(this) as Expression, dt, false, ctx.toPosition())
}
if(ctx.childCount==3 && ctx.children[0].text=="(" && ctx.children[2].text==")")
@@ -110,13 +109,15 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
override fun visitDirective(ctx: DirectiveContext): Directive {
val pos = ctx.toPosition()
val position = Position(pos.file, pos.line, pos.startCol, ctx.directivename().UNICODEDNAME().symbol.stopIndex)
if(ctx.directivenamelist() != null) {
val namelist = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
val identifiers = namelist.map { DirectiveArg(it.nameInSource.joinToString("."), null, ctx.toPosition()) }
return Directive(ctx.directivename.text, identifiers, ctx.toPosition())
val identifiers = namelist.map { DirectiveArg(it.nameInSource.joinToString("."), null, it.position) }
return Directive(ctx.directivename().text, identifiers, position)
}
else
return Directive(ctx.directivename.text, ctx.directivearg().map { it.accept(this) as DirectiveArg }, ctx.toPosition())
return Directive(ctx.directivename().text, ctx.directivearg().map { it.accept(this) as DirectiveArg }, position)
}
override fun visitDirectivearg(ctx: DirectiveargContext): DirectiveArg {
@@ -128,7 +129,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
return DirectiveArg(str.text.substring(1, str.text.length-1), integer, ctx.toPosition())
}
val identifier = ctx.identifier()?.accept(this) as IdentifierReference?
return DirectiveArg(identifier?.nameInSource?.single(), integer, ctx.toPosition())
return DirectiveArg(identifier?.nameInSource?.single(), integer, identifier?.position ?: ctx.toPosition())
}
override fun visitVardecl(ctx: VardeclContext): VarDecl {
@@ -146,21 +147,34 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
if(alignpage && alignword)
throw SyntaxError("choose a single alignment option", ctx.toPosition())
val identifiers = ctx.identifier().map { getname(it) }
val identifiers = ctx.identifierlist().identifier().map { getname(it) }
val identifiername = identifiers[0]
val name = if(identifiers.size==1) identifiername else "<multiple>"
val arrayIndex = ctx.arrayindex()?.accept(this) as ArrayIndex?
val isArray = ctx.ARRAYSIG() != null || arrayIndex != null
val baseDt = baseDatatypeFor(ctx.datatype())
val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt)
val baseDt = dataTypeFor(ctx.datatype()) ?: DataType.UNDEFINED
val dt = if(!isArray) baseDt else {
if(baseDt.isPointer)
DataType.arrayOfPointersFromAntlrTo(baseDt.sub, baseDt.subTypeFromAntlr)
else if(baseDt.isStructInstance)
throw SyntaxError("array of structures not allowed (use array of pointers)", ctx.toPosition())
else
DataType.arrayFor(baseDt.base, split!=SplitWish.NOSPLIT)
}
val splitWords = if(split==SplitWish.DONTCARE) {
if(dt.isPointerArray) SplitWish.SPLIT // pointer arrays are always @split by default
else split
} else split
return VarDecl(
VarDeclType.VAR, // can be changed to MEMORY or CONST as required
VarDeclOrigin.USERCODE,
dt,
zp,
split,
splitWords,
arrayIndex,
name,
if(identifiers.size==1) emptyList() else identifiers,
@@ -217,30 +231,30 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitIdentifierTarget(ctx: IdentifierTargetContext): AssignTarget {
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
return AssignTarget(identifier, null, null, null, false, ctx.toPosition())
return AssignTarget(identifier, null, null, null, false, position=ctx.toPosition())
}
override fun visitArrayindexedTarget(ctx: ArrayindexedTargetContext): AssignTarget {
val ax = ctx.arrayindexed()
val arrayvar = ax.scoped_identifier().accept(this) as IdentifierReference
val index = ax.arrayindex().accept(this) as ArrayIndex
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
return AssignTarget(null, arrayindexed, null, null, false, ctx.toPosition())
val arrayindexed = ArrayIndexedExpression(arrayvar, null, index, ax.toPosition())
return AssignTarget(null, arrayindexed, null, null, false, position=ctx.toPosition())
}
override fun visitMemoryTarget(ctx: MemoryTargetContext): AssignTarget {
return AssignTarget(null, null,
DirectMemoryWrite(ctx.directmemory().expression().accept(this) as Expression, ctx.toPosition()),
null, false, ctx.toPosition())
null, false, position=ctx.toPosition())
}
override fun visitVoidTarget(ctx: VoidTargetContext): AssignTarget {
return AssignTarget(null, null, null, null, true, ctx.toPosition())
return AssignTarget(null, null, null, null, true, position=ctx.toPosition())
}
override fun visitMulti_assign_target(ctx: Multi_assign_targetContext): AssignTarget {
val targets = ctx.assign_target().map { it.accept(this) as AssignTarget }
return AssignTarget(null, null, null, targets, false, ctx.toPosition())
return AssignTarget(null, null, null, targets, false, position=ctx.toPosition())
}
override fun visitPostincrdecr(ctx: PostincrdecrContext): Assignment {
@@ -254,7 +268,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitArrayindexed(ctx: ArrayindexedContext): ArrayIndexedExpression {
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
val index = ctx.arrayindex().accept(this) as ArrayIndex
return ArrayIndexedExpression(identifier, index, ctx.toPosition())
return ArrayIndexedExpression(identifier, null, index, ctx.toPosition())
}
override fun visitDirectmemory(ctx: DirectmemoryContext): DirectMemoryRead {
@@ -262,16 +276,21 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
override fun visitAddressof(ctx: AddressofContext): AddressOf {
val identifier = ctx.scoped_identifier()?.accept(this) as IdentifierReference?
val identifier = ctx.scoped_identifier().accept(this) as IdentifierReference
val msb = ctx.ADDRESS_OF_MSB()!=null
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
return if (identifier != null)
AddressOf(identifier, null, msb, ctx.toPosition())
else {
val array = ctx.arrayindexed()
AddressOf(array.scoped_identifier().accept(this) as IdentifierReference,
array.arrayindex().accept(this) as ArrayIndex,
msb, ctx.toPosition())
val index = ctx.arrayindex()?.accept(this) as? ArrayIndex
var typed = false
if(ctx.TYPED_ADDRESS_OF()!=null) {
// new typed AddressOf
if(msb)
throw SyntaxError("typed address of not allowed with msb", ctx.toPosition())
typed = true
}
return if (index != null) {
AddressOf(identifier, index, null, msb, typed, ctx.toPosition())
} else {
AddressOf(identifier, null, null, msb, typed, ctx.toPosition())
}
}
@@ -427,19 +446,20 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
override fun visitInlineasm(ctx: InlineasmContext): InlineAssembly {
val type = ctx.directivename().UNICODEDNAME().text
val isIR = when(type) {
"asm" -> false
"ir" -> true
else -> throw SyntaxError("unknown inline asm type $type", ctx.toPosition())
}
val text = ctx.INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), false, ctx.toPosition())
}
override fun visitInlineir(ctx: InlineirContext): InlineAssembly {
val text = ctx.INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), true, ctx.toPosition())
return InlineAssembly(text.substring(2, text.length-2), isIR, ctx.toPosition())
}
override fun visitSubroutine(ctx: SubroutineContext): Subroutine {
val name = getname(ctx.identifier())
val parameters = ctx.sub_params()?.sub_param()?.map { it.accept(this) as SubroutineParameter } ?: emptyList()
val returntypes = ctx.sub_return_part()?.datatype()?. map { dataTypeFor(it) } ?: emptyList()
val returntypes = ctx.sub_return_part()?.datatype()?. map { dataTypeFor(it)!! } ?: emptyList()
val statements = ctx.statement_block().accept(this) as AnonymousScope
return Subroutine(
name,
@@ -470,12 +490,11 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
throw SyntaxError("invalid parameter tag '$tag'", pctx.toPosition())
}
val zp = getZpOption(tags)
val decldt = decl.datatype()
var datatype = if(decldt!=null) dataTypeFor(decldt) else DataType.UNDEFINED
var datatype = dataTypeFor(decl.datatype()) ?: DataType.UNDEFINED
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
datatype = datatype.elementToArray()
val identifiers = decl.identifier()
val identifiers = decl.identifierlist().identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
val identifiername = getname(identifiers[0])
@@ -588,6 +607,49 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
return OnGoto(isCall, index, labels, elsepart, ctx.toPosition())
}
override fun visitPointerDereferenceTarget(ctx: PointerDereferenceTargetContext): AssignTarget {
val deref = ctx.pointerdereference().accept(this)
return if(deref is PtrDereference)
AssignTarget(null, null, null, null, false, deref, null, deref.position)
else if(deref is ArrayIndexedPtrDereference)
AssignTarget(null, null, null, null, false, null, deref, deref.position)
else
throw FatalAstException("weird dereference ${ctx.toPosition()}")
}
override fun visitPointerdereference(ctx: PointerdereferenceContext): Expression {
val scopeprefix = ctx.prefix?.accept(this) as IdentifierReference?
val derefs = ctx.derefchain()!!.singlederef()!!.map { it.identifier().text to it.arrayindex()?.accept(this) as ArrayIndex? }
if(derefs.all { it.second==null }) {
val derefchain = derefs.map { it.first }
val chain = ((scopeprefix?.nameInSource ?: emptyList()) + derefchain).toMutableList()
if (ctx.field != null)
chain += ctx.field.text
return PtrDereference(chain, ctx.field == null, ctx.toPosition())
} else {
val chain = derefs.toMutableList()
if(scopeprefix!=null) {
chain.addAll(0, scopeprefix.nameInSource.map { it to (null as ArrayIndex?) }.toMutableList())
}
if (ctx.field != null)
chain += ctx.field.text to null
return ArrayIndexedPtrDereference(chain, ctx.field == null, ctx.toPosition())
}
}
override fun visitStructdeclaration(ctx: StructdeclarationContext): StructDecl {
val name = getname(ctx.identifier())
val fields: List<Pair<DataType, List<String>>> = ctx.structfielddecl().map { getStructField(it) }
val flattened = fields.flatMap { (dt, names) -> names.map { dt to it}}
return StructDecl(name, flattened.toTypedArray(), ctx.toPosition())
}
private fun getStructField(ctx: StructfielddeclContext): Pair<DataType, List<String>> {
val identifiers = ctx.identifierlist()?.identifier() ?: emptyList()
val dt = dataTypeFor(ctx.datatype())!!
return dt to identifiers.map { getname(it) }
}
override fun visitModule_element(ctx: Module_elementContext): Node = visitChildren(ctx)
override fun visitBlock_statement(ctx: Block_statementContext): Statement = visitChildren(ctx) as Statement
@@ -595,13 +657,15 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitVariabledeclaration(ctx: VariabledeclarationContext): VarDecl = visitChildren(ctx) as VarDecl
override fun visitLiteralvalue(ctx: LiteralvalueContext): Expression = visitChildren(ctx) as Expression
override fun visitBasedatatype(ctx: BasedatatypeContext) = throw FatalAstException("should not be called")
override fun visitDirectivenamelist(ctx: DirectivenamelistContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_decl(ctx: Asmsub_declContext?) = throw FatalAstException("should not be called")
override fun visitAsmsub_decl(ctx: Asmsub_declContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_params(ctx: Asmsub_paramsContext) = throw FatalAstException("should not be called")
override fun visitExpression_list(ctx: Expression_listContext) = throw FatalAstException("should not be called")
override fun visitBranchcondition(ctx: BranchconditionContext) = throw FatalAstException("should not be called")
override fun visitDatatype(ctx: DatatypeContext) = throw FatalAstException("should not be called")
override fun visitIdentifierlist(ctx: IdentifierlistContext) = throw FatalAstException("should not be called")
override fun visitDirectivename(ctx: DirectivenameContext) = throw FatalAstException("should not be called")
override fun visitSizeof_argument(ctx: Sizeof_argumentContext) = throw FatalAstException("should not be called")
override fun visitReturnvalues(ctx: ReturnvaluesContext) = throw FatalAstException("should not be called")
override fun visitTypecast(ctx: TypecastContext) = throw FatalAstException("should not be called")
@@ -612,6 +676,10 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitAsmsub_returns(ctx: Asmsub_returnsContext) = throw FatalAstException("should not be called")
override fun visitAsmsub_return(ctx: Asmsub_returnContext) = throw FatalAstException("should not be called")
override fun visitSub_return_part(ctx: Sub_return_partContext) = throw FatalAstException("should not be called")
override fun visitStructfielddecl(ctx: StructfielddeclContext) = throw FatalAstException("should not be called")
override fun visitDerefchain(ctx: DerefchainContext) = throw FatalAstException("should not be called")
override fun visitSinglederef(ctx: SinglederefContext) = throw FatalAstException("should not be called")
override fun visitPointertype(ctx: PointertypeContext) = throw FatalAstException("should not be called")
private fun getname(identifier: IdentifierContext): String = identifier.children[0].text
@@ -649,12 +717,11 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
private fun asmSubroutineParam(pctx: Asmsub_paramContext): AsmSubroutineParameter {
val vardecl = pctx.vardecl()
val decldt = vardecl.datatype()
var datatype = if(decldt!=null) dataTypeFor(decldt) else DataType.UNDEFINED
var datatype = dataTypeFor(vardecl.datatype()) ?: DataType.UNDEFINED
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
datatype = datatype.elementToArray()
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
val identifiers = vardecl.identifier()
val identifiers = vardecl.identifierlist().identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
val identifiername = getname(identifiers[0])
@@ -691,7 +758,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
}
return AsmSubroutineReturn(
dataTypeFor(rctx.datatype()),
dataTypeFor(rctx.datatype())!!,
registerorpair,
statusregister)
}
@@ -704,9 +771,30 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
}
}
private fun dataTypeFor(it: DatatypeContext) = DataType.forDt(baseDatatypeFor(it))
private fun dataTypeFor(dtctx: DatatypeContext?): DataType? {
if(dtctx==null)
return null
val base = baseDatatypeFor(dtctx.basedatatype())
if(base!=null)
return DataType.forDt(base)
val pointer = pointerDatatypeFor(dtctx.pointertype())
if(pointer!=null)
return pointer
val struct = dtctx.structtype.identifier().map { dtctx.text }
return DataType.structInstanceFromAntlr(struct)
}
private fun baseDatatypeFor(it: DatatypeContext) = BaseDataType.valueOf(it.text.uppercase())
private fun pointerDatatypeFor(pointertype: PointertypeContext?): DataType? {
if(pointertype==null)
return null
val base = baseDatatypeFor(pointertype.basedatatype())
if(base!=null)
return DataType.pointer(base)
val identifier = pointertype.scoped_identifier().identifier().map { it.text}
return DataType.pointerFromAntlr(identifier)
}
private fun baseDatatypeFor(ctx: BasedatatypeContext?) = if(ctx==null) null else BaseDataType.valueOf(ctx.text.uppercase())
private fun stmtBlockOrSingle(statementBlock: Statement_blockContext?, statement: StatementContext?): AnonymousScope {
return if(statementBlock!=null)
@@ -723,7 +811,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
val name = getname(ad.identifier())
val params = ad.asmsub_params()?.asmsub_param()?.map { asmSubroutineParam(it) } ?: emptyList()
val returns = ad.asmsub_returns()?.asmsub_return()?.map { asmReturn(it) } ?: emptyList()
val clobbers = ad.asmsub_clobbers()?.clobber()?.NAME()?.map { cpuRegister(it.text, ad.toPosition()) } ?: emptyList()
val clobbers = ad.asmsub_clobbers()?.clobber()?.UNICODEDNAME()?.map { cpuRegister(it.text, ad.toPosition()) } ?: emptyList()
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.zp, it.registerOrPair, it.position) }
val normalReturntypes = returns.map { it.type }
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }

View File

@@ -40,10 +40,7 @@ sealed class Expression: Node {
else
other.left isSameAs left && other.right isSameAs right
}
is ArrayIndexedExpression -> {
(other is ArrayIndexedExpression && other.arrayvar.nameInSource == arrayvar.nameInSource
&& other.indexer isSameAs indexer)
}
is ArrayIndexedExpression -> isSameArrayIndexedAs(other)
is DirectMemoryRead -> {
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
}
@@ -51,7 +48,7 @@ sealed class Expression: Node {
(other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression)
}
is AddressOf -> {
(other is AddressOf && other.identifier.nameInSource == identifier.nameInSource && other.arrayIndex==arrayIndex)
(other is AddressOf && other.identifier?.nameInSource == identifier?.nameInSource && other.arrayIndex==arrayIndex && other.dereference==dereference)
}
is RangeExpression -> {
(other is RangeExpression && other.from==from && other.to==to && other.step==step)
@@ -70,10 +67,10 @@ sealed class Expression: Node {
if(sourceDt.base==targetDt && sourceDt.sub==null)
return Pair(false, this)
if(this is TypecastExpression) {
this.type = targetDt
this.type = DataType.forDt(targetDt)
return Pair(false, this)
}
val typecast = TypecastExpression(this, targetDt, implicit, this.position)
val typecast = TypecastExpression(this, DataType.forDt(targetDt), implicit, this.position)
return Pair(true, typecast)
}
}
@@ -129,7 +126,7 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
"~" -> {
if(inferred.isBytes) InferredTypes.knownFor(BaseDataType.UBYTE)
else if(inferred.isWords) InferredTypes.knownFor(BaseDataType.UWORD)
else InferredTypes.InferredType.unknown()
else InferredTypes.unknown()
}
"-" -> {
if(inferred.isBytes) InferredTypes.knownFor(BaseDataType.BYTE)
@@ -230,6 +227,49 @@ class BinaryExpression(
"<=", ">=",
"==", "!=" -> InferredTypes.knownFor(BaseDataType.BOOL)
"<<", ">>" -> leftDt
"." -> {
val leftExpr = left as? BinaryExpression
val leftIdentifier = left as? IdentifierReference
val leftIndexer = left as? ArrayIndexedExpression
val rightIdentifier = right as? IdentifierReference
val rightArrayIndexedDeref = right as? ArrayIndexedPtrDereference
if(rightIdentifier!=null) {
val struct: StructDecl? =
if (leftIdentifier != null) {
// PTR . FIELD
leftIdentifier.targetVarDecl()?.datatype?.subType as? StructDecl
} else if (leftIndexer != null && rightIdentifier.nameInSource.size == 1) {
// ARRAY[x].NAME --> maybe it's a pointer dereference
val dt = leftIndexer.inferType(program).getOrUndef()
if (dt.isPointer) {
dt.dereference().subType as? StructDecl
} else null
} else if (leftExpr != null) {
// SOMEEXPRESSION . NAME
val leftDt = leftExpr.inferType(program)
if (leftDt.isPointer)
leftDt.getOrUndef().subType as StructDecl?
else
null
} else null
if (struct == null)
InferredTypes.unknown()
else {
val fieldDt = if (rightIdentifier.nameInSource.size == 1)
struct.getFieldType(rightIdentifier.nameInSource.single())
else
rightIdentifier.traverseDerefChainForDt(struct)
if (fieldDt != null)
if (fieldDt.isUndefined) InferredTypes.unknown() else InferredTypes.knownFor(fieldDt)
else
InferredTypes.unknown()
}
} else if(leftIdentifier!=null && rightArrayIndexedDeref!=null) {
InferredTypes.unknown()
// TODO("LEFT=$leftIdentifier RIGHT=${rightArrayIndexedDeref.chain}")
} else
InferredTypes.unknown()
}
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
}
}
@@ -242,6 +282,13 @@ class BinaryExpression(
if(leftDt.isUndefined || rightDt.isUndefined)
return DataType.UNDEFINED to null
// anything combined with a pointer type -> pointer type.
if(leftDt.isPointer)
return leftDt to null
if(rightDt.isPointer)
return rightDt to null
// byte + byte -> byte
// byte + word -> word
// word + byte -> word
@@ -323,21 +370,30 @@ class BinaryExpression(
}
}
class ArrayIndexedExpression(var arrayvar: IdentifierReference,
class ArrayIndexedExpression(var plainarrayvar: IdentifierReference?,
var pointerderef: PtrDereference?,
val indexer: ArrayIndex,
override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
arrayvar.linkParents(this)
plainarrayvar?.linkParents(this)
pointerderef?.linkParents(this)
indexer.linkParents(this)
}
override val isSimple = indexer.indexExpr is NumericLiteral || indexer.indexExpr is IdentifierReference
override fun replaceChildNode(node: Node, replacement: Node) {
when {
node===arrayvar -> arrayvar = replacement as IdentifierReference
when (replacement) {
is IdentifierReference -> {
plainarrayvar = replacement
pointerderef = null
}
is PtrDereference -> {
plainarrayvar = null
pointerderef = replacement
}
else -> throw FatalAstException("invalid replace")
}
replacement.parent = this
@@ -347,28 +403,64 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifier(nameInSource: List<String>) = arrayvar.referencesIdentifier(nameInSource) || indexer.referencesIdentifier(nameInSource)
override fun referencesIdentifier(nameInSource: List<String>) =
plainarrayvar?.referencesIdentifier(nameInSource)==true || pointerderef?.referencesIdentifier(nameInSource)==true || indexer.referencesIdentifier(nameInSource)
override fun inferType(program: Program): InferredTypes.InferredType {
val target = arrayvar.targetStatement(program)
if (target is VarDecl) {
if(plainarrayvar!=null) {
val target = plainarrayvar!!.targetStatement()
if(target is VarDecl) {
return when {
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
target.datatype.isPointer -> InferredTypes.knownFor(target.datatype.dereference())
else -> InferredTypes.knownFor(target.datatype)
}
} else if(target is StructFieldRef) {
return InferredTypes.knownFor(target.type)
} else if(target==null) {
return InferredTypes.unknown()
} else if (plainarrayvar!!.nameInSource.size == 1) {
val subparam = definingSubroutine?.parameters?.firstOrNull { it.name == plainarrayvar!!.nameInSource[0] }
return if(subparam!=null) InferredTypes.knownFor(subparam.type) else InferredTypes.unknown()
} else
TODO("infer type from target $target ${target.position} arrayindexed @ ${this.position}")
} else if(pointerderef!=null) {
val dt= pointerderef!!.inferType(program).getOrUndef()
return when {
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
else -> InferredTypes.knownFor(target.datatype)
dt.isString || dt.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
dt.isArray -> InferredTypes.knownFor(dt.elementType())
dt.isPointer -> InferredTypes.knownFor(dt.dereference())
else -> InferredTypes.unknown()
}
}
return InferredTypes.unknown()
}
override fun toString(): String {
return "ArrayIndexed(ident=$arrayvar, idx=$indexer; pos=$position)"
return if(plainarrayvar!=null)
"ArrayIndexed(arrayvar=$plainarrayvar, idx=$indexer; pos=$position)"
else if(pointerderef!=null)
"ArrayIndexed(ptr=$pointerderef, idx=$indexer; pos=$position)"
else
"??????"
}
override fun copy() = ArrayIndexedExpression(plainarrayvar?.copy(), pointerderef?.copy(), indexer.copy(), position)
fun isSameArrayIndexedAs(other: Expression): Boolean {
if(other !is ArrayIndexedExpression || !(other.indexer.indexExpr isSameAs indexer.indexExpr))
return false
if(plainarrayvar?.nameInSource != other.plainarrayvar?.nameInSource)
return false
if(pointerderef!=null)
return pointerderef!!.isSamePointerDeref(other.pointerderef)
return true
}
override fun copy() = ArrayIndexedExpression(arrayvar.copy(), indexer.copy(), position)
}
class TypecastExpression(var expression: Expression, var type: BaseDataType, val implicit: Boolean, override val position: Position) : Expression() {
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@@ -376,10 +468,6 @@ class TypecastExpression(var expression: Expression, var type: BaseDataType, val
expression.linkParents(this)
}
init {
if(type==BaseDataType.BOOL) require(!implicit) {"no implicit cast to boolean allowed"}
}
override val isSimple = expression.isSimple
override fun replaceChildNode(node: Node, replacement: Node) {
@@ -395,9 +483,11 @@ class TypecastExpression(var expression: Expression, var type: BaseDataType, val
override fun referencesIdentifier(nameInSource: List<String>) = expression.referencesIdentifier(nameInSource)
override fun inferType(program: Program) = InferredTypes.knownFor(type)
override fun constValue(program: Program): NumericLiteral? {
if(!type.isBasic)
return null
val cv = expression.constValue(program) ?: return null
cv.linkParents(parent)
val cast = cv.cast(type, implicit)
val cast = cv.cast(type.base, implicit)
return if(cast.isValid) {
val newval = cast.valueOrZero()
newval.linkParents(parent)
@@ -412,36 +502,46 @@ class TypecastExpression(var expression: Expression, var type: BaseDataType, val
}
}
data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayIndex?, val msb: Boolean, override val position: Position) : Expression() {
data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: ArrayIndex?, var dereference: PtrDereference?,
val msb: Boolean, val typed: Boolean, override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
identifier.linkParents(this)
identifier?.linkParents(this)
arrayIndex?.linkParents(this)
dereference?.linkParents(this)
}
override val isSimple = true
override fun replaceChildNode(node: Node, replacement: Node) {
if(node===identifier) {
require(replacement is IdentifierReference)
identifier = replacement
replacement.parent = this
} else if(node===arrayIndex) {
require(replacement is ArrayIndex)
arrayIndex = replacement
replacement.parent = this
} else {
throw FatalAstException("invalid replace, no child node $node")
when {
node===identifier -> {
if(replacement is IdentifierReference) {
identifier = replacement
dereference = null
} else {
dereference = replacement as PtrDereference
identifier = null
}
}
node===arrayIndex -> {
require(replacement is ArrayIndex)
arrayIndex = replacement
}
else -> {
throw FatalAstException("invalid replace, no child node $node")
}
}
replacement.parent = this
}
override fun copy() = AddressOf(identifier.copy(), arrayIndex?.copy(), msb, position)
override fun copy() = AddressOf(identifier?.copy(), arrayIndex?.copy(), dereference?.copy(), msb, typed, position)
override fun constValue(program: Program): NumericLiteral? {
if(msb)
return null
val target = this.identifier.targetStatement(program)
val target = this.identifier?.targetStatement()
val targetVar = target as? VarDecl
if(targetVar!=null) {
if (targetVar.type == VarDeclType.MEMORY || targetVar.type == VarDeclType.CONST) {
@@ -472,8 +572,21 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
}
return null
}
override fun referencesIdentifier(nameInSource: List<String>) = identifier.nameInSource==nameInSource || arrayIndex?.referencesIdentifier(nameInSource)==true
override fun inferType(program: Program) = InferredTypes.knownFor(BaseDataType.UWORD)
override fun referencesIdentifier(nameInSource: List<String>) = identifier?.nameInSource==nameInSource || arrayIndex?.referencesIdentifier(nameInSource)==true || dereference?.referencesIdentifier(nameInSource)==true
override fun inferType(program: Program): InferredTypes.InferredType {
if(!typed)
return InferredTypes.knownFor(BaseDataType.UWORD) // orignal pre-v12 untyped AddressOf
if(identifier!=null) {
val type = identifier!!.inferType(program).getOrUndef()
val addrofDt = type.typeForAddressOf(msb)
return if(addrofDt.isUndefined) InferredTypes.unknown() else InferredTypes.knownFor(addrofDt)
} else if(dereference!=null) {
val type = dereference!!.inferType(program).getOrUndef()
val addrofDt = type.typeForAddressOf(msb)
return if(addrofDt.isUndefined) InferredTypes.unknown() else InferredTypes.knownFor(addrofDt)
} else
throw FatalAstException("invalid addressof")
}
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
}
@@ -687,6 +800,8 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
if(targettype==BaseDataType.LONG)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
if(targettype.isPointer)
return ValueAfterCast(true, null, NumericLiteral(BaseDataType.UWORD, number, position))
}
BaseDataType.BYTE -> {
if(targettype==BaseDataType.UBYTE) {
@@ -745,38 +860,38 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
when (targettype) {
BaseDataType.BYTE if number >= -128 && number <= 127 -> {
val converted = number.toInt().toByte().toDouble()
if(implicit && converted!=number)
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
return if(implicit && converted!=number)
ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
else
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
}
BaseDataType.UBYTE if number >= 0 && number <= 255 -> {
val converted = number.toInt().toUByte().toDouble()
if(implicit && converted!=number)
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
return if(implicit && converted!=number)
ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
else
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
}
BaseDataType.WORD if number >= -32768 && number <= 32767 -> {
val converted = number.toInt().toShort().toDouble()
if(implicit && converted!=number)
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
return if(implicit && converted!=number)
ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
else
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
}
BaseDataType.UWORD if number >= 0 && number <= 65535 -> {
val converted = number.toInt().toUShort().toDouble()
if(implicit && converted!=number)
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
return if(implicit && converted!=number)
ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
else
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
}
BaseDataType.LONG if number >=0 && number <= 2147483647 -> {
val converted = number.toInt().toDouble()
if(implicit && converted!=number)
return ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
return if(implicit && converted!=number)
ValueAfterCast(false, "refused truncating of float to avoid loss of precision", this)
else
return ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
ValueAfterCast(true, null, NumericLiteral(targettype, converted, position))
}
else -> {}
}
@@ -822,6 +937,7 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
when(targetDt) {
BaseDataType.BYTE -> if(number<=127.0) return cast(targetDt, false)
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT -> return cast(targetDt, false)
BaseDataType.POINTER -> return cast(targetDt, false)
else -> {}
}
}
@@ -838,6 +954,7 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
BaseDataType.BYTE -> if(number<=127.0) return cast(targetDt, false)
BaseDataType.WORD -> if(number<=32767.0) return cast(targetDt, false)
BaseDataType.LONG, BaseDataType.FLOAT -> return cast(targetDt, false)
BaseDataType.POINTER -> return cast(targetDt, false)
else -> {}
}
}
@@ -999,9 +1116,9 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
val loopvarDt = forloop.loopVarDt(program)
if(loopvarDt.isKnown) {
return if(!loopvarDt.isNumericOrBool)
InferredTypes.InferredType.unknown()
InferredTypes.unknown()
else
InferredTypes.InferredType.known(loopvarDt.getOrUndef().elementToArray())
InferredTypes.knownFor(loopvarDt.getOrUndef().elementToArray())
}
}
@@ -1009,23 +1126,33 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
require(value.isNotEmpty()) { "can't determine type of empty array" }
val datatypesInArray = value.map { it.inferType(program) }
if(datatypesInArray.any{ it.isUnknown })
return InferredTypes.InferredType.unknown()
return InferredTypes.unknown()
val dts = datatypesInArray.map { it.getOrUndef() }
if(dts.all { it.isPointer }) {
val unique = dts.toSet()
if(unique.size==1) {
val dt = unique.single()
return if(dt.subType!=null)
InferredTypes.knownFor(DataType.arrayOfPointersTo(dt.subType!!))
else
InferredTypes.knownFor(DataType.arrayOfPointersTo(dt.sub!!))
}
}
return when {
dts.any { it.isFloat } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.FLOAT))
dts.any { it.isString } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
dts.any { it.isSignedWord } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.WORD))
dts.any { it.isUnsignedWord } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
dts.any { it.isSignedByte } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.BYTE))
dts.any { it.isFloat } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.FLOAT))
dts.any { it.isString } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.UWORD))
dts.any { it.isSignedWord } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.WORD))
dts.any { it.isUnsignedWord } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.UWORD))
dts.any { it.isSignedByte } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.BYTE))
dts.any { it.isBool } -> {
if(dts.all { it.isBool})
InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.BOOL))
InferredTypes.knownFor(DataType.arrayFor(BaseDataType.BOOL))
else
InferredTypes.InferredType.unknown()
InferredTypes.unknown()
}
dts.any { it.isUnsignedByte } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE))
dts.any { it.isArray } -> InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
else -> InferredTypes.InferredType.unknown()
dts.any { it.isUnsignedByte } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.UBYTE))
dts.any { it.isArray } -> InferredTypes.knownFor(DataType.arrayFor(BaseDataType.UWORD))
else -> InferredTypes.unknown()
}
}
@@ -1050,7 +1177,8 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
return null // abort
}
} else {
require(elementType.isNumericOrBool)
if(!elementType.isNumericOrBool)
return null // only a numeric or boolean array can be casted to another value
value.map {
val cast = (it as NumericLiteral).cast(elementType.base, true)
if(cast.isValid)
@@ -1185,17 +1313,27 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override val isSimple = true
fun targetStatement(program: Program?) =
if(program!=null && nameInSource.singleOrNull() in program.builtinFunctions.names)
fun targetStatement(builtins: IBuiltinFunctions? = null): Statement? =
if(builtins!=null && nameInSource.singleOrNull() in builtins.names)
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
else
definingScope.lookup(nameInSource)
fun targetVarDecl(): VarDecl? = targetStatement() as? VarDecl
fun targetSubroutine(): Subroutine? = targetStatement() as? Subroutine
fun targetStructDecl(): StructDecl? = targetStatement() as? StructDecl
fun targetStructFieldRef(): StructFieldRef? {
if(nameInSource.size<2) return null
return targetStatement() as? StructFieldRef
}
fun targetVarDecl(): VarDecl? = targetStatement(null) as? VarDecl
fun targetSubroutine(): Subroutine? = targetStatement(null) as? Subroutine
fun firstTarget(builtins: IBuiltinFunctions? = null): Statement? =
if(builtins!=null && nameInSource.singleOrNull() in builtins.names)
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
else
definingScope.lookup(nameInSource.take(1))
fun targetNameAndType(program: Program): Pair<String, DataType> {
val target = targetStatement(program) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
val target = targetStatement(program.builtinFunctions) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
val targetname: String = if(target.name in program.builtinFunctions.names)
"<builtin>.${target.name}"
else
@@ -1223,6 +1361,10 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
return null
}
if(node is StructFieldRef)
return null
val vardecl = node as? VarDecl
if(vardecl==null) {
return null
@@ -1251,17 +1393,61 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override fun referencesIdentifier(nameInSource: List<String>): Boolean = this.nameInSource==nameInSource
override fun inferType(program: Program): InferredTypes.InferredType {
return when (val targetStmt = targetStatement(program)) {
return when (val targetStmt = targetStatement(program.builtinFunctions)) {
is VarDecl -> {
if(targetStmt.datatype.isUndefined)
InferredTypes.unknown()
else
InferredTypes.knownFor(targetStmt.datatype)
}
else -> InferredTypes.InferredType.unknown()
null -> {
val param = definingSubroutine?.parameters?.find { it.name==nameInSource.singleOrNull() }
if(param!=null)
return InferredTypes.knownFor(param.type)
val fieldType = traverseDerefChainForDt(null)
if(fieldType.isUndefined)
InferredTypes.unknown()
else
InferredTypes.knownFor(fieldType)
}
is StructDecl -> {
InferredTypes.unknown() // the type of a structdecl itself is actually not defined
}
is StructFieldRef -> {
InferredTypes.knownFor(targetStmt.type)
}
else -> InferredTypes.unknown()
}
}
fun traverseDerefChainForDt(startStruct: StructDecl?): DataType {
var struct: StructDecl
var fieldDt: DataType? = null
if(startStruct!=null) {
struct = startStruct
}
else {
val vardecl = definingScope.lookup(nameInSource.take(1)) as? VarDecl
if (vardecl?.datatype?.isPointer != true)
return DataType.UNDEFINED
require(vardecl.datatype.subType!=null) { "pointer type should point to a struct ${vardecl.position}" }
struct = vardecl.datatype.subType as StructDecl
fieldDt = vardecl.datatype
}
for((idx, field) in nameInSource.drop(1).withIndex()) {
fieldDt = struct.getFieldType(field)
if(fieldDt==null)
return DataType.UNDEFINED
if(idx==nameInSource.size-2) {
// was last path element
return fieldDt
}
struct = fieldDt.subType as? StructDecl ?: return DataType.UNDEFINED
}
return fieldDt ?: DataType.UNDEFINED
}
fun wasStringLiteral(): Boolean {
val decl = targetVarDecl()
if(decl == null || decl.origin!=VarDeclOrigin.STRINGLITERAL)
@@ -1272,6 +1458,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
}
class FunctionCallExpression(override var target: IdentifierReference,
override val args: MutableList<Expression>,
override val position: Position) : Expression(), IFunctionCall {
@@ -1330,7 +1517,7 @@ class FunctionCallExpression(override var target: IdentifierReference,
val constVal = constValue(program ,false)
if(constVal!=null)
return InferredTypes.knownFor(constVal.type)
val stmt = target.targetStatement(program) ?: return InferredTypes.unknown()
val stmt = target.targetStatement(program.builtinFunctions) ?: return InferredTypes.unknown()
when (stmt) {
is BuiltinFunctionPlaceholder -> {
return program.builtinFunctions.returnType(target.nameInSource[0])
@@ -1343,6 +1530,11 @@ class FunctionCallExpression(override var target: IdentifierReference,
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
}
is StructDecl -> {
// calling a struct is syntax for allocating a static instance, and returns a pointer to that (not the instance itself)
return InferredTypes.knownFor(DataType.pointer(stmt))
}
is StructFieldRef -> throw FatalAstException("cannot call a struct field $stmt")
else -> return InferredTypes.unknown()
}
}
@@ -1443,7 +1635,7 @@ class IfExpression(var condition: Expression, var truevalue: Expression, var fal
override fun inferType(program: Program): InferredTypes.InferredType {
val t1 = truevalue.inferType(program)
val t2 = falsevalue.inferType(program)
return if(t1==t2) t1 else InferredTypes.InferredType.unknown()
return if(t1==t2) t1 else InferredTypes.unknown()
}
override fun copy(): Expression = IfExpression(condition.copy(), truevalue.copy(), falsevalue.copy(), position)
@@ -1458,7 +1650,7 @@ class IfExpression(var condition: Expression, var truevalue: Expression, var fal
override fun replaceChildNode(node: Node, replacement: Node) {
if(replacement !is Expression)
throw throw FatalAstException("invalid replace")
throw FatalAstException("invalid replace")
if(node===condition) condition=replacement
else if(node===truevalue) truevalue=replacement
else if(node===falsevalue) falsevalue=replacement
@@ -1466,6 +1658,91 @@ class IfExpression(var condition: Expression, var truevalue: Expression, var fal
}
}
class PtrDereference(
val chain: List<String>,
val derefLast: Boolean,
override val position: Position
) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
}
override val isSimple = false
override fun copy(): PtrDereference = PtrDereference(chain.toList(), derefLast, position)
override fun constValue(program: Program): NumericLiteral? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun inferType(program: Program): InferredTypes.InferredType {
fun resultType(dt: DataType?) = if(dt==null) InferredTypes.unknown() else InferredTypes.knownFor(if(derefLast) dt.dereference() else dt)
val target = definingScope.lookup(chain)
if(target==null)
return InferredTypes.unknown()
if(target is VarDecl)
return resultType(target.datatype)
if(target is StructFieldRef)
return resultType(target.type)
TODO("infertype $chain -> $target")
}
override fun replaceChildNode(node: Node, replacement: Node) =
throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = chain.size==1 && chain==nameInSource
fun isSamePointerDeref(other: Expression?): Boolean {
if(other==null || other !is PtrDereference)
return false
if(derefLast != other.derefLast)
return false
if(chain != other.chain)
return false
return true
}
fun firstTarget(): Statement? = definingScope.lookup(chain.take(1))
}
class ArrayIndexedPtrDereference(
val chain: List<Pair<String, ArrayIndex?>>,
val derefLast: Boolean,
override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
chain.forEach { it.second?.linkParents(this) }
}
override val isSimple = false
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = chain.size==1 && chain==nameInSource
override fun copy(): ArrayIndexedPtrDereference = ArrayIndexedPtrDereference(chain.toList(), derefLast, position)
override fun constValue(program: Program): NumericLiteral? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun inferType(program: Program): InferredTypes.InferredType {
if(parent !is BinaryExpression || (parent as? BinaryExpression)?.operator != "." && derefLast) {
val arrayIdentifier = chain.map { it.first }
val symbol = definingScope.lookup(arrayIdentifier) as? VarDecl
if(symbol!=null) {
require(symbol.datatype.isArray)
return InferredTypes.knownFor(symbol.datatype.sub!!)
}
}
// too hard to determine the type....?
return InferredTypes.unknown()
}
}
fun invertCondition(cond: Expression, program: Program): Expression {
if(cond is BinaryExpression) {
val invertedOperator = invertedComparisonOperator(cond.operator)

View File

@@ -21,7 +21,7 @@ object InferredTypes {
false
else if(type==BaseDataType.STR && this.datatype?.base==BaseDataType.STR)
true
else (this.datatype?.base == type && this.datatype.sub == null) // strict equality if known
else (this.datatype?.base == type && this.datatype.isBasic) // strict equality if known
companion object {
fun unknown() = InferredType(isUnknown = true, isVoid = false, datatype = null)
@@ -56,6 +56,9 @@ object InferredTypes {
val isBool = datatype?.isBool==true
val isBytes = datatype?.isByte==true
val isWords = datatype?.isWord==true
val isPointer = datatype?.isPointer==true
val isStructInstance = datatype?.isStructInstance==true
val isUnsignedWord = datatype?.isUnsignedWord==true
val isInteger = datatype?.isInteger==true
val isNumeric = datatype?.isNumeric==true
val isNumericOrBool = datatype?.isNumericOrBool==true
@@ -82,6 +85,7 @@ object InferredTypes {
type.isFloat -> InferredType.known(BaseDataType.FLOAT)
type.isString -> InferredType.known(BaseDataType.STR)
type.isLong -> InferredType.known(BaseDataType.LONG)
type.isPointerArray -> InferredType.known(type)
type.isSplitWordArray -> {
when(type.sub) {
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
@@ -90,9 +94,9 @@ object InferredTypes {
else -> throw IllegalArgumentException("invalid sub type")
}
}
type.isArray -> {
InferredType.known(DataType.arrayFor(type.sub!!, false))
}
else -> throw IllegalArgumentException("invalid type")
type.isArray -> InferredType.known(type)
type.isPointer -> InferredType.known(type)
type.isStructInstance -> InferredType.known(type)
else -> throw IllegalArgumentException("invalid type $type")
}
}

View File

@@ -270,7 +270,7 @@ class VarDecl(
// parameter variable memory mapped to a R0-R15 virtual register
val regname = param.registerOrPair.asScopedNameVirtualReg(param.type)
decltype = VarDeclType.MEMORY
value = AddressOf(IdentifierReference(regname, param.position), null, false, param.position)
value = AddressOf(IdentifierReference(regname, param.position), null, null, false, false,param.position)
}
val dt = if(param.type.isArray) DataType.UWORD else param.type
return VarDecl(decltype, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, SplitWish.DONTCARE, null, param.name, emptyList(), value,
@@ -393,8 +393,49 @@ class VarDecl(
}
}
class ArrayIndex(var indexExpr: Expression,
override val position: Position) : Node {
class StructDecl(override val name: String, val fields: Array<Pair<DataType, String>>, override val position: Position) : Statement(), INamedStatement, ISubType {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
}
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = false
override fun copy() = StructDecl(name, fields.clone(), position)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun memsize(sizer: IMemSizer): Int = fields.sumOf { sizer.memorySize(it.first, 1) }
override fun sameas(other: ISubType): Boolean = other is StructDecl && other.name==name && other.fields.contentEquals(fields)
fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second==name }?.first
override val scopedNameString by lazy { scopedName.joinToString(".") }
}
class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, val type: DataType, override val name: String, override val position: Position): Statement(), INamedStatement {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
pointer.linkParents(this)
}
override val scopedName: List<String>
get() = pointer.nameInSource
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = pointer.referencesIdentifier(nameInSource) || struct.referencesIdentifier(nameInSource)
override fun copy(): StructFieldRef = StructFieldRef(pointer.copy(), struct.copy(), type, name, position)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class ArrayIndex(var indexExpr: Expression, override val position: Position) : Node {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@@ -485,6 +526,7 @@ class Assignment(var target: AssignTarget, var value: Expression, var origin: As
/**
* Is the assigment value an expression that references the assignment target itself?
* Note it doesn't have to be the first term in the expression!
* The expression can be a BinaryExpression, PrefixExpression or TypecastExpression (possibly with one sub-cast).
*/
val isAugmentable: Boolean
@@ -546,12 +588,16 @@ class Assignment(var target: AssignTarget, var value: Expression, var origin: As
}
}
data class AssignTarget(var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?,
val memoryAddress: DirectMemoryWrite?,
val multi: List<AssignTarget>?,
val void: Boolean,
override val position: Position) : Node {
data class AssignTarget(
var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?,
var memoryAddress: DirectMemoryWrite?,
val multi: List<AssignTarget>?,
val void: Boolean,
var pointerDereference: PtrDereference? = null,
var arrayIndexedDereference: ArrayIndexedPtrDereference? = null,
override val position: Position
) : Node {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@@ -559,43 +605,105 @@ data class AssignTarget(var identifier: IdentifierReference?,
identifier?.linkParents(this)
arrayindexed?.linkParents(this)
memoryAddress?.linkParents(this)
pointerDereference?.linkParents(this)
arrayIndexedDereference?.linkParents(this)
multi?.forEach { it.linkParents(this) }
}
override fun replaceChildNode(node: Node, replacement: Node) {
when {
node === identifier -> identifier = replacement as IdentifierReference
node === arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression
node === identifier -> {
identifier = null
arrayindexed = null
pointerDereference = null
arrayIndexedDereference = null
when (replacement) {
is IdentifierReference -> identifier = replacement
is PtrDereference -> pointerDereference = replacement
else -> throw FatalAstException("invalid replacement for AssignTarget.identifier: $replacement")
}
}
node === arrayindexed -> {
identifier = null
pointerDereference = null
arrayIndexedDereference = null
arrayindexed = null
memoryAddress = null
when (replacement) {
is ArrayIndexedExpression -> arrayindexed = replacement
is DirectMemoryWrite -> memoryAddress = replacement
is PtrDereference -> pointerDereference = replacement
is ArrayIndexedPtrDereference -> arrayIndexedDereference = replacement
else -> throw FatalAstException("invalid replacement for AssignTarget.arrayindexed: $replacement")
}
}
node === pointerDereference -> {
identifier = null
pointerDereference = null
arrayIndexedDereference = null
arrayindexed = null
memoryAddress = null
when (replacement) {
is ArrayIndexedExpression -> arrayindexed = replacement
is DirectMemoryWrite -> memoryAddress = replacement
is PtrDereference -> pointerDereference = replacement
is ArrayIndexedPtrDereference -> arrayIndexedDereference = replacement
else -> throw FatalAstException("invalid replacement for AssignTarget.pointerDereference: $replacement")
}
}
node === this.arrayIndexedDereference -> {
identifier = null
pointerDereference = null
arrayIndexedDereference = null
arrayindexed = null
memoryAddress = null
when(replacement) {
is ArrayIndexedExpression -> arrayindexed = replacement
is DirectMemoryWrite -> memoryAddress = replacement
is PtrDereference -> pointerDereference = replacement
is ArrayIndexedPtrDereference -> arrayIndexedDereference = replacement
else -> throw FatalAstException("invalid replacement for AssignTarget.arrayIndexedDereference: $replacement")
}
}
node === multi -> throw FatalAstException("can't replace multi assign targets")
else -> throw FatalAstException("invalid replace")
else -> throw FatalAstException("invalid replace $node $replacement")
}
replacement.parent = this
}
fun accept(visitor: IAstVisitor) = visitor.visit(this)
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun copy() = AssignTarget(identifier?.copy(), arrayindexed?.copy(), memoryAddress?.copy(), multi?.toList(), void, position)
override fun copy() = AssignTarget(
identifier?.copy(),
arrayindexed?.copy(),
memoryAddress?.copy(),
multi?.toList(),
void,
pointerDereference?.copy(),
arrayIndexedDereference?.copy(),
position
)
override fun referencesIdentifier(nameInSource: List<String>): Boolean =
identifier?.referencesIdentifier(nameInSource)==true ||
arrayindexed?.referencesIdentifier(nameInSource)==true ||
memoryAddress?.referencesIdentifier(nameInSource)==true ||
pointerDereference?.referencesIdentifier(nameInSource)==true ||
arrayIndexedDereference?.referencesIdentifier(nameInSource)==true ||
multi?.any { it.referencesIdentifier(nameInSource)}==true
fun inferType(program: Program): InferredTypes.InferredType {
if (identifier != null) {
val symbol = definingScope.lookup(identifier!!.nameInSource) ?: return InferredTypes.unknown()
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
if (symbol is StructFieldRef) return InferredTypes.knownFor(symbol.type)
}
if (arrayindexed != null) {
return arrayindexed!!.inferType(program)
return when {
arrayindexed != null -> arrayindexed!!.inferType(program)
memoryAddress != null -> InferredTypes.knownFor(BaseDataType.UBYTE)
pointerDereference != null -> pointerDereference!!.inferType(program)
arrayIndexedDereference != null -> arrayIndexedDereference!!.inferType(program)
else -> InferredTypes.unknown() // a multi-target has no 1 particular type
}
if (memoryAddress != null)
return InferredTypes.knownFor(BaseDataType.UBYTE)
// a multi-target has no 1 particular type
return InferredTypes.unknown()
}
fun toExpression(): Expression {
@@ -603,9 +711,11 @@ data class AssignTarget(var identifier: IdentifierReference?,
return when {
identifier != null -> identifier!!.copy()
arrayindexed != null -> arrayindexed!!.copy()
memoryAddress != null -> DirectMemoryRead(memoryAddress.addressExpression.copy(), memoryAddress.position)
memoryAddress != null -> DirectMemoryRead(memoryAddress!!.addressExpression.copy(), memoryAddress!!.position)
multi != null -> throw FatalAstException("cannot turn a multi-assign into a single source expression")
else -> throw FatalAstException("invalid assignmenttarget")
pointerDereference != null -> pointerDereference!!.copy()
arrayIndexedDereference != null -> arrayIndexedDereference!!.copy()
else -> throw FatalAstException("invalid assignment target")
}
}
@@ -614,45 +724,57 @@ data class AssignTarget(var identifier: IdentifierReference?,
memoryAddress != null -> {
// if the target is a memory write, and the value is a memory read, they're the same if the address matches
if (value is DirectMemoryRead)
this.memoryAddress.addressExpression isSameAs value.addressExpression
memoryAddress!!.addressExpression isSameAs value.addressExpression
else
false
}
identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource
arrayindexed != null -> {
if(value is ArrayIndexedExpression && value.arrayvar.nameInSource == arrayindexed!!.arrayvar.nameInSource)
arrayindexed!!.indexer isSameAs value.indexer
else
false
}
arrayindexed != null -> value is ArrayIndexedExpression && arrayindexed!!.isSameArrayIndexedAs(value)
multi != null -> false
pointerDereference != null -> {
return if (value is PtrDereference) {
pointerDereference!!.chain==value.chain
} else false
}
arrayIndexedDereference != null -> {
return if (value is ArrayIndexedPtrDereference) {
arrayIndexedDereference!!.chain==value.chain && arrayIndexedDereference!!.derefLast==value.derefLast
} else false
}
else -> false
}
}
fun isSameAs(other: AssignTarget, program: Program): Boolean {
if (this === other)
return true
if(void && other.void)
return true
if (this.identifier != null && other.identifier != null)
return this.identifier!!.nameInSource == other.identifier!!.nameInSource
if (this.memoryAddress != null && other.memoryAddress != null) {
val addr1 = this.memoryAddress.addressExpression.constValue(program)
val addr2 = other.memoryAddress.addressExpression.constValue(program)
return addr1 != null && addr2 != null && addr1 == addr2
}
if (this.arrayindexed != null && other.arrayindexed != null) {
if (this.arrayindexed!!.arrayvar.nameInSource == other.arrayindexed!!.arrayvar.nameInSource) {
val x1 = this.arrayindexed!!.indexer.constIndex()
val x2 = other.arrayindexed!!.indexer.constIndex()
return x1 != null && x2 != null && x1 == x2
when {
this === other -> return true
void && other.void -> return true
this.identifier != null && other.identifier != null -> return this.identifier!!.nameInSource == other.identifier!!.nameInSource
this.memoryAddress != null && other.memoryAddress != null -> {
val addr1 = memoryAddress!!.addressExpression.constValue(program)
val addr2 = other.memoryAddress!!.addressExpression.constValue(program)
return addr1 != null && addr2 != null && addr1 == addr2
}
this.arrayindexed != null && other.arrayindexed != null -> {
if(this.arrayindexed!!.plainarrayvar!=null && this.arrayindexed!!.plainarrayvar?.nameInSource == other.arrayindexed!!.plainarrayvar?.nameInSource) {
val x1 = this.arrayindexed!!.indexer.constIndex()
val x2 = other.arrayindexed!!.indexer.constIndex()
return x1 != null && x2 != null && x1 == x2
}
else if(this.pointerDereference != null && other.pointerDereference != null && this.pointerDereference!!.isSamePointerDeref(other.pointerDereference))
return true
else
return false
}
pointerDereference !=null && other.pointerDereference !=null -> {
return pointerDereference!! isSameAs other.pointerDereference!!
}
arrayIndexedDereference !=null && other.arrayIndexedDereference !=null -> {
return arrayIndexedDereference!! isSameAs other.arrayIndexedDereference!!
}
this.multi != null && other.multi != null -> return this.multi == other.multi
else -> return false
}
if(this.multi != null && other.multi != null)
return this.multi == other.multi
return false
}
fun isIOAddress(target: ICompilationTarget): Boolean {
@@ -677,14 +799,18 @@ data class AssignTarget(var identifier: IdentifierReference?,
}
}
arrayIdx != null -> {
val targetStmt = arrayIdx.arrayvar.targetVarDecl()
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteral
if (addr != null)
target.isIOAddress(addr.number.toUInt())
else
false
} else false
if(arrayIdx.plainarrayvar!=null) {
val targetStmt = arrayIdx.plainarrayvar!!.targetVarDecl()
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteral
if (addr != null)
target.isIOAddress(addr.number.toUInt())
else
false
} else false
}
// can't really tell for the other types... assume false.
return false
}
ident != null -> {
val decl = ident.targetVarDecl() ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")

View File

@@ -73,7 +73,7 @@ interface IAstModification {
try {
parent.replaceChildNode(node, replacement)
replacement.linkParents(parent)
} catch (fa: FatalAstException) {
} catch (_: FatalAstException) {
// possibly because of another replacement. Ignore here, we try again later.
}
}
@@ -105,6 +105,10 @@ abstract class AstWalker {
open fun before(continueStmt: Continue, parent: Node): Iterable<IAstModification> = noModifications
open fun before(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun before(deref: PtrDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun before(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun before(struct: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun before(field: StructFieldRef, parent: Node): Iterable<IAstModification> = noModifications
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
@@ -151,6 +155,10 @@ abstract class AstWalker {
open fun after(continueStmt: Continue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(deref: PtrDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun after(struct: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(field: StructFieldRef, parent: Node): Iterable<IAstModification> = noModifications
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
@@ -271,6 +279,16 @@ abstract class AstWalker {
track(after(decl, parent), decl, parent)
}
fun visit(struct: StructDecl, parent: Node) {
track(before(struct, parent), struct, parent)
track(after(struct, parent), struct, parent)
}
fun visit(field: StructFieldRef, parent: Node) {
track(before(field, parent), field, parent)
track(after(field, parent), field, parent)
}
fun visit(subroutine: Subroutine, parent: Node) {
track(before(subroutine, parent), subroutine, parent)
subroutine.asmAddress?.varbank?.accept(this, subroutine)
@@ -420,7 +438,8 @@ abstract class AstWalker {
fun visit(arrayIndexedExpression: ArrayIndexedExpression, parent: Node) {
track(before(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
arrayIndexedExpression.arrayvar.accept(this, arrayIndexedExpression)
arrayIndexedExpression.plainarrayvar?.accept(this, arrayIndexedExpression)
arrayIndexedExpression.pointerderef?.accept(this, arrayIndexedExpression)
arrayIndexedExpression.indexer.accept(this)
track(after(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
}
@@ -430,6 +449,8 @@ abstract class AstWalker {
assignTarget.arrayindexed?.accept(this, assignTarget)
assignTarget.identifier?.accept(this, assignTarget)
assignTarget.memoryAddress?.accept(this, assignTarget)
assignTarget.pointerDereference?.accept(this, assignTarget)
assignTarget.arrayIndexedDereference?.accept(this, assignTarget)
assignTarget.multi?.forEach { it.accept(this, assignTarget) }
track(after(assignTarget, parent), assignTarget, parent)
}
@@ -466,8 +487,9 @@ abstract class AstWalker {
fun visit(addressOf: AddressOf, parent: Node) {
track(before(addressOf, parent), addressOf, parent)
addressOf.identifier.accept(this, addressOf)
addressOf.identifier?.accept(this, addressOf)
addressOf.arrayIndex?.accept(this)
addressOf.dereference?.accept(this, addressOf)
track(after(addressOf, parent), addressOf, parent)
}
@@ -512,5 +534,16 @@ abstract class AstWalker {
ongoto.elsepart?.accept(this, ongoto)
track(after(ongoto, parent), ongoto, parent)
}
fun visit(deref: PtrDereference, parent: Node) {
track(before(deref, parent), deref, parent)
track(after(deref, parent), deref, parent)
}
fun visit(deref: ArrayIndexedPtrDereference, parent: Node) {
track(before(deref, parent), deref, parent)
deref.chain.forEach { it.second?.accept(this) }
track(after(deref, parent), deref, parent)
}
}

View File

@@ -44,6 +44,12 @@ interface IAstVisitor {
decl.arraysize?.accept(this)
}
fun visit(struct: StructDecl) {
}
fun visit(field: StructFieldRef) {
}
fun visit(subroutine: Subroutine) {
subroutine.asmAddress?.varbank?.accept(this)
subroutine.statements.forEach { it.accept(this) }
@@ -146,7 +152,8 @@ interface IAstVisitor {
}
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
arrayIndexedExpression.arrayvar.accept(this)
arrayIndexedExpression.plainarrayvar?.accept(this)
arrayIndexedExpression.pointerderef?.accept(this)
arrayIndexedExpression.indexer.accept(this)
}
@@ -154,6 +161,8 @@ interface IAstVisitor {
assignTarget.arrayindexed?.accept(this)
assignTarget.identifier?.accept(this)
assignTarget.memoryAddress?.accept(this)
assignTarget.pointerDereference?.accept(this)
assignTarget.arrayIndexedDereference?.accept(this)
assignTarget.multi?.forEach { it.accept(this) }
}
@@ -178,8 +187,9 @@ interface IAstVisitor {
}
fun visit(addressOf: AddressOf) {
addressOf.identifier.accept(this)
addressOf.identifier?.accept(this)
addressOf.arrayIndex?.accept(this)
addressOf.dereference?.accept(this)
}
fun visit(inlineAssembly: InlineAssembly) {
@@ -205,4 +215,11 @@ interface IAstVisitor {
onGoto.labels.forEach { it.accept(this) }
onGoto.elsepart?.accept(this)
}
fun visit(deref: PtrDereference) {
}
fun visit(deref: ArrayIndexedPtrDereference) {
deref.chain.forEach { it.second?.accept(this) }
}
}

View File

@@ -6,6 +6,7 @@ import prog8.ast.Program
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.PtrDereference
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.IErrorReporter
@@ -101,7 +102,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(addressOf: AddressOf) {
addressOf.identifier.targetSubroutine()?.let { notCalledButReferenced.add(it) }
addressOf.identifier?.targetSubroutine()?.let { notCalledButReferenced.add(it) }
super.visit(addressOf)
}
@@ -117,7 +118,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(identifier: IdentifierReference) {
val target = identifier.targetStatement(program)
val target = identifier.targetStatement(program.builtinFunctions)
if(target!=null) {
allIdentifiersAndTargets.add(identifier to target)
if(target is Subroutine)
@@ -132,9 +133,46 @@ class CallGraph(private val program: Program) : IAstVisitor {
val scopeTarget = scope.lookup(name)
if(scopeTarget is Subroutine)
notCalledButReferenced += scopeTarget
else if(scopeTarget is VarDecl) {
allIdentifiersAndTargets.add(identifier to scopeTarget)
break
} else if(scopeTarget is StructFieldRef) {
val vd = scope.lookup(name.take(1))
if(vd is VarDecl)
allIdentifiersAndTargets.add(identifier to vd)
allIdentifiersAndTargets.add(identifier to scopeTarget)
break
}
}
}
override fun visit(decl: VarDecl) {
// make sure the location where a struct definition is (can be block scope or subroutine scope), gets registered so it is seen as in use
if(decl.datatype.isPointer) {
if(decl.datatype.subType!=null) {
val struct = decl.definingScope.lookup(decl.datatype.subType!!.scopedNameString.split(".")) as? StructDecl
if (struct != null) {
allIdentifiersAndTargets.add(IdentifierReference(listOf(struct.name), struct.position) to struct)
val declSub = decl.definingSubroutine
val structSub = struct.definingSubroutine
if (declSub != null && structSub != null && declSub!=structSub) {
calls[declSub] = calls.getValue(declSub) + structSub
calledBy[structSub] = calledBy.getValue(structSub) + declSub
}
}
}
}
super.visit(decl)
}
override fun visit(deref: PtrDereference) {
val first = deref.definingScope.lookup(deref.chain.take(1))
if(first is VarDecl) {
allIdentifiersAndTargets.add(IdentifierReference(listOf(first.name), first.position) to first)
}
super.visit(deref)
}
override fun visit(inlineAssembly: InlineAssembly) {
allAssemblyNodes.add(inlineAssembly)
}

View File

@@ -46,6 +46,7 @@ Data types
- maximum storage size for arrays is 256 bytes (512 for split word arrays) , the maximum number of elements in the array depends on the size of a single element value.
you can use larger "arrays" via pointer indexing, see below at Pointers. One way of obtaining a piece of memory to store
such an "array" is by using ``memory()`` builtin function.
- there is limited support for structs and typed pointers, see below at "Pointers and Structs".
Variables
@@ -76,10 +77,13 @@ Subroutines
With only a little bit of code it is possible to implement a simple cooperative multitasking system that runs multiple tasks simultaneously. See the "multitasking" example,
which uses the "coroutines" library. Each task is a subroutine and it simply has its state stored in the statically allocated variables so it can resume after yielding, without doing anything special.
Pointers
--------
- There is no specific pointer datatype.
However, variables of the ``uword`` datatype can be used as a pointer to one of the possible 65536 memory locations,
Pointers and Structs
--------------------
Legacy 'untyped' pointers:
- In Prog8 versions before 12.0 there was no support for typed pointers, only 'untyped' ones:
Variables of the ``uword`` datatype can be used as a pointer to one of the possible 65536 memory locations,
so the value it points to is always a single byte. This is similar to ``uint8_t*`` from C.
You have to deal with the uword manually if the object it points to is something different.
- Note that there is the ``peekw`` builtin function that *does* allow you to directy obtain the *word* value at the given memory location.
@@ -88,6 +92,12 @@ Pointers
- Pointers don't have to be a variable, you can immediately access the value of a given memory location using ``@($d020)`` for instance.
Reading is done by assigning it to a variable, writing is done by just assigning the new value to it.
Typed pointers and structs:
- Since version 12, prog8 supports struct types and typed pointers.
- Structs are a grouping of one or more fields, that together make up the struct type.
- Typed pointers are just that: a pointer to a specific type (which can be a simple type such as float, or a struct type.)
Foreign function interface (external/ROM calls)
-----------------------------------------------

View File

@@ -96,6 +96,7 @@ Features
and inline assembly to have full control when every register, cycle or byte matters
- Variables are all allocated statically, no memory allocation overhead
- Variable data types include signed and unsigned bytes and words, arrays, strings.
- Structs and typed pointers
- Tight control over Zeropage usage
- Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.
- Programs can be configured to execute in ROM
@@ -222,6 +223,7 @@ Look in the `syntax-files <https://github.com/irmen/prog8/tree/master/syntax-fil
compiling.rst
programming.rst
variables.rst
structpointers.rst
binlibrary.rst
libraries.rst
targetsystem.rst

View File

@@ -121,6 +121,10 @@ mkword (msb, lsb)
peek (address)
same as @(address) - reads the byte at the given address in memory.
peekbool (address)
Reads the boolean value (byte 0 or 1) at the given address in memory and returns it.
If the memory location contains another value than 0 or 1, results are undefined.
peekw (address)
reads the word value at the given address in memory. Word is read as usual little-endian lsb/msb byte order.
Caution: when using peekw to get words out of an array pointer, make sure the array is *not* a split word array
@@ -132,6 +136,10 @@ peekf (address)
poke (address, value)
same as @(address)=value - writes the byte value at the given address in memory.
pokebool (address, value)
Writes the boolean value at the given address in memory, as byte 0 or 1.
Can also be written as pokebowl(addres, value), just for fun.
pokew (address, value)
writes the word value at the given address in memory, in usual little-endian lsb/msb byte order.

View File

@@ -999,7 +999,7 @@ containment check: ``in``
}
address of: ``&``, ``&<``, ``&>``
address of: ``&``, ``&<``, ``&>``, ``&&``
This is a prefix operator that can be applied to a string or array variable or literal value.
It results in the memory address (UWORD) of that string or array in memory: ``uword a = &stringvar``
Sometimes the compiler silently inserts this operator to make it easier for instance
@@ -1012,6 +1012,11 @@ address of: ``&``, ``&<``, ``&>``
and MSB byte array separately, respectively. Note that ``&<`` is just the same as ``&`` in this case.
For more details on split word arrays, see :ref:`arrayvars`.
**Typed pointer version:** the single ``&`` operator still returns an untyped uword address for
backward compatibility reasons, so existing programs keep working. The *double ampersand* ``&&`` operator
however returns a *typed* pointer to the value. The semantics are slightly different because adding or subtracting
a number from a typed pointer uses *pointer arithmetic* that takes the size of the value that it points to into account.
ternary:
Prog8 doesn't have a ternary operator to choose one of two values (``x? y : z`` in many other languages)
@@ -1061,7 +1066,7 @@ Reusing *virtual registers* R0-R15 for parameters
Normally, every subroutine parameter will get its own local variable in the subroutine where the argument value
will be stored when the subroutine is called. In certain situations, this may lead to many variables being allocated.
You *can* instruct the compiler to not allocate a new variable, but instead to reuse one of the *virtual registers* R0-R15
You *can* tell the compiler to not allocate a new variable, but instead to reuse one of the *virtual registers* R0-R15
(accessible in the code as ``cx16.r0`` - ``cx16.r15``) for the parameter. This is done by adding a ``@Rx`` tag
to the parameter. This can only be done for booleans, byte, and word types.
Note: the R0-R15 *virtual registers* are described in more detail below for the Assembly subroutines.

View File

@@ -65,11 +65,6 @@ Various things:
and it does it at run time. In this demo a jump table is not only created in the library,
but also in the main program and copied into the library for its use.
`Additional custom compilation targets (such as VIC-20) <https://github.com/gillham/prog8targets>`_
Various custom targets for Prog8 that are not (yet?) part of the Prog8 examples themselves.
These additional compilation targets may be in varying state of completeness.
Perhaps most recognisable at the time of adding this link, are the various VIC-20 targets.
.. image:: _static/curious.png
:align: center

View File

@@ -0,0 +1,91 @@
.. _pointers:
====================
Structs and Pointers
====================
Legacy untyped pointers (uword)
-------------------------------
Prior to version 12 of the language, the only pointer type available was a plain ``uword`` value (the memory address)
which could be used as a pointer to an ``ubyte`` (the byte value at that memory address).
Array indexing on an ``uword`` simply means to point to the ``ubyte`` at the location of the address + index value.
When the address of a value (explicitly) or a value of a reference type (string, array) was passed as an argument to a subroutine call,
it became one of these plain ``uword`` 'pointers'. The subroutine receiving it always had to interpret the 'pointer'
explicitly for what it actually pointed to, if that wasn't a simple byte.
Some implicit conversions were allowed too (such as putting ``str`` as the type of a subroutine parameter,
which would be changed to ``uword`` by the compiler).
*For backward compatibility reasons, this untyped ``uword`` pointer still exists in the language.*
Since version 12 there now are *typed pointers* that better express the intent and tell the compiler how to use the pointer,
these are explained below.
Typed pointer to simple datatype
--------------------------------
The syntax for declaring typed pointers is as follows:
``^^type``: pointer to a type
You can declare a pointer to any numeric datatype (bytes, words, longs, floats), booleans, and also strings.
(The latter: ``^^str`` - a pointer to a string - is equivalient to ``^^ubyte`` though because a string is just an array of ubytes.)
Finally, the type can be a struct type, which then declares a pointer to that struct type. This is explained in the next section.
So, for example; ``^^float fptr`` declares fptr as a pointer to a float value.
``^^type[size]``: array with size size containing pointers to a type.
So for example; ``^^word[100] values`` declares values to be an array of 100 pointers to words.
Note that an array of pointers (regardless of the type they point to) is always a @split word array.
It is not possible to define pointers to *arrays*; ``^^(type[])`` is invalid syntax.
Pointers of different types cannot be assigned to one another, unless you use an explicit cast.
Typed pointers and an 'untyped' uword pointer/value can be assigned to each other without an explicit cast.
Because it is pretty common to check if a pointer value is zero or not (because zero usually means that the pointer doesn't exist/has no value),
pointers can be implicitly cast to a boolean. This allows you to easily write conditionals such as ``while ptr { ... }``
Dereferencing a pointer, pointer arithmetic
-------------------------------------------
To get the value the pointer points at, you *dereference* the pointer. The syntax for that is: ``pointer^^``.
Say the pointer variable is of type ``^^float``, then ``pointer^^`` will return the float value it points at.
You can also use array indexing syntax to get the n-th value. For example: ``floatpointer[3]`` will return the
fourth floating point value in the sequence that the floatpointer points at. Because the pointer is a typed pointer,
the compiler knows what the size of the value is that it points at and correctly skips forward the required number of bytes in memory.
In this case, say a float takes 5 bytes, then ``floatpointer[3]`` will return the float value stored at memory address floatpointer+15.
Notice that ``floatpointer[0]`` is equivalent to ``floatpointer^^``.
You can add and subtract values from a pointer, this is called **pointer arithmetic**.
For example, to advance a pointer to the next value, you can use ``pointer++``.
To make it point to the preceding value, you can use ``pointer--``.
Adding or subtracting X to a pointer will change the pointer by X times the size of the value it points at (the same as the C language does it).
This is true even for pointers to struct types: the compiler knows the storage size of the whole struct type and advances or rewinds
the pointer value (memory address) by the appropriate number of bytes (X times the size of the struct). More info below.
Structs
-------
Work in progress.
Typed pointer to Struct type
----------------------------
Work in progress.
Address-Of: untyped vs typed
----------------------------
``&`` still returns untyped (uword) pointer, as it did in older Prog8 versions. This is for backward compatibility reasons so existing programs don't break.
The *double ampersand* operator ``&&`` returns a *typed* pointer to the value. The semantics are slightly different from the old untyped address-of operator, because adding or subtracting
a number from a typed pointer uses *pointer arithmetic* that takes the size of the value that it points to into account.

View File

@@ -134,7 +134,8 @@ Calling a subroutine requires three steps:
#. preparing the arguments (if any) and passing them to the routine.
Numeric types are passed by value (bytes, words, booleans, floats),
but array types and strings are passed by reference which means as ``uword`` being a pointer to their address in memory.
but array types passed by reference which means as ``uword`` being a pointer to their address in memory.
Strings are passed as a pointer to a byte: ``^^ubyte``.
#. calling the subroutine
#. preparing the return value (if any) and returning that from the call.

View File

@@ -1,15 +1,91 @@
TODO
====
STRUCTS: are being developed in their own separate branch for now, called "structs".
Idea is to make it feature complete in the IR/Virtual target, then merge it to master?, and then start building the 6502 code generation for it.
...
STRUCTS and TYPED POINTERS
--------------------------
'DONE' means working in the 'virtual' compiler target... (no 6502 codegen has been touched yet)
- DONE: add ast type check for assignments to struct fields; node_ptr.nextnode = enemy_ptr should error
- DONE: declare struct as a separate entity so you can then declare multiple variables (pointers) of the same struct type. Like usual.
- DONE: struct is a 'packed' struct, fields are placed in order of declaration. This guarantees exact size and place of the fields
- DONE: structs only supported as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
- DONE: need to introduce typed pointer datatype in prog8 to allow this to make any sense. Syntax to declare a pointer type: ^^datatype (double hat to avoid parsing confusion with the eor operator)
- DONE: initially only a pointer-to-struct should actually work, pointer-to-other-type is possible but that can come later.
- DONE: a struct can contain only numeric type fields (byte,word,float) or str fields (translated into ^^ubyte) or other pointer fields. No nested structs, no arrays.
- DONE: max 1 page of memory total size to allow regular register indexing
- DONE: assigning ptrs of different types is only allowed via a cast as usual. For simple address (uword) assignments, no cast is needed (but allowed)
- DONE: how to dereference a pointer? Pascal does it like this: ptr^ But this conflicts with the existing eor operator so we now use ptr^^^ (double hat)
- DONE: dereferencing a pointer to struct could look like Pascal's ptr^.field as well, but the ^ is actually redundant here; compiler already knows it's a pointer type.
Note that actually dereferencing a pointer to a struct as an explicit operation, conflicts with the third axiom on this list (structs only as reference types) so it can only be done for basic types?
So... setting struct fields can simply be ``structvar.field = 42`` and reading them ``a = structvar.field``
- DONE: you should be able to get the address of an individual field: ``&structpointer.field``
- DONE: teach sizeof() how to calculate struct sizes (need unit test + doc)
- DONE: sizeof(ptr^^) works
- DONE: implicit cast of pointer to bool, also in loop conditions (while ptr {...})
- DONE: implicit cast of pointer to uword in conditional expressions
- DONE: subroutine parameters and return values should be able to accept pointers as well now
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
- DONE (?) allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
- DONE: allow multi-field declarations in structs
- DONE: static initialization of structs. It behaves like arrays; it won't reset to the original value when program is restarted, so beware.
Syntax: ^^Node ptr = Node(1,2,3,4) statically allocates a Node with fields set to 1,2,3,4 and puts the address in ptr.
Node() without arguments allocates a node in BSS variable space instead that gets zeroed out at startup.
()Internally this gets translated into a structalloc(1,2,3,4) builtin function call that has a pointer to the struct as its return type)
- DONE: pointer arrays are split-words only, enforce this (variable dt + initializer array dt)
- DONE: make an error message for all pointer expressions (prefixed, binary) so we can start implementing the ones we need one by one.
- DONE: start by making ptr.value++ work , and ptr.value = ptr.value+20, and ptr.value = cx16.r0L+20+ptr.value Likewise for subtraction. DON'T FORGET C POINTER SEMANTICS. Other operators are nonsensical for ptr arith
- DONE: support @dirty on pointer vars -> uninitialized pointer placed in BSS_noclear segment
- DONE: support comparison operators on pointers
- DONE: implement augmented assignment on pointer dereference
- DONE: pointer types in subroutine signatures (both normal and asm-subs, parameters and return values)
- DONE: arrays of structs? No -> Just an array of uword pointers to said structs.
- DONE: what about pointers to subroutines? should these be typed as well now? Probably not, just stick with UWORD untyped pointer to avoid needless complexity.
- DONE: implement inplace logical and & or, with short-cirtuit, on dereferenced pointer
- DONE: existing ARRAY type remains unchanged (it doesn't become a typed pointer) so we can keep doing register-indexed LDA array,Y addressing directly on them.
- DONE: passing STR to a subroutine: parameter type becomes ^^UBYTE (rather than UWORD) (we still lose the bounds check)
- DONE: passing ARRAY to a subroutine: parameter type becomes ^^ElementDt (rather than UWORD) (we still lose the bounds check)
- DONE: @(ptr) complains that ptr is not uword when ptr is ^^ubyte (should be allowed)
- DONE: pointer[0] should be replaced with @(pointer) if pointer is ^^ubyte, so these are now all identical: ptr[0], ptr^^, @(ptr) if ptr is ^^ubyte
- DONE: STR should be asssignment compatible with UBYTE^^ but local scoped STR should still be accessed directly using LDA str,Y instead of through the pointer, like arrays.
- DONE: replace ^^str by ^^ubyte
- DONE: allow return ubyte/uword when pointer type is expected as return value type
- DONE: fix _msb/_lsb storage of the split-words pointer-arrays
- DONE: what about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not constants.
- DONE: make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator.
- DONE: existing '&' address-of still returns untyped uword (for backward compatibility). New '&&' operator returns typed pointer.
- DONE: allow list1^^ = list2^^ (value wise assignment of List structures) by replacing it with a sys.memcopy(list2, list1, sizeof(List)) call.
- DONE: allow a.b.ptr[i].value (equiv to a.b.ptr[i]^^.value) expressions (assignment target doesn't parse yet, see below)
- DONE: check passing arrays to typed ptr sub-parameters. NOTE: word array can only be a @nosplit array if the parameter type is ^^word, because the words need to be sequential in memory there
- DONE: allow str assign to ^^ubyte without cast (take address)
- DONE: added peekbool() and pokebool() and pokebowl() boolean peek and poke, the latter is equivalent to pokebool()
- DONE: fixed support for (expression) array index dereferencing "array[2]^^" where array contains pointers to primitives: replace with peek()
- DONE: fixed support for (assigntarget) array index dereferencing "array[2]^^" where array contains pointers to primitives: replace with poke()
- write docs in structpointers.rst
- scan through virtual library modules to change untyped uword pointers to typed pointers
- add support for array index dereferencing as assign target "array[2]^^.value = 99" where array is struct pointers (currently a 'no support' error)
- add support for array index dereferencing as assign target "array[2].value = 99" where array is struct pointers (currently a parser error)
- try to fix parse error l1^^.s[0] = 4242 (equivalent to l1.s[0]=4242 , which does parse correctly)
- try to make sizeof(^^type) parse correctly (or maybe replace it immediately with sys.SIZEOF_POINTER)
- add ?. null-propagation operator (for expression and assignment)?
- 6502 codegen: remove checks in checkForPointerTypesOn6502()
- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code"
- 6502 asm symbol name prefixing should work for dereferences too.
- update structpointers.rst docs with 6502 things?
- scan through 6502 library modules to change untyped uword pointers to typed pointers
- scan through 6502 examples to change untyped uword pointers to typed pointers
- really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
- enums?
- romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
- Kotlin: can we use inline value classes in certain spots?
@@ -33,6 +109,8 @@ Future Things and Ideas
IR/VM
-----
- possible to use LOADFIELD/STOREFIELD instructions more?
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- fix call() return value handling (... what's wrong with it again?)
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)

View File

@@ -5,6 +5,7 @@ Variables and Values
====================
Because this is such a big subject, variables and values have their own chapter.
Structs and pointers are in a separate chapter again: :ref:`pointers`.
Variables
@@ -342,7 +343,7 @@ a dynamic location in memory: currently this is equivalent to directly referenci
memory at the given index. In contrast to a real array variable, the index value can be the size of a word.
Unlike array variables, negative indexing for pointer variables does *not* mean it will be counting from the end, because the size of the buffer is unknown.
Instead, it simply addresses memory that lies *before* the pointer variable.
See also :ref:`pointervars`
See also :ref:`pointervars` and the chapter about it :ref:`pointers`.
**LSB/MSB split word and str arrays:**
@@ -565,13 +566,14 @@ without defining a memory-mapped location, you can do so by enclosing the addres
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
This is the official syntax to 'dereference a pointer' as it is often named in other languages.
You can actually also use the array indexing notation for this. It will be silently converted into
the direct memory access expression as explained above. Note that unlike regular arrays,
the index is not limited to an ubyte value. You can use a full uword to index a pointer variable like this::
pointervar[999] = 0 ; set memory byte to zero at location pointervar + 999.
More information about *typed pointers* can be found in the chapter :ref:`pointers`.
Converting/Casting types into other types
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -112,9 +112,9 @@ main {
; because we want speed, we don't use the dynamic c64.set_sprite_ptr() here
if(zc < 30*128)
c64.SPRPTR[i] = lsb(&spritedata.sprite_large/64)
c64.SPRPTR[i] = lsb(&spritedata.sprite_large as uword / 64)
else
c64.SPRPTR[i] = lsb(&spritedata.sprite_small/64)
c64.SPRPTR[i] = lsb(&spritedata.sprite_small as uword / 64)
c64.SPCOL[i] = spritecolors[(zc>>13) as ubyte + 4] ; further away=darker color
}

View File

@@ -29,6 +29,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -21,6 +21,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = 0 ; undefined, no floats supported
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127

View File

@@ -0,0 +1,148 @@
; Animal guessing game where the computer guesses your secret animal, and gets smarter every time it fails.
; The tree is in memory it is not saved on disk right now so restarting the program loses all knowledge...
;
; Exercise for the reader to store the memory containing the nodes and strings and reload it on new runs...?
; TODO: remove the examples/animals.p8 once this one compiles for 6502 too
%import textio
%import strings
main {
str userinput = "?"*80 ; buffer for user input
sub start() {
db.init()
txt.lowercase()
intro()
^^db.Node active = db.first
; The game loop asks questions and navigates the y/n tree of animals.
; if an animal node is found, that animal could be the answer.
; if it's not correct, a new question and the animal are inserted at that position in the tree.
repeat {
if active.question!=0 {
txt.print(active.question)
txt.print("? ")
if ask_yes_no()=='y'
active = active.positive
else
active = active.negative
} else {
txt.print("Is it a ")
txt.print(active.animal)
txt.print("? ")
if ask_yes_no()=='y' {
txt.print("\nYay, I knew it!\n")
txt.print("Let's go for another round.\n\n")
}
else
learn_new_animal()
active = db.first
intro()
}
}
sub intro() {
txt.print("Hello. Please think of an animal.\n")
txt.print("I will try to guess what it is!\n\n")
}
sub ask_yes_no() -> ubyte {
repeat {
if txt.input_chars(userinput)!=0 {
txt.nl()
if userinput[0]=='y' or userinput[0]=='n'
return userinput[0]
txt.print("Please answer yes or no.\n")
} else {
txt.nl()
}
}
}
sub learn_new_animal() {
str new_animal = "?" * 30
str answer = "?" * 10
; note that we make copies of the animal name and question strings to store them later
txt.print("\nI give up. What is the animal? ")
ubyte new_animal_length = txt.input_chars(new_animal)
uword new_animal_copy = arena.alloc(new_animal_length+1)
void strings.copy(new_animal, new_animal_copy)
txt.print("\nWhat yes/no question would best tell a ")
txt.print(new_animal)
txt.print(" apart from a ")
txt.print(active.animal)
txt.print("? ")
ubyte question_length = txt.input_chars(userinput)
uword question_copy = arena.alloc(question_length+1)
void strings.copy(userinput, question_copy)
txt.print("In case of the ")
txt.print(new_animal)
txt.print(", what is the answer to that question? ")
ubyte yesno = ask_yes_no()
; cannot use struct initializer db.Node(....) here because we need to have a new node every time
^^db.Node new_animal_node = arena.alloc(sizeof(db.Node))
new_animal_node.animal = new_animal_copy
new_animal_node.question = new_animal_node.negative = new_animal_node.positive = 0
^^db.Node wrong_animal_node = arena.alloc(sizeof(db.Node))
wrong_animal_node.animal = active.animal
wrong_animal_node.question = wrong_animal_node.negative = wrong_animal_node.positive = 0
active.question = question_copy
active.animal = 0
if yesno=='y' {
active.positive = new_animal_node
active.negative = wrong_animal_node
} else {
active.positive = wrong_animal_node
active.negative = new_animal_node
}
txt.print("\nI've learned about that new animal now!\n")
txt.print("Let's go for another round.\n\n")
}
}
}
db {
; knowledge about animals is stored in a tree that can grow with new animals.
; questions have y/n answers that point to possible animals.
struct Node {
str question
str animal
^^Node negative
^^Node positive
}
^^Node first
sub init() {
first = Node("does it swim", 0, 0, 0)
^^Node question = Node("can it fly", 0, 0, 0)
first.negative = question
first.positive = Node(0, "dolpin", 0, 0)
question.negative = Node(0, "horse", 0, 0)
question.positive = Node(0, "eagle", 0, 0)
}
}
arena {
; extremely trivial arena allocator (that never frees)
uword buffer = memory("arena", 10000, 0)
uword next = buffer
sub alloc(ubyte size) -> uword {
defer next += size
return next
}
}

View File

@@ -0,0 +1,232 @@
; Binary Search Tree.
; It's a simple implementation for test/demonstration purposes of the pointer support;
; no balancing is done and memory is not freed when elements are removed.
%import textio
main {
sub start() {
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
btree.add(cx16.r0)
txt.print_ub(btree.size())
txt.print(" sorted values: ")
btree.print_tree_inorder()
txt.print("'tree' form:\n")
btree.print_tree_preorder()
txt.print("203 in tree? ")
txt.print_bool(btree.contains(203))
txt.print("\n204 in tree? ")
txt.print_bool(btree.contains(204))
txt.print("\n605 in tree? ")
txt.print_bool(btree.contains(605))
txt.print("\n606 in tree? ")
txt.print_bool(btree.contains(606))
txt.nl()
txt.print("Removing some numbers.\n")
btree.remove(9999)
btree.remove(97)
btree.remove(187)
btree.remove(203)
btree.remove(275)
btree.remove(321)
btree.remove(520)
btree.remove(562)
btree.remove(606)
btree.remove(719)
btree.remove(794)
txt.print_ub(btree.size())
txt.print(" sorted values: ")
btree.print_tree_inorder()
txt.print("'tree' form:\n")
btree.print_tree_preorder()
}
}
btree {
struct Node {
^^Node left
^^Node right
uword value
}
^^Node root = 0
sub add(uword value) {
^^Node node = arena.alloc(sizeof(Node))
node.value = value
node.left = node.right = 0
if root==0
root=node
else {
^^Node parent = root
repeat {
if parent.value >= value {
if parent.left
parent = parent.left
else {
parent.left = node
return
}
} else {
if parent.right
parent = parent.right
else {
parent.right = node
return
}
}
}
}
}
sub contains(uword value) -> bool {
^^Node r = root
while r {
if r.value==value
return true
if r.value>value
r = r.left
else
r = r.right
}
return false
}
sub size() -> ubyte {
ubyte count
if root
count_node(root)
return count
sub count_node(^^Node r) {
count++
if r.left {
sys.pushw(r)
count_node(r.left)
r = sys.popw()
}
if r.right {
sys.pushw(r)
count_node(r.right)
r = sys.popw()
}
}
}
sub remove(uword value) {
; note: we don't deallocate the memory from the node, for simplicity sake
^^Node n = root
^^Node parent = 0
while n {
if n.value==value {
if n.left==0
replacechild(parent, n, n.right)
else if n.right==0
replacechild(parent, n, n.left)
else {
; Both left & right subtrees are present.
; N = node to delete.
; Find N's successor S. (N's right subtree's minimum element)
; Attach N's left subtree to S.left (S doesn't have a left child)
; Attach N's right subtree to Parent in place of N.
^^Node successor = find_successor(n)
successor.left = n.left
replacechild(parent, n, n.right)
}
return
}
parent = n
if n.value>value
n = n.left
else
n = n.right
}
sub find_successor(^^Node p) -> ^^Node {
^^Node succ = p
p = p.right
while p {
succ = p
p = p.left
}
return succ
}
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
if p.left==child
p.left = newchild
else
p.right = newchild
}
}
sub print_tree_inorder() {
if root
print_tree(root)
txt.nl()
sub print_tree(^^Node r) {
if r.left {
sys.pushw(r)
print_tree(r.left)
r = sys.popw()
}
txt.print_uw(r.value)
txt.print(", ")
if r.right {
sys.pushw(r)
print_tree(r.right)
r = sys.popw()
}
}
}
sub print_tree_preorder() {
if root
print_tree(root,0)
txt.nl()
sub print_tree(^^Node r, ubyte depth) {
repeat depth txt.print(" ")
txt.print_uw(r.value)
txt.nl()
if r.left {
sys.pushw(r)
sys.push(depth)
print_tree(r.left, depth+1)
depth = sys.pop()
r = sys.popw()
}
if r.right {
sys.pushw(r)
sys.push(depth)
print_tree(r.right, depth+1)
depth = sys.pop()
r = sys.popw()
}
}
}
}
arena {
; extremely trivial arena allocator (that never frees)
uword buffer = memory("arena", 2000, 0)
uword next = buffer
sub alloc(ubyte size) -> uword {
defer next += size
return next
}
}

Some files were not shown because too many files have changed in this diff Show More